在广告数据包中获取编码的UUID

23

我正在尝试获取蓝牙设备的UUID。我遵循了安卓开发者指南,到目前为止我只能获取到设备名称和RSSI信号强度值。我正在尝试获取扫描方法中出现的设备的UUID,该方法如下:

    public void onLeScan(final BluetoothDevice device, int rssi,byte[] scanRecord) {

        ParcelUuid[] myUUid =device.getUuids();
        for(ParcelUuid a :myUUid){
            Log.d("UUID",a.getUuid().toString());
        }
        String s = new String(scanRecord);
        int len = scanRecord.length;
        String scanRecords =new String(scanRecord) ;



        deviceMap.put(device.getName().toString(), rssi);
        Message msg = MainActivity.myHandler.obtainMessage();
        Bundle bundle = new Bundle();
        bundle.putCharSequence("dev_name", device.getName().toString());
        bundle.putCharSequence("rssi", Integer.toString(rssi));
        msg.setData(bundle);
        MainActivity.myHandler.sendMessage(msg);
   }

这将返回 - btif_gattc_upstreams_evt: 事件 4096


哪个UUID?你的目标是什么? - Chris Stratton
1
不,通常情况下BLE设备中没有为该目的而设计的UUID,但有一个设备地址。你是指iBeacon UUID吗?如果是这样,在Android上,您必须从广告数据包中解析出来-就BLE标准和协议栈而言,这些字节只是一个特定于制造商的数据字段。 - Chris Stratton
你的 byte[] scanRecord 包含了后面无意义的填充内容。 - Chris Stratton
迭代字节并将每个字节转换为十六进制字符串表示。 - Chris Stratton
我已经尝试了好几个小时但是没有成功。你能给我一小段代码吗? - Boris Pawlowski
显示剩余5条评论
5个回答

42
如果您想从BLE扫描后的scanRec []字节中获取UUID /任何其他数据(例如制造商数据),则首先需要了解这些广告数据包的数据格式。
来自Bluetooth.org: Advertising or Scan Response Data format 太多理论,想看一些代码片段?下面的函数将直接打印解析后的原始数据字节。现在,您需要知道每个类型代码以了解数据包引用的是什么信息。例如,类型:0x09表示BLE设备名称,类型:0x07表示UUID。
public void printScanRecord (byte[] scanRecord) {

    // Simply print all raw bytes   
    try {
        String decodedRecord = new String(scanRecord,"UTF-8");
        Log.d("DEBUG","decoded String : " + ByteArrayToString(scanRecord));
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }

    // Parse data bytes into individual records
    List<AdRecord> records = AdRecord.parseScanRecord(scanRecord);


    // Print individual records 
    if (records.size() == 0) {
        Log.i("DEBUG", "Scan Record Empty");
    } else {
        Log.i("DEBUG", "Scan Record: " + TextUtils.join(",", records));
    }

}


public static String ByteArrayToString(byte[] ba)
{
  StringBuilder hex = new StringBuilder(ba.length * 2);
  for (byte b : ba)
    hex.append(b + " ");

  return hex.toString();
}


public static class AdRecord {

