RxAndroidBle — RxBleDevice getName() всегда возвращает 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

Я пропустил какой-либо параметр конфигурации? Никакой ошибки не выдается, так что, если кто-нибудь знает...

Спасибо!


person axierjhtjz    schedule 05.07.2017    source источник


Ответы (2)


BluetoothDevice может иметь имя null, если оно не транслируется. В качестве альтернативы вы можете проверить имя из scanResult.getScanRecord().getDeviceName(), хотя оно также может быть null.

На этот вопрос уже был дан ответ здесь или здесь.

person Dariusz Seweryn    schedule 05.07.2017
comment
Прочитал эти ответы, но, поскольку это библиотека, обертывающая логику Bluetooth, возможно, она может содержать какую-то ошибку или пропустить конфигурацию. Спасибо, в любом случае. Буду следить за getScanRecord() - person axierjhtjz; 05.07.2017
comment
Исходники библиотеки открыты — вы можете сами проверить на наличие ошибок. - person Dariusz Seweryn; 05.07.2017

Копирую исходники андроида из SDK версии 27. И немного меняю. Работает.

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

}

person Jackie    schedule 02.08.2018