Android 解析蓝牙广播数据(1)

本文介绍了如何解析蓝牙低功耗设备的扫描记录,包括服务UUID、本地名称、传输功率等信息,并强调了阅读源码和理解系统底层原理对于提升技术水平和建立完整知识体系的重要性,还提到了各大科技公司如腾讯、字节跳动等的面试真题作为实践参考。
摘要由CSDN通过智能技术生成

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 = 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_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 = scanRecord[currentPos];

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 = ((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 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 char[bytes.length * 2];

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

int v = bytes[j] & 0xFF;

hexChars[j * 2] = hexArray[v >>> 4];

hexChars[j * 2 + 1] = hexArray[v & 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 byte[length];

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);

}

// Construct a 128 bit UUID.

if (length == 128) {

ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);

long msb = buf.getLong(8);

long lsb = buf.getLong(0);

return new ParcelUuid(new UUID(msb, lsb));

}

// For 16 bit and 32 bit UUID we need to convert them to 128 bit value.

// 128_bit_value = uuid * 2^96 + BASE_UUID

long shortUuid;

if (length == 16) {

shortUuid = uuidBytes[0] & 0xFF;

shortUuid += (uuidBytes[1] & 0xFF) << 8;

} else {

shortUuid = uuidBytes[0] & 0xFF ;

shortUuid += (uuidBytes[1] & 0xFF) << 8;

shortUuid += (uuidBytes[2] & 0xFF) << 16;

shortUuid += (uuidBytes[3] & 0xFF) << 24;

}

long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32);

long lsb = BASE_UUID.getUuid().getLeastSignificantBits();

return new ParcelUuid(new UUID(msb, lsb));

}

最后

只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-QXfaVexx-1714331955430)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2021面试真题解析

[外链图片转存中…(img-kjjHRo6g-1714331955431)]

资料太多,全部展示会影响篇幅,暂时就先列举这些部分截图

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值