蓝牙:服务发现失败

4

我尝试在我的安卓应用(在一个运行4.1.2的三星手机上)和我的笔记本电脑(Win7,64位:服务器)之间建立蓝牙连接,但总是失败并显示“服务发现失败”。

我阅读了各种关于此问题的主题文章(这里那里),但没有解决我的问题。

我有两个问题:

  • "00001101-0000-1000-8000-00805F9B34FB" 这个众所周知的UUID是什么意思?为什么/何时应该使用?
  • 是否有任何建议来调查/解决我的问题?

备注:

  • 我尝试建立受保护的和非受保护的连接(两者都失败了)
  • 我能够从“设置>蓝牙”中把我的笔记本电脑和设备配对
  • 如评论中建议的:我尝试使用随机生成的UUID(但在两端相同),而不是众所周知的UUID,但仍然出现完全相同的行为。

我有以下权限:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

编辑

不是在客户端上硬编码UUID,我尝试了以下方式(但仍然出现相同的错误):

UUID uuid = bluetoothDevice.getUuids()[bluetoothDevice.getUuids().length-1].getUuid();

回显服务器的UUID始终是数组中的最后一个。

编辑结束

以下是客户端相关日志:

08-22 12:30:28.627: ERROR/BluetoothService.cpp(12008): stopDiscoveryNative: D-Bus error in StopDiscovery: org.bluez.Error.Failed (Invalid discovery session)
08-22 12:30:28.647: INFO/BluetoothSocket.cpp(18030): Setting Master socket option
08-22 12:30:28.647: VERBOSE/BluetoothSocket.cpp(18030): ...fd 43 created (RFCOMM, lm = 7)
08-22 12:30:28.687: DEBUG/BluetoothPolicyService(12008): getAllowBluetoothDataTransfer - showMsg: true
08-22 12:30:28.687: DEBUG/BluetoothPolicyService(12008): MDM: isProfileEnabled = true
08-22 12:30:28.697: DEBUG/BluetoothUtils(18030): isSocketAllowedBySecurityPolicy start : device null
08-22 12:30:28.727: ERROR/BluetoothEventLoop.cpp(12008): onCreateDeviceResult: D-Bus error: org.bluez.Error.AlreadyExists (Already Exists)
08-22 12:30:28.727: VERBOSE/BluetoothService.cpp(12008): discoverServicesNative
08-22 12:30:28.727: VERBOSE/BluetoothService.cpp(12008): ... Object Path = /org/bluez/12635/hci0/dev_00_09_DD_50_88_54
08-22 12:30:28.727: VERBOSE/BluetoothService.cpp(12008): ... Pattern = , strlen = 0
08-22 12:30:29.138: VERBOSE/BluetoothEventLoop.cpp(12008): event_filter: Received signal org.bluez.Device:PropertyChanged from /org/bluez/12635/hci0/dev_00_09_DD_50_88_54
08-22 12:30:29.138: DEBUG/BluetoothEventLoop(12008): Device property changed
08-22 12:30:32.141: DEBUG/BluetoothA2DPStateReceiver(15827): BluetoothA2DPStateReceiver constructor call()
08-22 12:30:32.141: DEBUG/BluetoothA2DPStateReceiver(15827): onReceive(): action = android.bluetooth.device.action.ACL_CONNECTED
08-22 12:30:32.141: DEBUG/BluetoothA2DPStateReceiver(15827): ACTION_ACL_CONNECTED
08-22 12:30:32.141: DEBUG/BluetoothA2DPSinkInfo(15827): checkBlackListCarkit() : isBlackListCarkit false
08-22 12:30:32.161: DEBUG/BluetoothNotiBroadcastReceiver(15910): onReceive
08-22 12:30:40.749: ERROR/Bluetooth(18030): Cannot connect
    java.io.IOException: Service discovery failed
    at android.bluetooth.BluetoothSocket$SdpHelper.doSdp(BluetoothSocket.java:475)
    at android.bluetooth.BluetoothSocket.connect(BluetoothSocket.java:241)
    at com.company.BlueToothTestActivity.open(BlueToothTestActivity.java:48)
    at com.company.BlueToothTestActivity$1.run(BlueToothTestActivity.java:29)
