【问题标题】:startLeScan with 128 bit UUIDs doesn't work on native Android BLE implementation具有 128 位 UUID 的 startLeScan 不适用于本机 Android BLE 实现
【发布时间】:2013-08-03 20:35:29
【问题描述】:

我在 Nexus 4 上使用新推出的 Android 4.3 BLE API 时遇到了问题。

回调只是没有被调用。我仍然可以在日志中看到传入的包:

08-02 15:48:57.985: I/bt-hci(1051): btu_ble_process_adv_pkt
08-02 15:48:58.636: I/bt-hci(1051): BLE HCI(id=62) event = 0x02)

如果我不使用该参数来过滤 UUID,它会起作用。我们正在为我们公司的设备使用制造商特定的 128 位 UUID

现在,我们的设备提供的服务比我在阵列中提供的更多。但这应该不是问题。

有人遇到同样的问题吗?有什么解决办法吗?

编辑

扫描有几个问题,本题只讨论一个:如果你也有扫描方面的问题,请先阅读this comment。还要记住,我的设备强制使用 16 位和 128 位 UUID。你们中的大多数人使用 BLE 标准提供的 16 位 UUID,例如心率或速度和节奏。

【问题讨论】:

  • 我在 Nexus 4、4.3 构建 JWR66Y 上看到了这个确切的问题。当我不包含任何 UUID 时,它甚至会发生。有时我需要重启,有时我会在重启范围内的 BLE 设备时开始收到回调。
  • @JonWillis 请不要混淆三个问题:1st BLE 一般不稳定(根本不接收扫描结果)和2nd Nexus 4只报告一个扫描结果,3rd 无法正确过滤扫描结果(这个问题)。从第一个问题开始:见stackoverflow.com/questions/17870189/…。一种解决方法是禁用 wifi 并在蓝牙不起作用时重新启动它。从第二个问题开始:另外请记住,Nexus 4 在一次扫描中只为每台设备显示一个扫描结果。如果要不断扫描结果,则需要重新启动扫描。
  • 感谢您的信息。当我同时遇到这三个问题时,很难不将这些问题混为一谈。
  • 请注意,根据 BLE 规范,“每次扫描每个设备一个扫描结果”实际上是非常好的行为。你应该期待它。

标签: android bluetooth-lowenergy android-4.3-jelly-bean gatt


【解决方案1】:

@Navin 的代码不错,但它包含原始 16 位 Android 代码的溢出错误。 (如果任一字节大于 127 则变为负整数。)

这是一个修复该错误的实现并且添加了 128 位支持:

private List<UUID> parseUuids(byte[] advertisedData) {
     List<UUID> uuids = new ArrayList<UUID>();

     ByteBuffer buffer = ByteBuffer.wrap(advertisedData).order(ByteOrder.LITTLE_ENDIAN);
     while (buffer.remaining() > 2) {
         byte length = buffer.get();
         if (length == 0) break;

         byte type = buffer.get();
         switch (type) {
             case 0x02: // Partial list of 16-bit UUIDs
             case 0x03: // Complete list of 16-bit UUIDs
                 while (length >= 2) {
                     uuids.add(UUID.fromString(String.format(
                             "%08x-0000-1000-8000-00805f9b34fb", buffer.getShort())));
                     length -= 2;
                 }
                 break;

             case 0x06: // Partial list of 128-bit UUIDs
             case 0x07: // Complete list of 128-bit UUIDs
                 while (length >= 16) {
                     long lsb = buffer.getLong();
                     long msb = buffer.getLong();
                     uuids.add(new UUID(msb, lsb));
                     length -= 16;
                 }
                 break;

             default:
                 buffer.position(buffer.position() + length - 1);
                 break;
         }
     }

     return uuids;
 }

【讨论】:

  • 也许你能解释一下这段代码如何更好?它可能会提高新手用户或一般用户的实用性。
  • 这是来自 GattService 吗?如果是这样,我该如何以及在哪里扩展它?
【解决方案2】:

试试这个从广告的 128 位 UUID 中检索/过滤设备:

