具有128位UUID的startLeScan在原生Android BLE实现上不起作用

我在我的Nexus 4的Android 4.3新引入的BLE API上使用startLeScan(新的UUID [] {MY_DESIRED_128_BIT_SERVICE_UUID},callback)时遇到了问题。

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

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为我们公司的设备。

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

有没有人面临同样的问题? 任何解决scheme

编辑

有几个与扫描有关的问题,这个问题只讨论一个:如果你也有一些扫描问题,请先阅读这个评论 。 另外请记住,我的设备强加一个16位和128位的UUID。 大多数人使用BLE标准提供的16位UUID,如心率,速度和节奏。

Solutions Collecting From Web of "具有128位UUID的startLeScan在原生Android BLE实现上不起作用"

@ Navin的代码是好的,但它包含了原来的16位Android代码的溢出错误。 (如果任何一个字节大于127,则它变成一个负整数。)

这里有一个修复这个bug的实现, 增加了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; } 

试试这个从公布的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; } 

这是至less在Android 4.3 JWR66Y中报告的错误 :

  • 如果我提供我的16位UUID,筛选工作
  • 如果我提供了128位的UUID,或者我提供了两个UUID,则过滤不会返回任何扫描结果

我的设置:我的设备在广告( 1 16位和1 128位 )上提供2个UUID,在服务发现(1 128位和3 16位)上提供4个UUID。

即使它得到修复,我也会警告大家不要使用Android提供的filter选项。 为了向后兼容,并且因为它在Samsung Galaxy S3和Android 4.3上被破坏了

尽pipe4.3似乎不支持通过128位UUID进行过滤,但这些UUID可能存在于由LeScanCallback返回的byte [] scanRecord中。

有可能是parsing这些数据的正确方法,但是如果每次通过查找要查找的UUID的偏移量来手动筛选结果,都会得到相同的数据。 您可以通过将扫描数据打印到日志(作为hexstring)并查找您感兴趣的UUID(它们可能遵循0x06或0x07并将被颠倒)来完成此操作。 一旦find偏移量,build立一个基本的filter应该不会太难。

下面是一个简单的例子,它通过一个单一的UUID进行过滤(使用Apache Commons Lang作为ArrayUtils,以及在这里find的字节到hex的方法,但是你可以在必要时replace你自己的代码)

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

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

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

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

从我的testing结果产量正是我想要的。

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

然后你可以再试一次startLeScan(UUID [],callback)。

我用这种方法成功地发现了带有特定UUID [0x1809]的温度计设备,

它为我工作。

我的经验是,我不得不提供每件服务,我想连接到一个设备,而不仅仅是我所关心的。 扫描后我最终做了服务发现来解决这个问题。

128位UUID LE扫描在Samsung S5上运行,运行Android 4.4.2; 但是是的,它在Nexus 4,7上失败。testing4.4.2,4.4.3,4.4.4。

我在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;” pipe理16位长的价值! 因此,将UINT16更改为UINT32 dor data_mask,重新编译Android,它将起作用。 Rif http://androidxref.com/5.1.1_r6/xref/external/bluetooth/bluedroid/bta/dm/bta_dm_int.h#594

我使用Android 4.3版本的N7 2013,遇到了与TI的SensorTag相同的问题。

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

  if(!mBluetoothAdapter.startLeScan(mBleScanCallback)){ Le("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); 

另外我注意到,有时连接没有build立(这可能或可能不涉及固件实现或正确closures连接)。 同样的方式似乎试图重新join加泰罗尼亚似乎是伎俩。

必须使用这些解决方法真的令人失望…