From a3fd88f21d7d72dbd421365179699ca865cac315 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Mon, 7 Jun 2021 14:19:43 +0200 Subject: [PATCH 1/4] Migration to Java 8 --- scanner/build.gradle | 4 - .../v18/scanner/BluetoothLeScannerCompat.java | 37 +++---- .../v18/scanner/BluetoothLeScannerImplJB.java | 25 ++--- .../BluetoothLeScannerImplLollipop.java | 98 +++++++++---------- .../scanner/BluetoothLeScannerImplOreo.java | 2 +- .../support/v18/scanner/BluetoothLeUtils.java | 1 + .../v18/scanner/PendingIntentExecutor.java | 2 +- .../support/v18/scanner/ScanResult.java | 47 ++++----- .../support/v18/scanner/ScanSettings.java | 38 ++++--- .../support/v18/scanner/ScannerService.java | 2 +- 10 files changed, 114 insertions(+), 142 deletions(-) diff --git a/scanner/build.gradle b/scanner/build.gradle index a22b93d..7fe224e 100644 --- a/scanner/build.gradle +++ b/scanner/build.gradle @@ -19,10 +19,6 @@ android { testCoverageEnabled true } } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_7 - targetCompatibility JavaVersion.VERSION_1_7 - } } dependencies { diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java index 887903d..2f40a2b 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerCompat.java @@ -63,7 +63,7 @@ * * @see ScanFilter */ -@SuppressWarnings("WeakerAccess") +@SuppressWarnings({"WeakerAccess", "unused"}) public abstract class BluetoothLeScannerCompat { /** @@ -129,7 +129,7 @@ public final void startScan(@NonNull final ScanCallback callback) { throw new IllegalArgumentException("callback is null"); } final Handler handler = new Handler(Looper.getMainLooper()); - startScanInternal(Collections.emptyList(), new ScanSettings.Builder().build(), + startScanInternal(Collections.emptyList(), new ScanSettings.Builder().build(), callback, handler); } @@ -156,7 +156,7 @@ public final void startScan(@Nullable final List filters, throw new IllegalArgumentException("callback is null"); } final Handler handler = new Handler(Looper.getMainLooper()); - startScanInternal(filters != null ? filters : Collections.emptyList(), + startScanInternal(filters != null ? filters : Collections.emptyList(), settings != null ? settings : new ScanSettings.Builder().build(), callback, handler); } @@ -185,7 +185,7 @@ public final void startScan(@Nullable final List filters, if (callback == null) { throw new IllegalArgumentException("callback is null"); } - startScanInternal(filters != null ? filters : Collections.emptyList(), + startScanInternal(filters != null ? filters : Collections.emptyList(), settings != null ? settings : new ScanSettings.Builder().build(), callback, handler != null ? handler : new Handler(Looper.getMainLooper())); } @@ -278,7 +278,7 @@ public final void startScan(@Nullable final List filters, if (context == null) { throw new IllegalArgumentException("context is null"); } - startScanInternal(filters != null ? filters : Collections.emptyList(), + startScanInternal(filters != null ? filters : Collections.emptyList(), settings != null ? settings : new ScanSettings.Builder().build(), context, callbackIntent, requestCode); } @@ -424,17 +424,6 @@ public final void stopScan(@NonNull final Context context, /** A collection of scan result of devices in range. */ @NonNull private final Map devicesInRange = new HashMap<>(); - @NonNull - private final Runnable flushPendingScanResultsTask = new Runnable() { - @Override - public void run() { - if (!scanningStopped) { - flushPendingScanResults(); - handler.postDelayed(this, scanSettings.getReportDelayMillis()); - } - } - }; - /** A task, called periodically, that notifies about match lost. */ @NonNull private final Runnable matchLostNotifierTask = new Runnable() { @@ -448,12 +437,7 @@ public void run() { final ScanResult result = iterator.next(); if (result.getTimestampNanos() < now - scanSettings.getMatchLostDeviceTimeout()) { iterator.remove(); - handler.post(new Runnable() { - @Override - public void run() { - scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result); - } - }); + handler.post(() -> scanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result)); } } @@ -488,6 +472,15 @@ public void run() { final long delay = settings.getReportDelayMillis(); emulateBatching = delay > 0 && (!offloadedBatchingSupported || !settings.getUseHardwareBatchingIfSupported()); if (emulateBatching) { + final Runnable flushPendingScanResultsTask = new Runnable() { + @Override + public void run() { + if (!scanningStopped) { + flushPendingScanResults(); + handler.postDelayed(this, scanSettings.getReportDelayMillis()); + } + } + }; handler.postDelayed(flushPendingScanResultsTask, delay); } } diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplJB.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplJB.java index d80eab4..5ce5749 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplJB.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplJB.java @@ -25,7 +25,6 @@ import android.Manifest; import android.app.PendingIntent; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.os.Handler; @@ -234,22 +233,14 @@ private void setPowerSaveSettings() { } } - private final BluetoothAdapter.LeScanCallback scanCallback = new BluetoothAdapter.LeScanCallback() { - @Override - public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { - final ScanResult scanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), - rssi, SystemClock.elapsedRealtimeNanos()); - - synchronized (wrappers) { - final Collection scanCallbackWrappers = wrappers.values(); - for (final ScanCallbackWrapper wrapper : scanCallbackWrappers) { - wrapper.handler.post(new Runnable() { - @Override - public void run() { - wrapper.handleScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult); - } - }); - } + private final BluetoothAdapter.LeScanCallback scanCallback = (device, rssi, scanRecord) -> { + final ScanResult scanResult = new ScanResult(device, ScanRecord.parseFromBytes(scanRecord), + rssi, SystemClock.elapsedRealtimeNanos()); + + synchronized (wrappers) { + final Collection scanCallbackWrappers = wrappers.values(); + for (final ScanCallbackWrapper wrapper : scanCallbackWrappers) { + wrapper.handler.post(() -> wrapper.handleScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult)); } } }; diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplLollipop.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplLollipop.java index 1198c11..c6e555b 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplLollipop.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplLollipop.java @@ -249,73 +249,63 @@ private ScanCallbackWrapperLollipop(final boolean offloadedBatchingSupported, @Override public void onScanResult(final int callbackType, final android.bluetooth.le.ScanResult nativeScanResult) { - handler.post(new Runnable() { - @Override - public void run() { - final BluetoothLeScannerImplLollipop scannerImpl = - (BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner(); - final ScanResult result = scannerImpl.fromNativeScanResult(nativeScanResult); - handleScanResult(callbackType, result); - } + handler.post(() -> { + final BluetoothLeScannerImplLollipop scannerImpl = + (BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner(); + final ScanResult result = scannerImpl.fromNativeScanResult(nativeScanResult); + handleScanResult(callbackType, result); }); } @Override public void onBatchScanResults(final List nativeScanResults) { - handler.post(new Runnable() { - @Override - public void run() { - // On several phones the onBatchScanResults is called twice for every batch. - // Skip the second call if came to early. - final long now = SystemClock.elapsedRealtime(); - if (lastBatchTimestamp > now - scanSettings.getReportDelayMillis() + 5) { - return; - } - lastBatchTimestamp = now; - - final BluetoothLeScannerImplLollipop scannerImpl = - (BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner(); - final List results = scannerImpl.fromNativeScanResults(nativeScanResults); - handleScanResults(results); + handler.post(() -> { + // On several phones the onBatchScanResults is called twice for every batch. + // Skip the second call if came to early. + final long now = SystemClock.elapsedRealtime(); + if (lastBatchTimestamp > now - scanSettings.getReportDelayMillis() + 5) { + return; } + lastBatchTimestamp = now; + + final BluetoothLeScannerImplLollipop scannerImpl = + (BluetoothLeScannerImplLollipop) BluetoothLeScannerCompat.getScanner(); + final List results = scannerImpl.fromNativeScanResults(nativeScanResults); + handleScanResults(results); }); } @Override public void onScanFailed(final int errorCode) { - handler.post(new Runnable() { - @Override - @RequiresPermission(allOf = {Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH}) - public void run() { - // We were able to determine offloaded batching and filtering before we started scan, - // but there is no method checking if callback types FIRST_MATCH and MATCH_LOST - // are supported. We get an error here it they are not. - if (scanSettings.getUseHardwareCallbackTypesIfSupported() - && scanSettings.getCallbackType() != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { - // On Nexus 6 with Android 6.0 (MPA44G, M Pre-release 3) the errorCode = 5 (SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES) - // On Pixel 2 with Android 9.0 the errorCode = 4 (SCAN_FAILED_FEATURE_UNSUPPORTED) - - // This feature seems to be not supported on your phone. - // Let's try to do pretty much the same in the code. - scanSettings.disableUseHardwareCallbackTypes(); - - final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner(); - try { - scanner.stopScan(scanCallback); - } catch (final Exception e) { - // Ignore - } - try { - scanner.startScanInternal(filters, scanSettings, scanCallback, handler); - } catch (final Exception e) { - // Ignore - } - return; + handler.post(() -> { + // We were able to determine offloaded batching and filtering before we started scan, + // but there is no method checking if callback types FIRST_MATCH and MATCH_LOST + // are supported. We get an error here it they are not. + if (scanSettings.getUseHardwareCallbackTypesIfSupported() + && scanSettings.getCallbackType() != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { + // On Nexus 6 with Android 6.0 (MPA44G, M Pre-release 3) the errorCode = 5 (SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES) + // On Pixel 2 with Android 9.0 the errorCode = 4 (SCAN_FAILED_FEATURE_UNSUPPORTED) + + // This feature seems to be not supported on your phone. + // Let's try to do pretty much the same in the code. + scanSettings.disableUseHardwareCallbackTypes(); + + final BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner(); + try { + scanner.stopScan(scanCallback); + } catch (final Exception e) { + // Ignore } - - // else, notify user application - handleScanError(errorCode); + try { + scanner.startScanInternal(filters, scanSettings, scanCallback, handler); + } catch (final Exception e) { + // Ignore + } + return; } + + // else, notify user application + handleScanError(errorCode); }); } }; diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreo.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreo.java index 673a469..4ac0570 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreo.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeScannerImplOreo.java @@ -95,7 +95,7 @@ throw new IllegalStateException("BT le scanner not available"); final ScanSettings nonNullSettings = settings != null ? settings : new ScanSettings.Builder().build(); - final List nonNullFilters = filters != null ? filters : Collections.emptyList(); + final List nonNullFilters = filters != null ? filters : Collections.emptyList(); final android.bluetooth.le.ScanSettings nativeSettings = toNativeScanSettings(adapter, nonNullSettings, false); List nativeFilters = null; diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java index 14a947d..2f54731 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/BluetoothLeUtils.java @@ -67,6 +67,7 @@ static String toString(@Nullable final Map map) { while (it.hasNext()) { final Map.Entry entry = it.next(); final Object key = entry.getKey(); + //noinspection SuspiciousMethodCalls buffer.append(key).append("=").append(Arrays.toString(map.get(key))); if (it.hasNext()) { buffer.append(", "); diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java index 06e9308..306bac4 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/PendingIntentExecutor.java @@ -27,7 +27,7 @@ @Nullable private Context service; private long lastBatchTimestamp; - private long reportDelay; + private final long reportDelay; /** * Creates the {@link PendingIntent} executor that will be used from a diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java index faf47fc..6781d02 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanResult.java @@ -79,26 +79,25 @@ public final class ScanResult implements Parcelable { static final int ET_CONNECTABLE_MASK = 0x01; // Remote Bluetooth device. - @SuppressWarnings("NullableProblems") @NonNull - private BluetoothDevice device; + private final BluetoothDevice device; // Scan record, including advertising data and scan response data. @Nullable private ScanRecord scanRecord; // Received signal strength. - private int rssi; + private final int rssi; // Device timestamp when the result was last seen. - private long timestampNanos; + private final long timestampNanos; - private int eventType; - private int primaryPhy; - private int secondaryPhy; - private int advertisingSid; - private int txPower; - private int periodicAdvertisingInterval; + private final int eventType; + private final int primaryPhy; + private final int secondaryPhy; + private final int advertisingSid; + private final int txPower; + private final int periodicAdvertisingInterval; /** * Constructs a new ScanResult. @@ -155,7 +154,18 @@ public ScanResult(@NonNull final BluetoothDevice device, final int eventType, } private ScanResult(final Parcel in) { - readFromParcel(in); + device = BluetoothDevice.CREATOR.createFromParcel(in); + if (in.readInt() == 1) { + scanRecord = ScanRecord.parseFromBytes(in.createByteArray()); + } + rssi = in.readInt(); + timestampNanos = in.readLong(); + eventType = in.readInt(); + primaryPhy = in.readInt(); + secondaryPhy = in.readInt(); + advertisingSid = in.readInt(); + txPower = in.readInt(); + periodicAdvertisingInterval = in.readInt(); } @Override @@ -177,21 +187,6 @@ public void writeToParcel(final Parcel dest, final int flags) { dest.writeInt(periodicAdvertisingInterval); } - private void readFromParcel(final Parcel in) { - device = BluetoothDevice.CREATOR.createFromParcel(in); - if (in.readInt() == 1) { - scanRecord = ScanRecord.parseFromBytes(in.createByteArray()); - } - rssi = in.readInt(); - timestampNanos = in.readLong(); - eventType = in.readInt(); - primaryPhy = in.readInt(); - secondaryPhy = in.readInt(); - advertisingSid = in.readInt(); - txPower = in.readInt(); - periodicAdvertisingInterval = in.readInt(); - } - @Override public int describeContents() { return 0; diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java index fbb1572..3e8a7ba 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScanSettings.java @@ -152,32 +152,32 @@ public final class ScanSettings implements Parcelable { private final long powerSaveRestInterval; // Bluetooth LE scan mode. - private int scanMode; + private final int scanMode; // Bluetooth LE scan callback type - private int callbackType; + private final int callbackType; // Time of delay for reporting the scan result - private long reportDelayMillis; + private final long reportDelayMillis; - private int matchMode; + private final int matchMode; - private int numOfMatchesPerFilter; + private final int numOfMatchesPerFilter; - private boolean useHardwareFilteringIfSupported; + private final boolean useHardwareFilteringIfSupported; - private boolean useHardwareBatchingIfSupported; + private final boolean useHardwareBatchingIfSupported; private boolean useHardwareCallbackTypesIfSupported; - private long matchLostDeviceTimeout; + private final long matchLostDeviceTimeout; - private long matchLostTaskInterval; + private final long matchLostTaskInterval; // Include only legacy advertising results - private boolean legacy; + private final boolean legacy; - private int phy; + private final int phy; public int getScanMode() { return scanMode; @@ -249,11 +249,13 @@ public long getReportDelayMillis() { } private ScanSettings(final int scanMode, final int callbackType, - final long reportDelayMillis, final int matchMode, - final int numOfMatchesPerFilter, final boolean legacy, final int phy, - final boolean hardwareFiltering, final boolean hardwareBatching, - final boolean hardwareCallbackTypes, final long matchTimeout, - final long taskInterval, + final long reportDelayMillis, + final int matchMode, final int numOfMatchesPerFilter, + final boolean legacy, final int phy, + final boolean hardwareFiltering, + final boolean hardwareBatching, + final boolean hardwareCallbackTypes, + final long matchTimeout, final long taskInterval, final long powerSaveScanInterval, final long powerSaveRestInterval) { this.scanMode = scanMode; this.callbackType = callbackType; @@ -281,6 +283,8 @@ private ScanSettings(final Parcel in) { phy = in.readInt(); useHardwareFilteringIfSupported = in.readInt() == 1; useHardwareBatchingIfSupported = in.readInt() == 1; + matchLostDeviceTimeout = in.readLong(); + matchLostTaskInterval = in.readLong(); powerSaveScanInterval = in.readLong(); powerSaveRestInterval = in.readLong(); } @@ -296,6 +300,8 @@ public void writeToParcel(final Parcel dest, final int flags) { dest.writeInt(phy); dest.writeInt(useHardwareFilteringIfSupported ? 1 : 0); dest.writeInt(useHardwareBatchingIfSupported ? 1 : 0); + dest.writeLong(matchLostDeviceTimeout); + dest.writeLong(matchLostTaskInterval); dest.writeLong(powerSaveScanInterval); dest.writeLong(powerSaveRestInterval); } diff --git a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java index 6d10769..4f0426b 100644 --- a/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java +++ b/scanner/src/main/java/no/nordicsemi/android/support/v18/scanner/ScannerService.java @@ -82,7 +82,7 @@ public int onStartCommand(final Intent intent, final int flags, final int startI if (start && !knownCallback) { final ArrayList filters = intent.getParcelableArrayListExtra(EXTRA_FILTERS); final ScanSettings settings = intent.getParcelableExtra(EXTRA_SETTINGS); - startScan(filters != null ? filters : Collections.emptyList(), + startScan(filters != null ? filters : Collections.emptyList(), settings != null ? settings : new ScanSettings.Builder().build(), callbackIntent, requestCode); } else if (stop && knownCallback) { From 2b7b5274ea41c24d9e8050648290b5eb91b8d6d9 Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Mon, 7 Jun 2021 14:23:58 +0200 Subject: [PATCH 2/4] Setting explicit Java compatibility to 1.8 --- scanner/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scanner/build.gradle b/scanner/build.gradle index 7fe224e..62f3d0c 100644 --- a/scanner/build.gradle +++ b/scanner/build.gradle @@ -19,6 +19,10 @@ android { testCoverageEnabled true } } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { From 98bf616562c50f748791b17f420881d673f3bebf Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Mon, 7 Jun 2021 15:01:26 +0200 Subject: [PATCH 3/4] Version 1.5.0 --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9a239ea..5bfd1cd 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The compat library may be found on Maven Central repository. Add it to your proj following dependency: ```Groovy -implementation 'no.nordicsemi.android.support.v18:scanner:1.4.5' +implementation 'no.nordicsemi.android.support.v18:scanner:1.5.0' ``` Projects not migrated to Android Jetpack should use version 1.3.1, which is feature-equal to 1.4.0. @@ -48,6 +48,11 @@ allprojects { } ``` +Since version 1.5 you will need to [enable desugaring of Java 8 language features](https://developer.android.com/studio/write/java8-support.html#supported_features) +if you have not already done so.(And if you are releasing an Android library, then anyone who uses +that library will also have to enable desugaring.) We expect for nearly all Android projects to have +already enabled desugaring. But if this causes problems for you, please use version 1.4.5. + ## API The Scanner Compat API is very similar to the original one, known from Android Oreo. From b838fff3210371a45f52cf4f31a494f6520bedec Mon Sep 17 00:00:00 2001 From: Aleksander Nowakowski Date: Mon, 7 Jun 2021 15:02:00 +0200 Subject: [PATCH 4/4] Version bumped --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 75b8cb9..c409b2d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ # org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=1.4.5 +VERSION_NAME=1.5.0 GROUP=no.nordicsemi.android.support.v18 POM_DESCRIPTION=Android Bluetooth LE Scanner Compat library