Android 解析蓝牙广播数据

static String toString(Map<T, byte\[\]> map) {

if (map == null) {

return "null";

}

if (map.isEmpty()) {

return "{}";

}

StringBuilder buffer = new StringBuilder();

buffer.append('{');

Iterator<Map.Entry<T, byte\[\]>> it = map.entrySet().iterator();

while (it.hasNext()) {

Map.Entry<T, byte\[\]> entry = it.next();

Object key = entry.getKey();

buffer.append(key).append("=").append(Arrays.toString(map.get(key)));

if (it.hasNext()) {

buffer.append(", ");

}

}

buffer.append('}');

return buffer.toString();

}

private static final String TAG = "ScanRecordUtil";

// The following data type values are assigned by Bluetooth SIG.

// For more details refer to Bluetooth 4.1 specification, Volume 3, Part C, Section 18.

private static final int DATA_TYPE_FLAGS = 0x01;

private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;

private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;

private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;

private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;

private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;

private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;

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_SERVICE_DATA = 0x16;

private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;

// Flags of the advertising data.

private final int mAdvertiseFlags;

@Nullable

private final List mServiceUuids;

private final SparseArray<byte\[\]> mManufacturerSpecificData;

private final Map<ParcelUuid, byte\[\]> mServiceData;

// Transmission power level(in dB).

private final int mTxPowerLevel;

// Local name of the Bluetooth LE device.

private final String mDeviceName;

// Raw bytes of scan record.

private final byte\[\] mBytes;

/**

  • Returns the advertising flags indicating the discoverable mode and capability of the device.

  • Returns -1 if the flag field is not set.

*/

public int getAdvertiseFlags() {

return mAdvertiseFlags;

}

/**

  • Returns a list of service UUIDs within the advertisement that are used to identify the

  • bluetooth GATT services.

*/

public List getServiceUuids() {

return mServiceUuids;

}

/**

  • Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific

  • data.

*/

public SparseArray<byte\[\]> getManufacturerSpecificData() {

return mManufacturerSpecificData;

}

/**

  • Returns the manufacturer specific data associated with the manufacturer id. Returns

  • {@code null} if the {@code manufacturerId} is not found.

*/

@Nullable

public byte\[\] getManufacturerSpecificData(int manufacturerId) {

return mManufacturerSpecificData.get(manufacturerId);

}

/**

  • Returns a map of service UUID and its corresponding service data.

*/

public Map<ParcelUuid, byte\[\]> getServiceData() {

return mServiceData;

}

/**

  • Returns the service data byte array associated with the {@code serviceUuid}. Returns

  • {@code null} if the {@code serviceDataUuid} is not found.

*/

@Nullable

public byte\[\] getServiceData(ParcelUuid serviceDataUuid) {

if (serviceDataUuid == null) {

return null;

}

return mServiceData.get(serviceDataUuid);

}

/**

  • Returns the transmission power level of the packet in dBm. Returns {@link Integer#MIN_VALUE}

  • if the field is not set. This value can be used to calculate the path loss of a received

  • packet using the following equation:

  • pathloss = txPowerLevel - rssi

*/

public int getTxPowerLevel() {

return mTxPowerLevel;

}

/**

  • Returns the local name of the BLE device. The is a UTF-8 encoded string.

*/

@Nullable

public String getDeviceName() {

return mDeviceName;

}

/**

  • Returns raw bytes of scan record.

*/

public byte\[\] getBytes() {

return mBytes;

}

private ScanRecordUtil(List serviceUuids,

SparseArray<byte\[\]> manufacturerData,

Map<ParcelUuid, byte\[\]> serviceData,

int advertiseFlags, int txPowerLevel,

String localName, byte\[\] bytes) {

mServiceUuids = serviceUuids;

mManufacturerSpecificData = manufacturerData;

mServiceData = serviceData;

mDeviceName = localName;

mAdvertiseFlags = advertiseFlags;

mTxPowerLevel = txPowerLevel;

mBytes = bytes;

}

/**

  • Parse scan record bytes to {@link ScanRecord}.

  • The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.

  • All numerical multi-byte entities and values shall use little-endian byte

  • order.

  • @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.

  • @hide

*/

