RxAndroidBle - RxBleDevice getName() 总是 returns null
RxAndroidBle - RxBleDevice getName() always returns null
有如下代码
rxBleClient = RxBleClient.create(this);
scanSubscription = rxBleClient.scanBleDevices(
new ScanSettings.Builder()
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build())
.subscribe(new Observer<ScanResult>() {
@Override
public void onCompleted() {
Log.d(TAG_BT, "Scan completed");
}
@Override
public void onError(Throwable e) {
Log.d(TAG_BT, "Scan onError", e);
}
@Override
public void onNext(ScanResult scanResult) {
RxBleDevice device = scanResult.getBleDevice();
Log.d(TAG_BT, "Scan - " + device.getName());
if (device.getName() != null &&
device.getName().contains(
SMParameters.SM_BLUETOOTH_SSID_PREFIX)) {
connectToDevice(device);
}
}
}
);
我在每个 onNext() 事件中得到的结果如下:
07-05 13:06:24.065 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:24.416 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:25.670 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:25.706 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:26.930 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:08:18.339 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:19.567 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:24.810 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:25.981 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.024 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.027 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.029 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.042 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.098 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.101 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.123 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.130 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:27.246 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:28.508 26550-26550/com.example.app D/TAG_BT: Scan - null
我是否遗漏了任何配置参数?没有错误被抛出,所以如果有人有任何线索......
谢谢!
BluetoothDevice
如果不播出可能会有一个null
的名字。您也可以检查 scanResult.getScanRecord().getDeviceName()
中的名称,尽管它也可以是 null
.
这个问题已经回答了
or here.
我从 SDK 版本 android 复制了源代码 27.And 更改了 little.It 的作品。
public String getDeviceName(byte[] scanRecodeBytes){
ScanRecord scanRecode = ScanRecord.parseFromBytes(scanRecodeBytes);
return scanRecode.getDeviceName();
}
class ScanRecord {
private static final String TAG = "ScanRecord";
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_16_BIT = 0x16;
private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
/**
* Flags of the advertising data.
*/
private final int mAdvertiseFlags;
@Nullable
private final List<ParcelUuid> mServiceUuids;
private final SparseArray<byte[]> mManufacturerSpecificData;
private final Map<ParcelUuid, byte[]> mServiceData;
private final int mTxPowerLevel;
private final String mDeviceName;
private final byte[] mBytes;
int getAdvertiseFlags() {
return mAdvertiseFlags;
}
List<ParcelUuid> getServiceUuids() {
return mServiceUuids;
}
/**
* Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
* data.
*/
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
byte[] getManufacturerSpecificData(int manufacturerId) {
return mManufacturerSpecificData.get(manufacturerId);
}
/**
* Returns a map of service UUID and its corresponding service data.
*/
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
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:
* <p>
* <code>pathloss = txPowerLevel - rssi</code>
*/
int getTxPowerLevel() {
return mTxPowerLevel;
}
/**
* Returns the local name of the BLE device. The is a UTF-8 encoded string.
*/
@Nullable
String getDeviceName() {
return mDeviceName;
}
/**
* Returns raw bytes of scan record.
*/
byte[] getBytes() {
return mBytes;
}
private ScanRecord(@Nullable List<ParcelUuid> 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 android.bluetooth.le.ScanRecord}.
* <p>
* The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
* <p>
* All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
* order.
*
* @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
*/
static ScanRecord parseFromBytes(byte[] scanRecord) {
if (scanRecord == null) {
return null;
}
int currentPos = 0;
int advertiseFlag = -1;
List<ParcelUuid> serviceUuids = new ArrayList<>();
String localName = null;
int txPowerLevel = Integer.MIN_VALUE;
SparseArray<byte[]> manufacturerData = new SparseArray<>();
Map<ParcelUuid, byte[]> serviceData = new HashMap<>(5);
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;
ParseData parseData = new ParseData(scanRecord, currentPos, advertiseFlag, serviceUuids, localName, txPowerLevel, manufacturerData, serviceData, dataLength, fieldType).invoke();
advertiseFlag = parseData.getAdvertiseFlag();
localName = parseData.getLocalName();
txPowerLevel = parseData.getTxPowerLevel();
currentPos += dataLength;
}
if (serviceUuids.isEmpty()) {
serviceUuids = null;
}
return new ScanRecord(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 ScanRecord(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
}
}
@Override
public String toString() {
return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
+ ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData)
+ ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
+ ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
}
/**
* Parse service UUIDs.
*
* @param scanRecord scanRecord
* @param currentPos currentPos
* @param dataLength dataLength
* @param uuidLength uuidLength
* @param serviceUuids serviceUuids
* @return currentPos
*/
@SuppressWarnings("UnusedReturnValue")
private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
int uuidLength, List<ParcelUuid> serviceUuids) {
while (dataLength > 0) {
byte[] uuidBytes = extractBytes(scanRecord, currentPos,
uuidLength);
serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
dataLength -= uuidLength;
currentPos += uuidLength;
}
return currentPos;
}
/**
* Helper method to extract bytes from byte array.
*
* @param scanRecord scanRecord
* @param start start
* @param length length
* @return extract bytes
*/
private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
byte[] bytes = new byte[length];
System.arraycopy(scanRecord, start, bytes, 0, length);
return bytes;
}
private static class ParseData {
private byte[] scanRecord;
private int currentPos;
private int advertiseFlag;
private List<ParcelUuid> serviceUuids;
private String localName;
private int txPowerLevel;
private SparseArray<byte[]> manufacturerData;
private Map<ParcelUuid, byte[]> serviceData;
private int dataLength;
private int fieldType;
ParseData(byte[] scanRecord, int currentPos, int advertiseFlag, List<ParcelUuid> serviceUuids, String localName, int txPowerLevel, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, int dataLength, int fieldType) {
this.scanRecord = scanRecord;
this.currentPos = currentPos;
this.advertiseFlag = advertiseFlag;
this.serviceUuids = serviceUuids;
this.localName = localName;
this.txPowerLevel = txPowerLevel;
this.manufacturerData = manufacturerData;
this.serviceData = serviceData;
this.dataLength = dataLength;
this.fieldType = fieldType;
}
int getAdvertiseFlag() {
return advertiseFlag;
}
String getLocalName() {
return localName;
}
int getTxPowerLevel() {
return txPowerLevel;
}
ParseData invoke() {
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, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
break;
case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
parseServiceUuid(scanRecord, currentPos, dataLength,
BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
break;
case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
parseServiceUuid(scanRecord, currentPos, dataLength,
BluetoothUuid.UUID_BYTES_128_BIT, 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_16_BIT:
case DATA_TYPE_SERVICE_DATA_32_BIT:
case DATA_TYPE_SERVICE_DATA_128_BIT:
int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
} else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
}
byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
serviceUuidLength);
ParcelUuid serviceDataUuid = BluetoothUuid.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;
}
return this;
}
}
}
class BluetoothUuid {
/**
* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
* for the various services.
* <p>
* The following 128 bit values are calculated as:
* uuid * 2^96 + BASE_UUID
*/
static final ParcelUuid PARCEL_UUID =
ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AUDIO_SOURCE =
ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
static final ParcelUuid ADV_AUDIO_DIST =
ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HSP =
ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HSP_AG =
ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HANDSFREE =
ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HANDSFREE_AG =
ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AVRCP_CONTROLLER =
ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AVRCP_TARGET =
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
static final ParcelUuid OBEX_OBJECT_PUSH =
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
static final ParcelUuid HID =
ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
static final ParcelUuid HOGP =
ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
static final ParcelUuid PANU =
ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
static final ParcelUuid NAP =
ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
static final ParcelUuid BNEP =
ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
static final ParcelUuid PBAP_PCE =
ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
static final ParcelUuid PBAP_PSE =
ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MAP =
ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MNS =
ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MAS =
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
/**
* Length of bytes for 16 bit UUID
*/
static final int UUID_BYTES_16_BIT = 2;
/**
* Length of bytes for 32 bit UUID
*/
static final int UUID_BYTES_32_BIT = 4;
/**
* Length of bytes for 128 bit UUID
*/
static final int UUID_BYTES_128_BIT = 16;
static final ParcelUuid[] RESERVED_UUIDS = {
PARCEL_UUID, AUDIO_SOURCE, ADV_AUDIO_DIST, HSP, HANDSFREE, AVRCP_CONTROLLER, AVRCP_TARGET,
OBEX_OBJECT_PUSH, PANU, NAP, MAP, MNS, MAS, SAP};
static boolean isAudioSource(ParcelUuid uuid) {
return uuid.equals(AUDIO_SOURCE);
}
static boolean isAudioSink(ParcelUuid uuid) {
return uuid.equals(PARCEL_UUID);
}
static boolean isAdvAudioDist(ParcelUuid uuid) {
return uuid.equals(ADV_AUDIO_DIST);
}
static boolean isHandsfree(ParcelUuid uuid) {
return uuid.equals(HANDSFREE);
}
static boolean isHeadset(ParcelUuid uuid) {
return uuid.equals(HSP);
}
static boolean isAvrcpController(ParcelUuid uuid) {
return uuid.equals(AVRCP_CONTROLLER);
}
static boolean isAvrcpTarget(ParcelUuid uuid) {
return uuid.equals(AVRCP_TARGET);
}
static boolean isInputDevice(ParcelUuid uuid) {
return uuid.equals(HID);
}
static boolean isPanu(ParcelUuid uuid) {
return uuid.equals(PANU);
}
static boolean isNap(ParcelUuid uuid) {
return uuid.equals(NAP);
}
static boolean isBnep(ParcelUuid uuid) {
return uuid.equals(BNEP);
}
static boolean isMap(ParcelUuid uuid) {
return uuid.equals(MAP);
}
static boolean isMns(ParcelUuid uuid) {
return uuid.equals(MNS);
}
static boolean isMas(ParcelUuid uuid) {
return uuid.equals(MAS);
}
static boolean isSap(ParcelUuid uuid) {
return uuid.equals(SAP);
}
/**
* Returns true if ParcelUuid is present in uuidArray
*
* @param uuidArray - Array of ParcelUuids
* @param uuid ParcelUuid
*/
static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
boolean uuidArrayEmpty = uuidArray == null || uuidArray.length == 0;
if (uuidArrayEmpty && uuid == null) {
return true;
}
if (uuidArray == null) {
return false;
}
for (ParcelUuid element : uuidArray) {
if (element.equals(uuid)) {
return true;
}
}
return false;
}
/**
* Returns true if there any common ParcelUuids in uuidA and uuidB.
*
* @param uuidA - List of ParcelUuids
* @param uuidB - List of ParcelUuids
*/
static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
if (uuidA == null && uuidB == null) {
return true;
}
if (uuidA == null) {
return uuidB.length == 0;
}
if (uuidB == null) {
return uuidA.length == 0;
}
HashSet<ParcelUuid> uuidSet = new HashSet<>(Arrays.asList(uuidA));
for (ParcelUuid uuid : uuidB) {
if (uuidSet.contains(uuid)) {
return true;
}
}
return false;
}
/**
* Returns true if all the ParcelUuids in ParcelUuidB are present in
* ParcelUuidA
*
* @param uuidA - Array of ParcelUuidsA
* @param uuidB - Array of ParcelUuidsB
*/
static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
if (uuidA == null && uuidB == null) {
return true;
}
if (uuidA == null) {
return uuidB.length == 0;
}
if (uuidB == null) {
return true;
}
HashSet<ParcelUuid> uuidSet = new HashSet<>(Arrays.asList(uuidA));
for (ParcelUuid uuid : uuidB) {
if (!uuidSet.contains(uuid)) {
return false;
}
}
return true;
}
/**
* Extract the Service Identifier or the actual uuid from the Parcel Uuid.
* For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
* this function will return 110B
*
* @param parcelUuid ParcelUuid
* @return the service identifier.
*/
static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32;
return (int) value;
}
/**
* Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
* but the returned UUID is always in 128-bit format.
* Note UUID is little endian in Bluetooth.
*
* @param uuidBytes Byte representation of uuid.
* @return {@link android.os.ParcelUuid} parsed from bytes.
* @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
*/
static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
if (uuidBytes == null) {
throw new IllegalArgumentException("uuidBytes cannot be null");
}
int length = uuidBytes.length;
if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT &&
length != UUID_BYTES_128_BIT) {
throw new IllegalArgumentException("uuidBytes length invalid - " + length);
}
// Construct a 128 bit UUID.
if (length == UUID_BYTES_128_BIT) {
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 == UUID_BYTES_16_BIT) {
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));
}
/**
* Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID,
* Note returned value is little endian (Bluetooth).
*
* @param uuid uuid to parse.
* @return shortest representation of {@code uuid} as bytes.
* @throws IllegalArgumentException If the {@code uuid} is null.
*/
static byte[] uuidToBytes(ParcelUuid uuid) {
if (uuid == null) {
throw new IllegalArgumentException("uuid cannot be null");
}
if (is16BitUuid(uuid)) {
byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
uuidBytes[0] = (byte) (uuidVal & 0xFF);
uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
return uuidBytes;
}
if (is32BitUuid(uuid)) {
byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
uuidBytes[0] = (byte) (uuidVal & 0xFF);
uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16);
uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24);
return uuidBytes;
}
// Construct a 128 bit UUID.
long msb = uuid.getUuid().getMostSignificantBits();
long lsb = uuid.getUuid().getLeastSignificantBits();
byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
buf.putLong(8, msb);
buf.putLong(0, lsb);
return uuidBytes;
}
/**
* Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
*
* @param parcelUuid ParcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
*/
static boolean is16BitUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
return uuid.getLeastSignificantBits() == BASE_UUID.getUuid().getLeastSignificantBits() && ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
}
/**
* Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
*
* @param parcelUuid ParcelUuid
* @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
*/
static boolean is32BitUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
return uuid.getLeastSignificantBits() == BASE_UUID.getUuid().getLeastSignificantBits() && !is16BitUuid(parcelUuid) && ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
}
}
class BluetoothLeUtils {
/**
* Returns a string composed from a {@link android.util.SparseArray}.
*/
static String toString(SparseArray<byte[]> array) {
if (array == null) {
return "null";
}
if (array.size() == 0) {
return "{}";
}
StringBuilder buffer = new StringBuilder();
buffer.append('{');
for (int i = 0; i < array.size(); ++i) {
buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i)));
}
buffer.append('}');
return buffer.toString();
}
/**
* Returns a string composed from a {@link java.util.Map}.
*/
static <T> 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();
//noinspection SuspiciousMethodCalls
buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append('}');
return buffer.toString();
}
/**
* Check whether two {@link android.util.SparseArray} equal.
*/
static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
if (array == otherArray) {
return true;
}
if (array == null || otherArray == null) {
return false;
}
if (array.size() != otherArray.size()) {
return false;
}
// Keys are guaranteed in ascending order when indices are in ascending order.
for (int i = 0; i < array.size(); ++i) {
if (array.keyAt(i) != otherArray.keyAt(i) ||
!Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
return false;
}
}
return true;
}
/**
* Check whether two {@link Map} equal.
*/
static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) {
if (map == otherMap) {
return true;
}
if (map == null || otherMap == null) {
return false;
}
if (map.size() != otherMap.size()) {
return false;
}
Set<T> keys = map.keySet();
if (!keys.equals(otherMap.keySet())) {
return false;
}
for (T key : keys) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
return false;
}
} else {
byte[] bytes = map.get(key);
byte[] bytes1 = otherMap.get(key);
if (!Arrays.equals(bytes, bytes1)) {
return false;
}
}
}
return true;
}
/**
* Ensure Bluetooth is turned on.
*
* @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not
* {@link android.bluetooth.BluetoothAdapter#STATE_ON}.
*/
static void checkAdapterStateOn(BluetoothAdapter adapter) {
if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) {
throw new IllegalStateException("BT Adapter is not turned ON");
}
}
}
有如下代码
rxBleClient = RxBleClient.create(this);
scanSubscription = rxBleClient.scanBleDevices(
new ScanSettings.Builder()
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build())
.subscribe(new Observer<ScanResult>() {
@Override
public void onCompleted() {
Log.d(TAG_BT, "Scan completed");
}
@Override
public void onError(Throwable e) {
Log.d(TAG_BT, "Scan onError", e);
}
@Override
public void onNext(ScanResult scanResult) {
RxBleDevice device = scanResult.getBleDevice();
Log.d(TAG_BT, "Scan - " + device.getName());
if (device.getName() != null &&
device.getName().contains(
SMParameters.SM_BLUETOOTH_SSID_PREFIX)) {
connectToDevice(device);
}
}
}
);
我在每个 onNext() 事件中得到的结果如下:
07-05 13:06:24.065 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:24.416 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:25.670 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:25.706 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:06:26.930 24390-24390/com.example.app D/TAG_BT: Scan - null
07-05 13:08:18.339 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:19.567 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:24.810 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:25.981 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.024 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.027 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.029 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.042 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.098 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.101 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.123 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:26.130 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:27.246 26550-26550/com.example.app D/TAG_BT: Scan - null
07-05 13:08:28.508 26550-26550/com.example.app D/TAG_BT: Scan - null
我是否遗漏了任何配置参数?没有错误被抛出,所以如果有人有任何线索......
谢谢!
BluetoothDevice
如果不播出可能会有一个null
的名字。您也可以检查 scanResult.getScanRecord().getDeviceName()
中的名称,尽管它也可以是 null
.
这个问题已经回答了
我从 SDK 版本 android 复制了源代码 27.And 更改了 little.It 的作品。
public String getDeviceName(byte[] scanRecodeBytes){
ScanRecord scanRecode = ScanRecord.parseFromBytes(scanRecodeBytes);
return scanRecode.getDeviceName();
}
class ScanRecord {
private static final String TAG = "ScanRecord";
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_16_BIT = 0x16;
private static final int DATA_TYPE_SERVICE_DATA_32_BIT = 0x20;
private static final int DATA_TYPE_SERVICE_DATA_128_BIT = 0x21;
private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
/**
* Flags of the advertising data.
*/
private final int mAdvertiseFlags;
@Nullable
private final List<ParcelUuid> mServiceUuids;
private final SparseArray<byte[]> mManufacturerSpecificData;
private final Map<ParcelUuid, byte[]> mServiceData;
private final int mTxPowerLevel;
private final String mDeviceName;
private final byte[] mBytes;
int getAdvertiseFlags() {
return mAdvertiseFlags;
}
List<ParcelUuid> getServiceUuids() {
return mServiceUuids;
}
/**
* Returns a sparse array of manufacturer identifier and its corresponding manufacturer specific
* data.
*/
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
byte[] getManufacturerSpecificData(int manufacturerId) {
return mManufacturerSpecificData.get(manufacturerId);
}
/**
* Returns a map of service UUID and its corresponding service data.
*/
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
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:
* <p>
* <code>pathloss = txPowerLevel - rssi</code>
*/
int getTxPowerLevel() {
return mTxPowerLevel;
}
/**
* Returns the local name of the BLE device. The is a UTF-8 encoded string.
*/
@Nullable
String getDeviceName() {
return mDeviceName;
}
/**
* Returns raw bytes of scan record.
*/
byte[] getBytes() {
return mBytes;
}
private ScanRecord(@Nullable List<ParcelUuid> 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 android.bluetooth.le.ScanRecord}.
* <p>
* The format is defined in Bluetooth 4.1 specification, Volume 3, Part C, Section 11 and 18.
* <p>
* All numerical multi-byte entities and values shall use little-endian <strong>byte</strong>
* order.
*
* @param scanRecord The scan record of Bluetooth LE advertisement and/or scan response.
*/
static ScanRecord parseFromBytes(byte[] scanRecord) {
if (scanRecord == null) {
return null;
}
int currentPos = 0;
int advertiseFlag = -1;
List<ParcelUuid> serviceUuids = new ArrayList<>();
String localName = null;
int txPowerLevel = Integer.MIN_VALUE;
SparseArray<byte[]> manufacturerData = new SparseArray<>();
Map<ParcelUuid, byte[]> serviceData = new HashMap<>(5);
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;
ParseData parseData = new ParseData(scanRecord, currentPos, advertiseFlag, serviceUuids, localName, txPowerLevel, manufacturerData, serviceData, dataLength, fieldType).invoke();
advertiseFlag = parseData.getAdvertiseFlag();
localName = parseData.getLocalName();
txPowerLevel = parseData.getTxPowerLevel();
currentPos += dataLength;
}
if (serviceUuids.isEmpty()) {
serviceUuids = null;
}
return new ScanRecord(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 ScanRecord(null, null, null, -1, Integer.MIN_VALUE, null, scanRecord);
}
}
@Override
public String toString() {
return "ScanRecord [mAdvertiseFlags=" + mAdvertiseFlags + ", mServiceUuids=" + mServiceUuids
+ ", mManufacturerSpecificData=" + BluetoothLeUtils.toString(mManufacturerSpecificData)
+ ", mServiceData=" + BluetoothLeUtils.toString(mServiceData)
+ ", mTxPowerLevel=" + mTxPowerLevel + ", mDeviceName=" + mDeviceName + "]";
}
/**
* Parse service UUIDs.
*
* @param scanRecord scanRecord
* @param currentPos currentPos
* @param dataLength dataLength
* @param uuidLength uuidLength
* @param serviceUuids serviceUuids
* @return currentPos
*/
@SuppressWarnings("UnusedReturnValue")
private static int parseServiceUuid(byte[] scanRecord, int currentPos, int dataLength,
int uuidLength, List<ParcelUuid> serviceUuids) {
while (dataLength > 0) {
byte[] uuidBytes = extractBytes(scanRecord, currentPos,
uuidLength);
serviceUuids.add(BluetoothUuid.parseUuidFrom(uuidBytes));
dataLength -= uuidLength;
currentPos += uuidLength;
}
return currentPos;
}
/**
* Helper method to extract bytes from byte array.
*
* @param scanRecord scanRecord
* @param start start
* @param length length
* @return extract bytes
*/
private static byte[] extractBytes(byte[] scanRecord, int start, int length) {
byte[] bytes = new byte[length];
System.arraycopy(scanRecord, start, bytes, 0, length);
return bytes;
}
private static class ParseData {
private byte[] scanRecord;
private int currentPos;
private int advertiseFlag;
private List<ParcelUuid> serviceUuids;
private String localName;
private int txPowerLevel;
private SparseArray<byte[]> manufacturerData;
private Map<ParcelUuid, byte[]> serviceData;
private int dataLength;
private int fieldType;
ParseData(byte[] scanRecord, int currentPos, int advertiseFlag, List<ParcelUuid> serviceUuids, String localName, int txPowerLevel, SparseArray<byte[]> manufacturerData, Map<ParcelUuid, byte[]> serviceData, int dataLength, int fieldType) {
this.scanRecord = scanRecord;
this.currentPos = currentPos;
this.advertiseFlag = advertiseFlag;
this.serviceUuids = serviceUuids;
this.localName = localName;
this.txPowerLevel = txPowerLevel;
this.manufacturerData = manufacturerData;
this.serviceData = serviceData;
this.dataLength = dataLength;
this.fieldType = fieldType;
}
int getAdvertiseFlag() {
return advertiseFlag;
}
String getLocalName() {
return localName;
}
int getTxPowerLevel() {
return txPowerLevel;
}
ParseData invoke() {
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, BluetoothUuid.UUID_BYTES_16_BIT, serviceUuids);
break;
case DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL:
case DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE:
parseServiceUuid(scanRecord, currentPos, dataLength,
BluetoothUuid.UUID_BYTES_32_BIT, serviceUuids);
break;
case DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL:
case DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE:
parseServiceUuid(scanRecord, currentPos, dataLength,
BluetoothUuid.UUID_BYTES_128_BIT, 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_16_BIT:
case DATA_TYPE_SERVICE_DATA_32_BIT:
case DATA_TYPE_SERVICE_DATA_128_BIT:
int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_BIT;
if (fieldType == DATA_TYPE_SERVICE_DATA_32_BIT) {
serviceUuidLength = BluetoothUuid.UUID_BYTES_32_BIT;
} else if (fieldType == DATA_TYPE_SERVICE_DATA_128_BIT) {
serviceUuidLength = BluetoothUuid.UUID_BYTES_128_BIT;
}
byte[] serviceDataUuidBytes = extractBytes(scanRecord, currentPos,
serviceUuidLength);
ParcelUuid serviceDataUuid = BluetoothUuid.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;
}
return this;
}
}
}
class BluetoothUuid {
/**
* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
* for the various services.
* <p>
* The following 128 bit values are calculated as:
* uuid * 2^96 + BASE_UUID
*/
static final ParcelUuid PARCEL_UUID =
ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AUDIO_SOURCE =
ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
static final ParcelUuid ADV_AUDIO_DIST =
ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HSP =
ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HSP_AG =
ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HANDSFREE =
ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
static final ParcelUuid HANDSFREE_AG =
ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AVRCP_CONTROLLER =
ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
static final ParcelUuid AVRCP_TARGET =
ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
static final ParcelUuid OBEX_OBJECT_PUSH =
ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
static final ParcelUuid HID =
ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
static final ParcelUuid HOGP =
ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
static final ParcelUuid PANU =
ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
static final ParcelUuid NAP =
ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
static final ParcelUuid BNEP =
ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
static final ParcelUuid PBAP_PCE =
ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
static final ParcelUuid PBAP_PSE =
ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MAP =
ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MNS =
ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
static final ParcelUuid MAS =
ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
static final ParcelUuid SAP =
ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
static final ParcelUuid BASE_UUID =
ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
/**
* Length of bytes for 16 bit UUID
*/
static final int UUID_BYTES_16_BIT = 2;
/**
* Length of bytes for 32 bit UUID
*/
static final int UUID_BYTES_32_BIT = 4;
/**
* Length of bytes for 128 bit UUID
*/
static final int UUID_BYTES_128_BIT = 16;
static final ParcelUuid[] RESERVED_UUIDS = {
PARCEL_UUID, AUDIO_SOURCE, ADV_AUDIO_DIST, HSP, HANDSFREE, AVRCP_CONTROLLER, AVRCP_TARGET,
OBEX_OBJECT_PUSH, PANU, NAP, MAP, MNS, MAS, SAP};
static boolean isAudioSource(ParcelUuid uuid) {
return uuid.equals(AUDIO_SOURCE);
}
static boolean isAudioSink(ParcelUuid uuid) {
return uuid.equals(PARCEL_UUID);
}
static boolean isAdvAudioDist(ParcelUuid uuid) {
return uuid.equals(ADV_AUDIO_DIST);
}
static boolean isHandsfree(ParcelUuid uuid) {
return uuid.equals(HANDSFREE);
}
static boolean isHeadset(ParcelUuid uuid) {
return uuid.equals(HSP);
}
static boolean isAvrcpController(ParcelUuid uuid) {
return uuid.equals(AVRCP_CONTROLLER);
}
static boolean isAvrcpTarget(ParcelUuid uuid) {
return uuid.equals(AVRCP_TARGET);
}
static boolean isInputDevice(ParcelUuid uuid) {
return uuid.equals(HID);
}
static boolean isPanu(ParcelUuid uuid) {
return uuid.equals(PANU);
}
static boolean isNap(ParcelUuid uuid) {
return uuid.equals(NAP);
}
static boolean isBnep(ParcelUuid uuid) {
return uuid.equals(BNEP);
}
static boolean isMap(ParcelUuid uuid) {
return uuid.equals(MAP);
}
static boolean isMns(ParcelUuid uuid) {
return uuid.equals(MNS);
}
static boolean isMas(ParcelUuid uuid) {
return uuid.equals(MAS);
}
static boolean isSap(ParcelUuid uuid) {
return uuid.equals(SAP);
}
/**
* Returns true if ParcelUuid is present in uuidArray
*
* @param uuidArray - Array of ParcelUuids
* @param uuid ParcelUuid
*/
static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
boolean uuidArrayEmpty = uuidArray == null || uuidArray.length == 0;
if (uuidArrayEmpty && uuid == null) {
return true;
}
if (uuidArray == null) {
return false;
}
for (ParcelUuid element : uuidArray) {
if (element.equals(uuid)) {
return true;
}
}
return false;
}
/**
* Returns true if there any common ParcelUuids in uuidA and uuidB.
*
* @param uuidA - List of ParcelUuids
* @param uuidB - List of ParcelUuids
*/
static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
if (uuidA == null && uuidB == null) {
return true;
}
if (uuidA == null) {
return uuidB.length == 0;
}
if (uuidB == null) {
return uuidA.length == 0;
}
HashSet<ParcelUuid> uuidSet = new HashSet<>(Arrays.asList(uuidA));
for (ParcelUuid uuid : uuidB) {
if (uuidSet.contains(uuid)) {
return true;
}
}
return false;
}
/**
* Returns true if all the ParcelUuids in ParcelUuidB are present in
* ParcelUuidA
*
* @param uuidA - Array of ParcelUuidsA
* @param uuidB - Array of ParcelUuidsB
*/
static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
if (uuidA == null && uuidB == null) {
return true;
}
if (uuidA == null) {
return uuidB.length == 0;
}
if (uuidB == null) {
return true;
}
HashSet<ParcelUuid> uuidSet = new HashSet<>(Arrays.asList(uuidA));
for (ParcelUuid uuid : uuidB) {
if (!uuidSet.contains(uuid)) {
return false;
}
}
return true;
}
/**
* Extract the Service Identifier or the actual uuid from the Parcel Uuid.
* For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
* this function will return 110B
*
* @param parcelUuid ParcelUuid
* @return the service identifier.
*/
static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32;
return (int) value;
}
/**
* Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID,
* but the returned UUID is always in 128-bit format.
* Note UUID is little endian in Bluetooth.
*
* @param uuidBytes Byte representation of uuid.
* @return {@link android.os.ParcelUuid} parsed from bytes.
* @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
*/
static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
if (uuidBytes == null) {
throw new IllegalArgumentException("uuidBytes cannot be null");
}
int length = uuidBytes.length;
if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT &&
length != UUID_BYTES_128_BIT) {
throw new IllegalArgumentException("uuidBytes length invalid - " + length);
}
// Construct a 128 bit UUID.
if (length == UUID_BYTES_128_BIT) {
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 == UUID_BYTES_16_BIT) {
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));
}
/**
* Parse UUID to bytes. The returned value is shortest representation, a 16-bit, 32-bit or 128-bit UUID,
* Note returned value is little endian (Bluetooth).
*
* @param uuid uuid to parse.
* @return shortest representation of {@code uuid} as bytes.
* @throws IllegalArgumentException If the {@code uuid} is null.
*/
static byte[] uuidToBytes(ParcelUuid uuid) {
if (uuid == null) {
throw new IllegalArgumentException("uuid cannot be null");
}
if (is16BitUuid(uuid)) {
byte[] uuidBytes = new byte[UUID_BYTES_16_BIT];
int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
uuidBytes[0] = (byte) (uuidVal & 0xFF);
uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
return uuidBytes;
}
if (is32BitUuid(uuid)) {
byte[] uuidBytes = new byte[UUID_BYTES_32_BIT];
int uuidVal = getServiceIdentifierFromParcelUuid(uuid);
uuidBytes[0] = (byte) (uuidVal & 0xFF);
uuidBytes[1] = (byte) ((uuidVal & 0xFF00) >> 8);
uuidBytes[2] = (byte) ((uuidVal & 0xFF0000) >> 16);
uuidBytes[3] = (byte) ((uuidVal & 0xFF000000) >> 24);
return uuidBytes;
}
// Construct a 128 bit UUID.
long msb = uuid.getUuid().getMostSignificantBits();
long lsb = uuid.getUuid().getLeastSignificantBits();
byte[] uuidBytes = new byte[UUID_BYTES_128_BIT];
ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN);
buf.putLong(8, msb);
buf.putLong(0, lsb);
return uuidBytes;
}
/**
* Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid.
*
* @param parcelUuid ParcelUuid
* @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
*/
static boolean is16BitUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
return uuid.getLeastSignificantBits() == BASE_UUID.getUuid().getLeastSignificantBits() && ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
}
/**
* Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
*
* @param parcelUuid ParcelUuid
* @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
*/
static boolean is32BitUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
return uuid.getLeastSignificantBits() == BASE_UUID.getUuid().getLeastSignificantBits() && !is16BitUuid(parcelUuid) && ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
}
}
class BluetoothLeUtils {
/**
* Returns a string composed from a {@link android.util.SparseArray}.
*/
static String toString(SparseArray<byte[]> array) {
if (array == null) {
return "null";
}
if (array.size() == 0) {
return "{}";
}
StringBuilder buffer = new StringBuilder();
buffer.append('{');
for (int i = 0; i < array.size(); ++i) {
buffer.append(array.keyAt(i)).append("=").append(Arrays.toString(array.valueAt(i)));
}
buffer.append('}');
return buffer.toString();
}
/**
* Returns a string composed from a {@link java.util.Map}.
*/
static <T> 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();
//noinspection SuspiciousMethodCalls
buffer.append(key).append("=").append(Arrays.toString(map.get(key)));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append('}');
return buffer.toString();
}
/**
* Check whether two {@link android.util.SparseArray} equal.
*/
static boolean equals(SparseArray<byte[]> array, SparseArray<byte[]> otherArray) {
if (array == otherArray) {
return true;
}
if (array == null || otherArray == null) {
return false;
}
if (array.size() != otherArray.size()) {
return false;
}
// Keys are guaranteed in ascending order when indices are in ascending order.
for (int i = 0; i < array.size(); ++i) {
if (array.keyAt(i) != otherArray.keyAt(i) ||
!Arrays.equals(array.valueAt(i), otherArray.valueAt(i))) {
return false;
}
}
return true;
}
/**
* Check whether two {@link Map} equal.
*/
static <T> boolean equals(Map<T, byte[]> map, Map<T, byte[]> otherMap) {
if (map == otherMap) {
return true;
}
if (map == null || otherMap == null) {
return false;
}
if (map.size() != otherMap.size()) {
return false;
}
Set<T> keys = map.keySet();
if (!keys.equals(otherMap.keySet())) {
return false;
}
for (T key : keys) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (!Objects.deepEquals(map.get(key), otherMap.get(key))) {
return false;
}
} else {
byte[] bytes = map.get(key);
byte[] bytes1 = otherMap.get(key);
if (!Arrays.equals(bytes, bytes1)) {
return false;
}
}
}
return true;
}
/**
* Ensure Bluetooth is turned on.
*
* @throws IllegalStateException If {@code adapter} is null or Bluetooth state is not
* {@link android.bluetooth.BluetoothAdapter#STATE_ON}.
*/
static void checkAdapterStateOn(BluetoothAdapter adapter) {
if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) {
throw new IllegalStateException("BT Adapter is not turned ON");
}
}
}