08-22 12:30:40.749: WARN/Bluetooth(18030): Unable to open connection !

以下是重现该错误的代码(这是一个简化版本,可以编译并重现错误 - 至少在我的硬件上 -)

客户端

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;

import java.io.IOException;
import java.util.UUID;

public class BlueToothTestActivity extends Activity {

private String macAddress = "00:09:DD:50:88:54";  //hardcoded laptop macAddress
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice bluetoothDevice;
private BluetoothSocket socket;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);   
    Thread bluetoothClientThread = new Thread(){
        @Override
        public void run() {
            if(open()){
                Log.i("Bluetooth","Connection is open !");
            }else{
                Log.w("Bluetooth","Unable to open connection !");
            }
        }
    };
    bluetoothClientThread.start();
    initUI();
}

public boolean open() {
    mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    bluetoothDevice = mBluetoothAdapter.getRemoteDevice(macAddress);
    String BT_UUID = "00001101-0000-1000-8000-00805F9B34FB";
    try {
        mBluetoothAdapter.cancelDiscovery();
        //socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));
        socket = bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(BT_UUID));
        socket.connect(); //Block 12 sec here, then throw the exception.
        return true;
    } catch (IOException e) {
        try {
            socket.close();
        } catch (IOException closeException) { }
        Log.e("Bluetooth", "Cannot connect", e);
        return false;
    }
}

private void initUI(){
    LinearLayout linearLayout = new LinearLayout(this);
    Button finish = new Button(this);
    finish.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            BlueToothTestActivity.this.finish();
        }
    });
    finish.setText("Exit");
    linearLayout.addView(finish);
    setContentView(linearLayout);
}
}

