FastBle icon indicating copy to clipboard operation
FastBle copied to clipboard

what to get manufacturerSpecificData in onLeScan(BleDevice bleDevice) method?

Open kkpyqt opened this issue 5 years ago • 3 comments

I can find bleDevice.getScanRecord(),but did not found : SparseArray<byte[]> mandufacturerData = scanRecord.getManufacturerSpecificData(); for eg: ble外围设备可以在广播的时候设定AdvertiseData的magic number【manufacturerId 和 manufacturerSpecificData】。这样即使定义service uuid跟别人的有冲突,也可以在中心过滤该magic number来找到符合自己需求的外围设备

外围构建AdvertiseData

AdvertiseData.Builder() .setIncludeDeviceName(true) .addServiceUuid(ParcelUuid.fromString("00007777-0000-1000-8000-00805f9b34fb")) .addManufacturerData(0x7777, new byte[]{0x07, 0x07}) .build();

中心处理AdvertiseData中的

final ScanCallback scanCallback = new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { super.onScanResult(callbackType, result); ScanRecord scanRecord = result.getScanRecord(); SparseArray<byte[]> mandufacturerData = scanRecord.getManufacturerSpecificData();

此时可以根据mandufacturerData来匹配自己设定的外围设备

作者:RDuwan 链接:https://www.jianshu.com/p/2fd90849d8e0 。

kkpyqt avatar Jul 20 '19 15:07 kkpyqt

同问,有些厂商设备数据是通过ScanRecord的manufacturerSpecificData传递的,但是没有找到获取的方法

932707629 avatar Sep 23 '21 12:09 932707629

同问,有些厂商设备数据是通过ScanRecord的manufacturerSpecificData传递的,但是没有找到获取的方法

其实收到了,只是我没找到数据位,不好意思

932707629 avatar Sep 29 '21 05:09 932707629

基于sdk32,也就是Android13源码的 android.bluetooth.le.ScanRecord.java这个类的parseFromBytes(byte[] scanRecord)方法,搞出来一个解析字节数组,可以拿到manufacturerData的字节数组

public class ScanRecordUtil {
private static final int DATA_TYPE_FLAGS = 0x01;
private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
....
public static SparseArray<byte[]> parseManufactureFromScanRecordBytes(byte[] scanRecord) {
      if (scanRecord == null) {
         return null;
      }

      int currentPos = 0;
      int advertiseFlag = -1;
      List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
      List<ParcelUuid> serviceSolicitationUuids = new ArrayList<ParcelUuid>();
      String localName = null;
      int txPowerLevel = Integer.MIN_VALUE;

      SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
      Map<ParcelUuid, byte[]> serviceData = new ArrayMap<ParcelUuid, byte[]>();

      try {
         while (currentPos < scanRecord.length) {
            // length is unsigned int.
            int length = scanRecord[currentPos++] & 0xFF;
            if (length == 0) {
               break;
            }
            // Note the length includes the length of the field type itself.
            int dataLength = length - 1;
            // fieldType is unsigned int.
            int fieldType = scanRecord[currentPos++] & 0xFF;
            switch (fieldType) {
               case DATA_TYPE_FLAGS:
                  advertiseFlag = scanRecord[currentPos] & 0xFF;
                  break;
               case DATA_TYPE_LOCAL_NAME_SHORT:
               case DATA_TYPE_LOCAL_NAME_COMPLETE:
                  localName = new String(
                          extractBytes(scanRecord, currentPos, dataLength));
                  break;
               case DATA_TYPE_TX_POWER_LEVEL:
                  txPowerLevel = scanRecord[currentPos];
                  break;
               case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:
                  // The first two bytes of the manufacturer specific data are
                  // manufacturer ids in little endian.
                  int manufacturerId = ((scanRecord[currentPos + 1] & 0xFF) << 8)
                          + (scanRecord[currentPos] & 0xFF);
                  byte[] manufacturerDataBytes = extractBytes(scanRecord, currentPos + 2,
                          dataLength - 2);
                  manufacturerData.put(manufacturerId, manufacturerDataBytes);
                  break;
               default:
                  // Just ignore, we don't handle such data type.
                  break;
            }
            currentPos += dataLength;
         }

         if (serviceUuids.isEmpty()) {
            serviceUuids = null;
         }
         return manufacturerData;
      } catch (Exception e) {
         Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));
         return manufacturerData;
      }
   }
}
...
 // Helper method to extract bytes from byte array.
   private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
      byte[] bytes = new byte[length];
      System.arraycopy(scanRecord, start, bytes, 0, length);
      return bytes;
   }

在Fastble这个库使用 BleManager.getInstance().scan()扫描回调的方法中拿到了字节数组,进行遍历即可

 @Override
            public void onScanFinished(List<BleDevice> scanResultList) {

for (int i = 0; i < scanResultList.size(); i++) {  
                    byte[] scanRecordByte = targetBleDevice.getScanRecord();
                    SparseArray<byte[]> manufactureSparseArray = ScanRecordUtil.parseManufactureFromScanRecordBytes(scanRecordByte);
                    Log.d(TAG, "man:" + manufactureSparseArray.toString());
                    for(int j = 0; j < manufactureSparseArray .size(); j++){
                        int manufacturerId = manufactureSparseArray.keyAt(j);
                        byte[] nima = manufactureSparseArray.get(manufacturerId);
                        Log.d(TAG,"这个是什么玩意:manufacturerId:"+manufacturerId+"<----->"+ new String(nima));
                    }
          
                }
    
}

这个方法已经在基于Android10的设备上通过验证,正确拿到从机(也就是外围设备,也就是服务器)自己定义的Manufacture,注意,服务器这个自定义不要超过31个字节,否则传输不过来. 顺便其他localName,advertiseFlag等属性,如法炮制即可

shaycormac avatar Jul 12 '23 10:07 shaycormac