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

}