private List<UUID> parseUUIDs(final byte[] advertisedData) {
    List<UUID> uuids = new ArrayList<UUID>();

    int offset = 0;
    while (offset < (advertisedData.length - 2)) {
        int len = advertisedData[offset++];
        if (len == 0)
            break;

        int type = advertisedData[offset++];
        switch (type) {
        case 0x02: // Partial list of 16-bit UUIDs
        case 0x03: // Complete list of 16-bit UUIDs
            while (len > 1) {
                int uuid16 = advertisedData[offset++];
                uuid16 += (advertisedData[offset++] << 8);
                len -= 2;
                uuids.add(UUID.fromString(String.format(
                        "%08x-0000-1000-8000-00805f9b34fb", uuid16)));
            }
            break;
        case 0x06:// Partial list of 128-bit UUIDs
        case 0x07:// Complete list of 128-bit UUIDs
            // Loop through the advertised 128-bit UUID's.
            while (len >= 16) {
                try {
                    // Wrap the advertised bits and order them.
                    ByteBuffer buffer = ByteBuffer.wrap(advertisedData,
                            offset++, 16).order(ByteOrder.LITTLE_ENDIAN);
                    long mostSignificantBit = buffer.getLong();
                    long leastSignificantBit = buffer.getLong();
                    uuids.add(new UUID(leastSignificantBit,
                            mostSignificantBit));
                } catch (IndexOutOfBoundsException e) {
                    // Defensive programming.
                    Log.e(LOG_TAG, e.toString());
                    continue;
                } finally {
                    // Move the offset to read the next uuid.
                    offset += 15;
                    len -= 16;
                }
            }
            break;
        default:
            offset += (len - 1);
            break;
        }
    }

    return uuids;
}

【讨论】:

  • 确保不要在扫描请求中添加 UUID 过滤器,以便返回所有通告的外围设备,并且可以从应用程序代码完成过滤。
  • 以上效果很好,谢谢。似乎有很多sn-ps,但这是唯一真正完成这项工作的。
  • 我同意@BenClayton。来自谷歌票的代码不起作用。我一直在尝试使用它。我使用这个版本的 parseUUID 方法在我的测试中取得了成功。 code.google.com/p/android/issues/…
  • 效果很好!谢谢!!
  • 对不起,我应该在什么时候使用这个解析器?我试试:List&lt;UUID&gt; uuids = parseUUIDs("0000ffe0-0000-1000-8000-00805f9b34fb".getBytes());List&lt;UUID&gt; uuids = parseUUIDs("0000ffe0".getBytes());List&lt;UUID&gt; uuids = parseUUIDs("ffe0".getBytes());
【解决方案3】:

至少在 Android 4.3 JWR66Y 中这是一个reported bug

  • 如果我提供我的 16 位 UUID,则过滤有效
  • 如果我提供 128 位 UUID 或同时提供两个 UUID,过滤不会返回任何扫描结果

我的设置:我的设备提供 2 个用于广告的 UUID (1 16bit and 1 128bit) 和 4 个用于服务发现的 UUID(1 128 位和 3 个 16 位)。

即使它得到修复,我也警告大家不要使用 Android 提供的过滤器选项。为了向后兼容,因为它在搭载 Android 4.3 的三星 Galaxy S3 上已损坏