public static ScanRecordUtil parseFromBytes(byte\[\] scanRecord) {

if (scanRecord == null) {

return null;

}

Log.e(TAG + "MYX23P", "进入parseFromBytes");

int currentPos = 0;

int advertiseFlag = -1;

List serviceUuids = new ArrayList();

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 = scanRecordcurrentPos++ & 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 = scanRecordcurrentPos++ & 0xFF;

switch (fieldType) {

case DATA_TYPE_FLAGS:

advertiseFlag = scanRecordcurrentPos & 0xFF;

break;

case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:

case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:

parseServiceUuid(scanRecord, currentPos,

dataLength,16, serviceUuids);

break;

case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:

case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:

parseServiceUuid(scanRecord, currentPos, dataLength,

32, serviceUuids);

break;

case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:

case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:

parseServiceUuid(scanRecord, currentPos, dataLength,

128, serviceUuids);

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 = scanRecordcurrentPos;

break;

case DATA_TYPE_SERVICE_DATA:

// The first two bytes of the service data are service data UUID in little

// endian. The rest bytes are service data.

int serviceUuidLength = 16;

byte\[\] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,

serviceUuidLength);

ParcelUuid serviceDataUuid = parseUuidFrom(

serviceDataUuidBytes);

byte\[\] serviceDataArray = extractBytes(scanRecord,

currentPos + serviceUuidLength, dataLength - serviceUuidLength);

serviceData.put(serviceDataUuid, serviceDataArray);

break;

case DATA_TYPE_MANUFACTURER_SPECIFIC_DATA:

// The first two bytes of the manufacturer specific data are

// manufacturer ids in little endian.

int manufacturerId = ((scanRecordcurrentPos + 1 & 0xFF) << 8) +

(scanRecordcurrentPos & 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 new ScanRecordUtil(serviceUuids, manufacturerData, serviceData,

advertiseFlag, txPowerLevel, localName, scanRecord);

} catch (Exception e) {

Log.e(TAG, "unable to parse scan record: " + Arrays.toString(scanRecord));

// As the record is invalid, ignore all the parsed results for this packet

// and return an empty record with raw scanRecord bytes in results

return new ScanRecordUtil(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);

}

}

@Override

public String toString() {

return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids

  • ", mManufacturerSpecificData=" + ScanRecordUtil.toString(mManufacturerSpecificData)

  • ", mServiceData=" + ScanRecordUtil.toString(mServiceData)

  • ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";

}

/**

  • byte数组转化为string

*/

static final char\[\] hexArray = "0123456789ABCDEF".toCharArray();

public static String bytesToHex(byte\[\] bytes) {

char\[\] hexChars = new charbytes.length \* 2;

for (int j = 0; j < bytes.length; j++) {

int v = bytesj & 0xFF;

hexCharsj \* 2 = hexArrayv \>\>\> 4;

hexCharsj \* 2 + 1 = hexArrayv \& 0x0F;

}

return new String(hexChars);

}

// Parse service UUIDs.

private static int parseServiceUuid(byte\[\] scanRecord, int currentPos, int dataLength,

int uuidLength, List serviceUuids) {

while (dataLength > 0) {

byte\[\] uuidBytes = extractBytes(scanRecord, currentPos,

uuidLength);

serviceUuids.add(parseUuidFrom(uuidBytes));

dataLength -= uuidLength;

currentPos += uuidLength;

}

return currentPos;

}

// Helper method to extract bytes from byte array.

private static byte\[\] extractBytes(byte\[\] scanRecord, int start, int length) {

byte\[\] bytes = new bytelength;

System.arraycopy(scanRecord, start, bytes, 0, length);

return bytes;

}

/**

  • 转化方法

  • @param uuidBytes

  • @return

*/

public static ParcelUuid parseUuidFrom(byte\[\] uuidBytes) {

if (uuidBytes == null) {

throw new IllegalArgumentException("uuidBytes cannot be null");

}

int length = uuidBytes.length;

if (length != 16 && length != 32 &&

length != 128) {

throw new IllegalArgumentException("uuidBytes length invalid - " + length);

相关推荐
帅次33 分钟前
Android 高级工程师面试:Flutter Widget 体系 近1年高频追问 20 题
android·flutter·面试·element·widget·setstate·renderobject
snow@li41 分钟前
Java:Java 服务器(Web 容器 / Servlet 容器)完整工作清单
java·服务器·前端
帅次1 小时前
Android 高级工程师面试:Jetpack 核心 近1年高频追问 20 题
android·面试·协程·jetpack·stateflow·lifecycle
青山木1 小时前
Hot 100 --- 二叉树与递归
java·数据结构·算法·leetcode·深度优先
洋子1 小时前
Yank Note 系列 14 - 我和 AI 怎么一起写文章
前端·markdown
爱笑鱼1 小时前
Handler(四):Handler 卡顿怎么定位?从 Slow delivery 到 Slow dispatch
android
jason成都1 小时前
GNSS-QC 数据清洗功能全面增强|滑坡监测实测验证,适配 rtklib_java 高精度解算链路
java·gnss
前端工作日常2 小时前
我学习到的Java概念
java·后端
卓怡学长2 小时前
w263基于springboot + vue 健康饮食管理系统
java·数据库·vue.js·spring boot·spring·intellij-idea
r_oo_ki_e_2 小时前
Java反射机制学习笔记
java·笔记·学习