    public AdRecord(int length, int type, byte[] data) {
        String decodedRecord = "";
        try {
            decodedRecord = new String(data,"UTF-8");

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        Log.d("DEBUG", "Length: " + length + " Type : " + type + " Data : " + ByteArrayToString(data));         
    }

    // ...

    public static List<AdRecord> parseScanRecord(byte[] scanRecord) {
        List<AdRecord> records = new ArrayList<AdRecord>();

        int index = 0;
        while (index < scanRecord.length) {
            int length = scanRecord[index++];
            //Done once we run out of records
            if (length == 0) break;

            int type = scanRecord[index];
            //Done if our record isn't a valid type
            if (type == 0) break;

            byte[] data = Arrays.copyOfRange(scanRecord, index+1, index+length);

            records.add(new AdRecord(length, type, data));
            //Advance
            index += length;
        }

        return records;
    }

    // ...
}

解析完成后,这些数据字节将更有意义,您可以找出下一级解码。


对我来说,它返回类似于 64898bd, aa0ceb2, 51e6e03, ccbf180, f6b03b9, 5c923fe 的东西。如何将其转换为 UUID? - Mr. Blond

4

正如评论中所提到的,BLE设备实际上没有特定的UUID(而是具有很多包含服务的UUID)。不过,一些方案如iBeacon在广告数据记录的制造商特定数据记录中编码了唯一标识符。

这是一种非常低效但概念上简单的方法,将整个scanRecord转换为十六进制字符串表示以进行调试打印:

String msg = "payload = ";
for (byte b : scanRecord)
  msg += String.format("%02x ", b);

请注意,这将包括实际的广告数据包和一些无意义的尾随字节。在解析广告数据包本身中包含的结构(长度字段)后,应忽略它们。

1
有没有一种明确的方法将这个字符串转换成类似于4CAD1A61-A9D3-428A-2913-DD84502313FC的东西? - Boris Pawlowski
当然,去掉空格并添加基于计数器的规则,在需要时加入“-”,可能会切换到索引样式的“for”循环。此时,您正在询问基本的Java编程知识,而不是BLE。 - Chris Stratton
实际上,根据解析这些信息,我只能获取设备名称,但无法获取UUID。 - Boris Pawlowski
看起来你好像并没有处理扫描记录本身,而只调用了device.getName()方法。 - Chris Stratton
事实上,BLE设备可以广播(虽然不是强制性的)一个主服务UUID,可以是长的(128位,自定义服务)或短的(16位,标准服务)。这样做是为了让中央设备能够轻松地对外围设备进行排序。 - jose.angel.jimenez
是的,但海报说他们正在寻找服务 UUID。 - Chris Stratton

2

我在开发我的蓝牙应用程序时遇到了同样的问题,但是在阅读以下链接的文档后:https://developer.android.com/reference/android/bluetooth/le/ScanResult.html

就UUID而言(取决于您正在开发的API),重要的类别包括:

AdvertiseData AdvertiseData.Builder ScanRecord ScanResult

在阅读这些类的文档后,这是我编写的代码,以获取正在扫描的任何设备的UUID:

//对于API < 21:

private BluetoothAdapter.LeScanCallback scanCallBackLe =
        new BluetoothAdapter.LeScanCallback() {
            @Override
            public void onLeScan(final BluetoothDevice device, int rssi, final byte[] scanRecord) {
                final int RSSI = rssi;
                if (RSSI >= signalThreshold){
                    scanHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            AdvertiseData data = new AdvertiseData.Builder()
                                    .addServiceUuid(ParcelUuid
                                            .fromString(UUID
                                                    .nameUUIDFromBytes(scanRecord).toString())).build();
                            scannerActivity.addDevice(device, RSSI, getUUID(data));
                        }
                    });
                }
            }
        };

//For APIs less than 21, Returns Device UUID
public String getUUID(AdvertiseData data){
    List<ParcelUuid> UUIDs = data.getServiceUuids();
    //ToastMakers.message(scannerActivity.getApplicationContext(), UUIDs.toString());
    String UUIDx = UUIDs.get(0).getUuid().toString();
    Log.e("UUID", " as list ->" + UUIDx);
    return UUIDx;
}

对于 API 版本 > 21:

private ScanCallback mScanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, final ScanResult result) {
        Log.i("callbackType", String.valueOf(callbackType));
        Log.i("result", result.toString());
        final int RSSI = result.getRssi();
        if (RSSI>=signalThreshold) {
            scanHandler.post(
                    new Runnable() {
                        @Override
                        public void run() {
                            BluetoothDevice device = result.getDevice();
                            scannerActivity.addDevice(device, result.getRssi(), getUUID(result));
                        }
                    });
        }
    } ...}

//For APIs greater than 21, Returns Device UUID
public String getUUID(ScanResult result){
    String UUIDx = UUID
            .nameUUIDFromBytes(result.getScanRecord().getBytes()).toString();
    ToastMakers.message(scannerActivity.getApplicationContext(), UUIDx);
    Log.e("UUID", " as String ->>" + UUIDx);
    return UUIDx;
}

我使用这段代码可以获得任何设备的128位UUID。 :)

更正:对于 API > 21,getUUID 函数的 "String UUIDx" 应该是:String UUIDx = result.getScanRecord().getServiceUuids().toString(); - ADEBAYO OSIPITAN
7
这两种方法都需要API > 21。 - starkej2
如何过滤扫描结果,以便只有特定的UUID可以添加到列表中? - Jeff Bootsholz

1

-1

您可以使用标准的Android BluetoothGattCharacteristic API,例如getFloatValue(int formatType, int offset),getIntValue(int formatType, int offset),getStringValue(int offset)。请参考非常好的Android开发者网站教程这里

 if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, "Received heart rate: " + heartRate);
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    }

1
在该示例代码中,行int flag = characteristic.getProperties();预期返回支持的特征字段的掩码。但实际上并不是这样。它返回特征属性,例如PROPERTY_NOTIFY。要获取HRM支持的特征字段的位掩码,需要读取并使用特征的第一个值。因此,将行int flag = characteristic.getProperties();替换为以下内容之一: byte[] charValue = characteristic.getValue(); byte flag = charValue[0]; 即可。 - obdkey

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