安卓蓝牙:获取发现设备的UUID

25

我目前正在为Android编写一个小型蓝牙库,我试图获取我周围发现的所有设备的服务UUID。

当我的广播接收器接收到BluetoothDevice.ACTION_FOUND意图时,我会提取设备并调用:

device.fetchUuidsWithSdp();

这将导致每个找到的设备都会触发 BluetoothDevice.ACTION_UUID 意图,我使用相同的接收器来处理它们:

BluetoothDevice d = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Parcelable[] uuidExtra = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);

if(uuidExtra ==  null) {
    Log.e(TAG, "UUID = null");
}

if(d != null && uuidExtra != null)
    Log.d(TAG, d.getName() + ": " + uuidExtra.toString());

问题是uuidExtra始终为null

如何获取周围设备的所有UUID?

编辑:

我正在使用Nexus 7开发。 我尝试了在互联网上找到的代码,但仍然遇到空指针异常:http://digitalhacksblog.blogspot.de/2012/05/android-example-bluetooth-discover-and.html

谢谢。


1
我遇到了完全相同的情况!你找到解决方案了吗?能帮我一下吗? - Bilal Rabbani
6个回答

15

关于此事的文档表明...

始终包含额外字段BluetoothDevice.EXTRA_UUID

然而,就像你一样,我发现这不是真的。

如果在设备发现仍在进行时调用fetchUuidsWithSdp(),则BluetoothDevice.EXTRA_UUID可能为空。

在收到BluetoothAdapter.ACTION_DISCOVERY_FINISHED后,请等待再进行任何对fetchUuidsWithSdp()的调用。


4
即使在调用fetchUuidsWithSdp()之前等待设备发现完成,我仍然遇到相同的问题。 - Kevin
在某些情况下这对我有效。当从我的三星Galaxy Nexus 2发现设备时,我只能在我的HTC One(M7)上接收服务,而不能从我的MacBook Pro上接收服务(结果为空)。 - mfb
编辑:似乎无论我在过程的哪个点调用fetchUuidsWithSdp(),它是否起作用都是完全随机的。 - mfb
这个运行得非常好。对于那些感觉行为随机的情况,请看下面我的回答。 - Arunkumar
即使我添加了 filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);,也从未调用 ACTION_DISCOVERY_FINISHED。 - user924

12

注意:此解决方案适用于经典蓝牙而不是BLE。对于BLE,请检查如何在周边设备的广告者上发送制造商特定数据。

获取UUID的问题在于您只有一个蓝牙适配器,我们不能同时使用适配器进行并行API调用。

正如Eddie所指出的那样,等待BluetoothAdapter.ACTION_DISCOVERY_FINISHED,然后调用fetchUuidsWithSdp()

尽管如此,这仍然无法保证为所有设备获取uuids。除此之外,还必须等待每个后续调用fetchuuidsWithSdp()完成,然后对另一个设备调用此方法。

请参阅下面的代码--

ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>();

private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            BluetoothDevice device = (BluetoothDevice) intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            mDeviceList.add(device);
        } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
            // discovery has finished, give a call to fetchUuidsWithSdp on first device in list.
            if (!mDeviceList.isEmpty()) {
                BluetoothDevice device = mDeviceList.remove(0);
                boolean result = device.fetchUuidsWithSdp();
            }
        } else if (BluetoothDevice.ACTION_UUID.equals(action)) {
            // This is when we can be assured that fetchUuidsWithSdp has completed.
            // So get the uuids and call fetchUuidsWithSdp on another device in list

            BluetoothDevice deviceExtra = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Parcelable[] uuidExtra = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
            System.out.println("DeviceExtra address - " + deviceExtra.getAddress());
            if (uuidExtra != null) {
                for (Parcelable p : uuidExtra) {
                    System.out.println("uuidExtra - " + p);
                }
            } else {
                System.out.println("uuidExtra is still null");
            }
            if (!mDeviceList.isEmpty()) {
                BluetoothDevice device = mDeviceList.remove(0);
                boolean result = device.fetchUuidsWithSdp();
            }
        }
    }
}

更新: 最新的安卓版本(mm及以上)会导致每个设备触发一次配对过程。

(Note from the translator: The original text seems to be referring to Android versions "mm & above," but I have translated it as "最新的安卓版本" (latest Android versions) due to the lack of context and my uncertainty about what "mm" refers to.)