这个服务器是一个简单的echo服务器(基于Bluecove库,我从这里拿了代码:http://www.miniware.net/mobile/articles/viewarticle.php?id=22)。

服务器

import java.io.*;
import javax.bluetooth.*;
import javax.microedition.io.*;

// sample code from : http://www.miniware.net/mobile/articles/viewarticle.php?id=22   (Nikos Fotiou)
public class EchoServer {
    public final UUID uuid = new UUID("0000110100001000800000805F9B34FB",false);
    public final String name = "EchoServer";                       //the name of the service
    public final String url  =  "btspp://localhost:" + uuid + ";name=" + name  + ";authenticate=false;encrypt=false;";
    LocalDevice local = null;
    StreamConnectionNotifier server = null;
    StreamConnection conn = null;

    public EchoServer() {
    try {
        System.out.println("Setting device to be discoverable...");
        local = LocalDevice.getLocalDevice();
        local.setDiscoverable(DiscoveryAgent.GIAC);
        System.out.println("Start service:"+url);
        server = (StreamConnectionNotifier)Connector.open(url);
        System.out.println("Waiting for incoming connection...");
        conn = server.acceptAndOpen();  // stop and wait here
        System.out.println("Client Connected..."); //never reach this line
        DataInputStream din   = new DataInputStream(conn.openInputStream());
        while(true){
            String cmd = "";
            char c;
            while (((c = din.readChar()) > 0) && (c!='\n') ){
                cmd = cmd + c;
            }
            System.out.println("Received " + cmd);
        }
    } catch (Exception  e) {
        System.err.println("Exception Occured: " + e.toString());
        e.printStackTrace();
    }
}

public static void main (String args[]){
    EchoServer echoserver = new EchoServer();
}

}

服务器pom.xml文件

<dependencies>
    <dependency>
        <groupId>net.sf.bluecove</groupId>
        <artifactId>bluecove</artifactId>
        <version>2.1.1-SNAPSHOT</version>
    </dependency>
</dependencies>

<repositories>
    <repository>
        <id>pyx4me-web-snapshot</id>
        <url>http://www.pyx4me.com/maven2-snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
</repositories>

日志显示 Android 端的 open 调用失败,硬编码的 UID 可能是罪魁祸首之一,它可能是系统保留标识符之一。如果我是你,我会尝试使用这个 网站 生成一个随机 UUID,并将其粘贴到服务器和客户端中,然后再次尝试。如果仍然失败,可以尝试对 SDK 中找到的蓝牙聊天示例程序进行修改以与您的服务器通信。 - t0mm13b
@t0mm13b 感谢您的评论。我尝试使用随机UUID(在两侧相同),但我仍然遇到相同的问题和日志。 - ben75
嗯...在EchoServer端,尝试将代码移动到一个线程中,因为它似乎会阻塞,因此安卓端无法“看到”它?这个注释*//never reach this line*让我想到了这一点。 - t0mm13b
server.acceptAndOpen(); 是阻塞的,这是预期的行为。服务器只是在等待客户端,因此将其移动到线程中似乎没有帮助。 - ben75
2个回答

8
你尝试使用的UUID是串口配置文件UUID。它可以在代码中声明为:
private static final UUID BLUETOOTH_SPP_UUID =
                UUID.fromString("00001101-0000-1000-8000-00805f9b34fb");

我在Windows 8笔记本电脑和Nexus S上尝试了您的代码。首先,我需要从Nexus设备上进行配对。然后,我启动了服务器并使用了Bluetooth SPP应用程序测试连接。它完美地工作了。然后我启动了你的代码,也工作良好。

当服务端未启动时,我能够重现"服务发现失败"异常。此外,在使用createRfcommSocket方法扫描了所有30个通道后,手机上的蓝牙堆栈被破坏,我不得不重启设备。

总之,为确保您能够通信:

  1. 确保笔记本电脑的蓝牙处于可发现状态
  2. (可选)关闭UAC并使用管理员帐户,以确保您不会遇到任何安全漏洞(我不必这样做,但在第二次启动时,防火墙必须打开才能启用调试)
  3. 重启手机
  4. 从手机上进行配对
  5. 使用SPP UUID,而不是自定义UUID
  6. 确保您在acceptAndOpen()调用之前添加了System.out.println(local.getBluetoothAddress());以获取正确的MAC地址
  7. 使用来自Play商店的第三方应用程序测试连接。我使用了这个进入命令行模式。仅需要测试连接。
  8. 更新MAC地址并运行应用程序
  9. 它应该工作。

这是我运行时得到的输出:

Start service:btspp://localhost:0000110100001000800000805f9b34fb;name=EchoServer;authenticate=false;encrypt=false;
MAC 402CF454215C
Waiting for incoming connection...
Client Connected...
Exception Occured: java.io.EOFException
BlueCove stack shutdown completed
java.io.EOFException
    at java.io.DataInputStream.readChar(Unknown Source)
    at examples.bluetooth_spp_server.EchoServer.<init>(EchoServer.java:30)
    at examples.bluetooth_spp_server.EchoServer.main(EchoServer.java:42)

为了完整起见,以下是在各种情况下返回的错误:

  • Windows蓝牙关闭: javax.bluetooth.BluetoothStateException: 未检测到蓝牙
  • Android蓝牙关闭: java.io.IOException: 无法启动服务发现
  • Windows服务器未运行: java.io.IOException: 服务发现失败
  • 未配对: TimeoutException

理论上,在启用安全连接时应自动进行配对,但实际上在手机或Windows上并不总是会出现配对对话框。如果发生故障,则需要考虑这种不确定性因素。


谢谢。但是当我尝试选项2时,socket.connect()失败并显示“连接超时”。此外,我不确定我的服务器UUID是否在存储库中,因为当我在客户端上调用bluetoothDevice.getUuids()时,我会收到一个包含SPP-UUID和我的自定义UUID(如果我随机生成一个)的数组。 - ben75
你使用的安卓版本是什么? - allprog
我在三星GT-B5330上使用Android 4.1.2。 - ben75
好的,我会再次确认这是否有效。我的设备上有相同的版本。 - allprog
@ben75 我测试了整个场景并更新了帖子。顺便说一下: 对我来说,getUuids() 按照这个顺序返回了 0x1000、0x1101、0x1115、0x1200 服务。 - allprog
显示剩余4条评论

1

a) UUID是串行端口UUID,主要用于使用不支持SDP协议的设备,例如小型嵌入式RFCOMM设备。

b) 检查您的手机蓝牙是否已打开。


是的,关闭蓝牙可能会出现问题。但在这种情况下,异常是:java.io.IOException: 无法启动服务发现。 - allprog
我记得曾经因为蓝牙关闭而出现了“无法启动服务发现”的问题。 - ligi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接