【讨论】:

    【解决方案4】:

    虽然 4.3 似乎不支持按 128 位 UUID 过滤,但这些 UUID 很可能存在于 LeScanCallback 返回的 byte[] scanRecord 中。

    可能有一种正确的方法来解析这些数据,但如果您每次都获得相同的数据,您可以通过查找您正在寻找的 UUID 的偏移量来手动过滤结果。您可以通过将扫描数据打印到日志(作为十六进制字符串)并查找您感兴趣的 UUID 来执行此操作(它们可能会跟随 0x06 或 0x07 并且将被反转)。找到偏移量后,设置基本过滤器应该不会太难。

    这是一个按单个 UUID 过滤的简单示例(ArrayUtils 使用 Apache Commons Langhere 找到的 bytes-to-hex 方法,但您可以在必要时替换您自己的代码)

    public static boolean hasMyService(byte[] scanRecord) {
    
        // UUID we want to filter by (without hyphens)
        final String myServiceID = "0000000000001000800000805F9B34FB";
    
        // The offset in the scan record. In my case the offset was 13; it will probably be different for you
        final int serviceOffset = 13; 
    
        try{
    
            // Get a 16-byte array of what may or may not be the service we're filtering for
            byte[] service = ArrayUtils.subarray(scanRecord, serviceOffset, serviceOffset + 16);
    
            // The bytes are probably in reverse order, so we need to fix that
            ArrayUtils.reverse(service);
    
            // Get the hex string
            String discoveredServiceID = bytesToHex(service);
    
            // Compare against our service
            return myServiceID.equals(discoveredServiceID);
    
        } catch (Exception e){
            return false;
        }
    
    }
    

    【讨论】:

    • 这是真的。我实际上是在扫描结果后使用这样的过滤。
    【解决方案5】:

    您确定外设在广告数据或扫描响应数据中列出了指定的服务 UUID?

    【讨论】:

    • 是的,我想要的服务包含在广告中(就像所有主要服务一样)。它适用于三星 Galaxy S4 及其 SAMSUNG BLE SDK。
    • 好的,那我不知道,抱歉。然而,这听起来确实像是 Android API 中的一个错误。
    【解决方案6】:

    从扫描结果中列出服务 UUID 的最佳方法是从位于 android 内部的 ScanRecord.java 中准确复制 parseFromBytes 方法.bluetooth.le 包(确保您拥有最新的 Android SDK),将返回更改为 ParcelUuidList,因为这是我们唯一关心的事情

        private static final int DATA_TYPE_FLAGS = 0x01;
        private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL = 0x02;
        private static final int DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE = 0x03;
        private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_PARTIAL = 0x04;
        private static final int DATA_TYPE_SERVICE_UUIDS_32_BIT_COMPLETE = 0x05;
        private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_PARTIAL = 0x06;
        private static final int DATA_TYPE_SERVICE_UUIDS_128_BIT_COMPLETE = 0x07;
        private static final int DATA_TYPE_LOCAL_NAME_SHORT = 0x08;
        private static final int DATA_TYPE_LOCAL_NAME_COMPLETE = 0x09;
        private static final int DATA_TYPE_TX_POWER_LEVEL = 0x0A;
        private static final int DATA_TYPE_SERVICE_DATA = 0x16;
        private static final int DATA_TYPE_MANUFACTURER_SPECIFIC_DATA = 0xFF;
    
        public static List<ParcelUuid> parseFromBytes(byte[] scanRecord) {
            if (scanRecord == null) {
                return null;
            }
    
            int currentPos = 0;
            int advertiseFlag = -1;
            List<ParcelUuid> serviceUuids = new ArrayList<ParcelUuid>();
            String localName = null;
            int txPowerLevel = Integer.MIN_VALUE;
    
            SparseArray<byte[]> manufacturerData = new SparseArray<byte[]>();
            Map<ParcelUuid, byte[]> serviceData = new HashMap<ParcelUuid, byte[]>();
    
            try {
                while (currentPos < scanRecord.length) {
                    // length is unsigned int.
                    int length = scanRecord[currentPos++] & 0xFF;
                    if (length == 0) {
                        break;
                    }
                    // Note the length includes the length of the field type itself.
                    int dataLength = length - 1;
                    // fieldType is unsigned int.
                    int fieldType = scanRecord[currentPos++] & 0xFF;
                    switch (fieldType) {
                        case DATA_TYPE_FLAGS:
                            advertiseFlag = scanRecord[currentPos] & 0xFF;
                            break;
                        case DATA_TYPE_SERVICE_UUIDS_16_BIT_PARTIAL:
                        case DATA_TYPE_SERVICE_UUIDS_16_BIT_COMPLETE:
                            parseServiceUuid(scanRecord, currentPos,
                                    dataLength, 2, 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:
                            // The first two bytes of the service data are service data UUID in little
                            // endian. The rest bytes are service data.
                            int serviceUuidLength = BluetoothUuid.UUID_BYTES_16_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;
                    }
                    currentPos += dataLength;
                }
    
                if (serviceUuids.isEmpty()) {
                    serviceUuids = null;
                }
    
    //            Log.i("SERVICE UUIDS", parcelUuidToString(serviceUuids));
            } 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 serviceUuids;
        }
    

    您还需要从同一个包中导入 BluetoothUuid.java

    import java.util.UUID;
    import android.os.ParcelUuid;
    import java.nio.ByteBuffer;
    import java.nio.ByteOrder;
    import java.util.Arrays;
    import java.util.HashSet;
    /**
     * Static helper methods and constants to decode the ParcelUuid of remote devices.
     *  @hide
     */
    public final class BluetoothUuid {
        /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
         * for the various services.
         *
         * The following 128 bit values are calculated as:
         *  uuid * 2^96 + BASE_UUID
         */
        public static final ParcelUuid AudioSink =
                ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid AudioSource =
                ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid AdvAudioDist =
                ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid HSP =
                ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid HSP_AG =
                ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid Handsfree =
                ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid Handsfree_AG =
                ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid AvrcpController =
                ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid AvrcpTarget =
                ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid ObexObjectPush =
                ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
        public static final ParcelUuid Hid =
                ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
        public static final ParcelUuid Hogp =
                ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
        public static final ParcelUuid PANU =
                ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid NAP =
                ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid BNEP =
                ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid PBAP_PCE =
                ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid PBAP_PSE =
                ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid MAP =
                ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid MNS =
                ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid MAS =
                ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
        public static final ParcelUuid SAP =
                ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
    
        public static final ParcelUuid BASE_UUID =
                ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
        /** Length of bytes for 16 bit UUID */
        public static final int UUID_BYTES_16_BIT = 2;
        /** Length of bytes for 32 bit UUID */
        public static final int UUID_BYTES_32_BIT = 4;
        /** Length of bytes for 128 bit UUID */
        public static final int UUID_BYTES_128_BIT = 16;
        public static final ParcelUuid[] RESERVED_UUIDS = {
                AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
                ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
        public static boolean isAudioSource(ParcelUuid uuid) {
            return uuid.equals(AudioSource);
        }
        public static boolean isAudioSink(ParcelUuid uuid) {
            return uuid.equals(AudioSink);
        }
        public static boolean isAdvAudioDist(ParcelUuid uuid) {
            return uuid.equals(AdvAudioDist);
        }
        public static boolean isHandsfree(ParcelUuid uuid) {
            return uuid.equals(Handsfree);
        }
        public static boolean isHeadset(ParcelUuid uuid) {
            return uuid.equals(HSP);
        }
        public static boolean isAvrcpController(ParcelUuid uuid) {
            return uuid.equals(AvrcpController);
        }
        public static boolean isAvrcpTarget(ParcelUuid uuid) {
            return uuid.equals(AvrcpTarget);
        }
        public static boolean isInputDevice(ParcelUuid uuid) {
            return uuid.equals(Hid);
        }
        public static boolean isPanu(ParcelUuid uuid) {
            return uuid.equals(PANU);
        }
        public static boolean isNap(ParcelUuid uuid) {
            return uuid.equals(NAP);
        }
        public static boolean isBnep(ParcelUuid uuid) {
            return uuid.equals(BNEP);
        }
        public static boolean isMap(ParcelUuid uuid) {
            return uuid.equals(MAP);
        }
        public static boolean isMns(ParcelUuid uuid) {
            return uuid.equals(MNS);
        }
        public static boolean isMas(ParcelUuid uuid) {
            return uuid.equals(MAS);
        }
        public static boolean isSap(ParcelUuid uuid) {
            return uuid.equals(SAP);
        }
        /**
         * Returns true if ParcelUuid is present in uuidArray
         *
         * @param uuidArray - Array of ParcelUuids
         * @param uuid
         */
        public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
            if ((uuidArray == null || uuidArray.length == 0) && 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
         *
         */
        public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
            if (uuidA == null && uuidB == null) return true;
            if (uuidA == null) {
                return uuidB.length == 0 ? true : false;
            }
            if (uuidB == null) {
                return uuidA.length == 0 ? true : false;
            }
            HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (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
         *
         */
        public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
            if (uuidA == null && uuidB == null) return true;
            if (uuidA == null) {
                return uuidB.length == 0 ? true : false;
            }
            if (uuidB == null) return true;
            HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (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
         * @return the service identifier.
         */
        public 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 ParcelUuid} parsed from bytes.
         * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
         */
        public 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.
         */
        public 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
         * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
         */
        public static boolean is16BitUuid(ParcelUuid parcelUuid) {
            UUID uuid = parcelUuid.getUuid();
            if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
                return false;
            }
            return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L);
        }
        /**
         * Check whether the given parcelUuid can be converted to 32 bit bluetooth uuid.
         *
         * @param parcelUuid
         * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
         */
        public static boolean is32BitUuid(ParcelUuid parcelUuid) {
            UUID uuid = parcelUuid.getUuid();
            if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) {
                return false;
            }
            if (is16BitUuid(parcelUuid)) {
                return false;
            }
            return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
        }
    

    根据我的测试,结果完全符合我的要求。

    【讨论】:

      【解决方案7】:

      您需要在广告数据中添加服务 UUID 作为link

      那你可以重试startLeScan(UUID[],callback)。

      我已经通过这种方法成功地发现了具有特定 UUID[0x1809] 的温度计设备

      它确实对我有用。

      【讨论】:

      • 我有 0x03 《16 位服务类 UUID 的完整列表》,你有,我有 0x07 《128 位服务类 UUID 的完整列表》,还有你没有的。我想要的服务是 128 位服务,因此包含在 0x07 中。但感谢您确认,16 位 UUID 似乎可以工作。
      【解决方案8】:

      我的经验是,我必须提供我想连接的设备所提供的每一项服务,而不仅仅是我关心的那个。为了解决这个问题,我最终在扫描后进行了服务发现。

      【讨论】:

      • 刚试过。如果我将设备提供的每项服务都放在其广告中,则在我的情况下不起作用。 (在我的情况下,它是一个 16 位和一个 128 位 UUID)
      【解决方案9】:

      128 位 UUID LE 扫描适用于三星 S5,运行 Android 4.4.2;但是是的,它在 Nexus 4、7 上失败。在 4.4.2、4.4.3、4.4.4 上测试。

      【讨论】:

        【解决方案10】:

        我在 Android 源代码 5.x 中发现了一个错误,但在 6.x 中不存在。

        这个文件中有一个函数: http://androidxref.com/5.1.1_r6/xref/external/bluetooth/bluedroid/bta/dm/bta_dm_api.c#1560 用于将 32 位 data_mask 传递给蓝牙文件广告和扫描响应堆栈。 但是结构“tBTA_DM_API_SET_ADV_CONFIG;”管理 16 位长值! 所以将 UINT16 更改为 UINT32 或 data_mask,重新编译 Android 即可。 里夫 http://androidxref.com/5.1.1_r6/xref/external/bluetooth/bluedroid/bta/dm/bta_dm_int.h#594

        【讨论】:

          【解决方案11】:

          我在使用我的 N7 2013 和 Android 4.3 的 TI 的 SensorTag 时遇到了同样的问题。

          我发现工作是启动 LeScan,等待一秒钟,停止它然后重新启动它。

                  if(!mBluetoothAdapter.startLeScan(mBleScanCallback)){
                      L.e("could not start scan action");
                  }
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {}
                  mBluetoothAdapter.stopLeScan(mBleScanCallback);
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException e) {}
                  mBluetoothAdapter.startLeScan(mBleScanCallback);
          

          我还注意到,有时连接未建立(这可能与固件实现或正确关闭连接有关,也可能无关)。以同样的方式,尝试重新连接到 gatt 似乎可以解决问题。

          不得不使用这些变通办法真是令人失望......

          【讨论】:

          • 此问题讨论无法根据 API 过滤扫描结果。没有过滤器的扫描工作正常。我不确定您的答案是关于过滤还是仅扫描不带过滤器的 uuid。
          • 在我的问题中查看我的编辑。你的回答不适用于我的问题。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-03-20
          • 1970-01-01
          • 1970-01-01
          • 2014-12-22
          • 2017-04-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多