有趣的是,我仍然得到了null :( - Sebastien FERRAND
2
确保注册意图。 - Eugene K
哪个操作会触发配对过程?是FetchUuidWithSdp()吗?如果是这样,我认为这是因为需要建立连接?如果是这样的话,为什么只有在MM及以上版本中才会触发配对过程呢? - Brian Reinhold
是的。原因是随着 Android 的新版本(主要是 MM 之后)安全正在加强。此外,如果没有活动数据传输,特别关注于断开蓝牙连接。 - Arunkumar

3

以下是我获取心率设备UUID的服务特征UUID的好例子:

private class HeartRateBluetoothGattCallback extends BluetoothGattCallback {

    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {         
        if (newState == BluetoothProfile.STATE_CONNECTED) {
            logMessage("CONNECTED TO " + gatt.getDevice().getName(), false, false);
            gatt.discoverServices();    
        } else if(newState == BluetoothProfile.STATE_DISCONNECTED) {
            logMessage("DISCONNECTED FROM " + gatt.getDevice().getName(), false, false);
            if(mIsTrackingHeartRate)
                handleHeartRateDeviceDisconnection(gatt);
        } 
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
        if (status == BluetoothGatt.GATT_SUCCESS) {
            logMessage("DISCOVERING SERVICES FOR " + gatt.getDevice().getName(), false, false);

            if(mDesiredHeartRateDevice != null && 
                    gatt.getDevice().getAddress().equals(mDesiredHeartRateDevice.getBLEDeviceAddress())) {

                if(subscribeToHeartRateGattServices(gatt)) {

                    mIsTrackingHeartRate = true;
                    setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.CONNECTED);
                    broadcastHeartRateDeviceConnected(gatt.getDevice());

                } else
                    broadcastHeartRateDeviceFailedConnection(gatt.getDevice());

            } else {
                parseGattServices(gatt);
                disconnectGatt(getDiscoveredBLEDevice(gatt.getDevice().getAddress()));
            }
        }   
    }

    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
        if(characteristic.getUuid().equals(UUID.fromString(HEART_RATE_VALUE_CHAR_READ_ID))) {
            int flag = characteristic.getProperties();
            int format = -1;

            if ((flag & 0x01) != 0) 
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
            else 
                format = BluetoothGattCharacteristic.FORMAT_UINT8;

            Integer heartRateValue = characteristic.getIntValue(format, 1);
            if(heartRateValue != null)
                broadcastHeartRateValue(heartRateValue);
            else
                Log.w(SERVICE_NAME, "UNABLE TO FORMAT HEART RATE DATA");
        }
    };

};

private void parseGattServices(BluetoothGatt gatt) {
    boolean isHeartRate = false;
    for(BluetoothGattService blueToothGattService : gatt.getServices()) {
        logMessage("GATT SERVICE: " + blueToothGattService.getUuid().toString(), false, false);
        if(blueToothGattService.getUuid().toString().contains(HEART_RATE_DEVICE_SERVICE_CHARACTERISTIC_PREFIX))
            isHeartRate = true;
    }   

    if(isHeartRate) {
        setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.IS_HEART_RATE);
        broadcastHeartRateDeviceFound(getDiscoveredBLEDevice(gatt.getDevice().getAddress()));
    } else 
        setDeviceScanned(getDiscoveredBLEDevice(gatt.getDevice().getAddress()), DiscoveredBLEDevice.NOT_HEART_RATE);
}

private void handleHeartRateDeviceDisconnection(BluetoothGatt gatt) {
    broadcastHeartRateDeviceDisconnected(gatt.getDevice());
    gatt.close();

    clearoutHeartRateData();
    scanForHeartRateDevices();
}

private void disconnectGatt(DiscoveredBLEDevice device) {
    logMessage("CLOSING GATT FOR " + device.getBLEDeviceName(), false, false);
    device.getBlueToothGatt().close();
    device.setBlueToothGatt(null);
    mInDiscoveryMode = false;
}

private boolean subscribeToHeartRateGattServices(BluetoothGatt gatt) {
    for(BluetoothGattService blueToothGattService : gatt.getServices()) {
        if(blueToothGattService.getUuid().toString().contains(HEART_RATE_DEVICE_SERVICE_CHARACTERISTIC_PREFIX)) {
            mHeartRateGattService = blueToothGattService;

            for(BluetoothGattCharacteristic characteristic : mHeartRateGattService.getCharacteristics()) {
                logMessage("CHARACTERISTIC UUID = " + characteristic.getUuid().toString(), false, false);

                for(BluetoothGattDescriptor descriptor :characteristic.getDescriptors()) {
                    logMessage("DESCRIPTOR UUID = " + descriptor.getUuid().toString(), false, false);
                }

                if(characteristic.getUuid().equals(UUID.fromString(HEART_RATE_VALUE_CHAR_READ_ID))) {
                    gatt.setCharacteristicNotification(characteristic, true);
                    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(HEART_RATE_VALUE_CHAR_DESC_ID));
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    return gatt.writeDescriptor(descriptor);
                }
            }

            break; //break out of master for-loop
        }
    }

    return false;
}

5
我认为这个问题是针对经典蓝牙而不是低功耗蓝牙的...因为你提到了“服务”,“特征”,“GATT”和心率设备。 - jschlepp

3

device.getUuids()用于获取已配对设备的所有UUID,以ParcelUuid的形式表示;以下是示例代码:

private void get_uuid_from_paired_devices(){
        Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
        for (BluetoothDevice device: pairedDevices){

            for (ParcelUuid uuid: device.getUuids()){
                String uuid_string = uuid.toString();
                Log.d(TAG, "uuid : "+uuid_string);
            }
        }
    }

2

我想你需要与设备配对才能接收UUID。至少,这就是发生在我身上的事情。


0

以下方法适用于我从远程设备获取记录

-0-
registerReceiver(..,
                new IntentFilter(BluetoothDevice.ACTION_UUID));

-1- device.fetchUuidsWithSdp();

-2-从广播接收器内部

   if (BluetoothDevice.ACTION_UUID.equals(action)) {
                        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                        Parcelable[] uuids = intent.getParcelableArrayExtra(BluetoothDevice.EXTRA_UUID);
                        for (Parcelable ep : uuids) {
                            Utilities.print("UUID records : "+ ep.toString());
                        }
                    }

你还可以使用以下代码获取离线缓存的UUID记录:
 BluetoothDevice.getUuids();

1
我尝试在三个不同的BLE设备上使用getUuids(),但所有设备都返回了一个空数组。 - Robert Lewis
getUuids()方法仅适用于经典蓝牙,据我所知。对于低功耗蓝牙则完全不同! - Brian Reinhold
这帮助我弄清楚了。我必须使用“GetParcelableArrayExtra”而不是“GetParcelableExtra”。官方文档中没有提到这一点,这让人感到沮丧。 - MondQ

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