diff --git a/README.md b/README.md index dd8f74b9..ea8b8979 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,14 @@ If your device is using the Nordic Buttonless Service for switching from app mod DFU bootloader mode, this library will handle switching automatically. In case your bootloader is configured to advertise with incremented MAC address (that is you use Secure DFU and the device is not bonded) this library will need to scan for the new `BluetoothDevice`. Starting from Android -Marshmallow, **location permission** is required and has to be granted in runtime before DFU is started. +Marshmallow, **location permission** is required and has to be granted in runtime before DFU is started. + +Starting from Android 8.1.0, all scans done without a scan filter whilst the screen is turned off +will not return any scan results. + +>Note: "ACCESS_BACKGROUND_LOCATION" permission would also be required to trigger a successful DFU +whilst the device screen is turned off, mainly to receive the scan results when scanning and connecting to the +peripheral in bootloader mode while the device screen is turned off. #### Retrying Starting from version 1.9.0 the library is able to retry a DFU update in case of an unwanted diff --git a/dfu/build.gradle b/dfu/build.gradle index adb3ec1c..ab38beb9 100644 --- a/dfu/build.gradle +++ b/dfu/build.gradle @@ -6,7 +6,7 @@ android { defaultConfig { minSdkVersion 18 targetSdkVersion 29 - versionCode 24 + versionCode 25 versionName VERSION_NAME } @@ -19,7 +19,7 @@ android { } dependencies { - implementation 'androidx.core:core:1.5.0-alpha01' + implementation 'androidx.core:core:1.5.0-alpha05' implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.google.code.gson:gson:2.8.6' diff --git a/dfu/src/main/java/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java b/dfu/src/main/java/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java index 262e59f7..727352f6 100644 --- a/dfu/src/main/java/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java +++ b/dfu/src/main/java/no/nordicsemi/android/dfu/internal/scanner/BootloaderScannerLollipop.java @@ -26,10 +26,13 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.le.BluetoothLeScanner; import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanResult; import android.bluetooth.le.ScanSettings; import android.os.Build; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; /** @@ -37,87 +40,96 @@ */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class BootloaderScannerLollipop extends ScanCallback implements BootloaderScanner { - private final Object mLock = new Object(); - private String mDeviceAddress; - private String mDeviceAddressIncremented; - private String mBootloaderAddress; - private boolean mFound; - - @Override - public String searchFor(final String deviceAddress) { - final String firstBytes = deviceAddress.substring(0, 15); - final String lastByte = deviceAddress.substring(15); // assuming that the device address is correct - final String lastByteIncremented = String.format(Locale.US, "%02X", (Integer.valueOf(lastByte, 16) + ADDRESS_DIFF) & 0xFF); - - mDeviceAddress = deviceAddress; - mDeviceAddressIncremented = firstBytes + lastByteIncremented; - mBootloaderAddress = null; - mFound = false; - - // Add timeout - new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(BootloaderScanner.TIMEOUT); - } catch (final InterruptedException e) { - // do nothing - } - - if (mFound) - return; - - mBootloaderAddress = null; - mFound = true; - - // Notify the waiting thread - synchronized (mLock) { - mLock.notifyAll(); - } - } - }, "Scanner timer").start(); - - final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) - return null; - final BluetoothLeScanner scanner = adapter.getBluetoothLeScanner(); - if (scanner == null) - return null; - /* - * Scanning with filters does not work on Nexus 9 (Android 5.1). No devices are found and scanner terminates on timeout. - * We will match the device address in the callback method instead. It's not like it should be, but at least it works. - */ - //final List filters = new ArrayList<>(); - //filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddress).build()); - //filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddressIncremented).build()); - final ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); - scanner.startScan(/*filters*/ null, settings, this); - - try { - synchronized (mLock) { - while (!mFound) - mLock.wait(); - } - } catch (final InterruptedException e) { - // do nothing - } - - scanner.stopScan(this); - return mBootloaderAddress; - } - - @Override - public void onScanResult(final int callbackType, final ScanResult result) { - final String address = result.getDevice().getAddress(); - - if (mDeviceAddress.equals(address) || mDeviceAddressIncremented.equals(address)) { - mBootloaderAddress = address; - mFound = true; - - // Notify the waiting thread - synchronized (mLock) { - mLock.notifyAll(); - } - } - } + private final Object mLock = new Object(); + private String mDeviceAddress; + private String mDeviceAddressIncremented; + private String mBootloaderAddress; + private boolean mFound; + + @Override + public String searchFor(final String deviceAddress) { + final String firstBytes = deviceAddress.substring(0, 15); + final String lastByte = deviceAddress.substring(15); // assuming that the device address is correct + final String lastByteIncremented = String.format(Locale.US, "%02X", (Integer.valueOf(lastByte, 16) + ADDRESS_DIFF) & 0xFF); + + mDeviceAddress = deviceAddress; + mDeviceAddressIncremented = firstBytes + lastByteIncremented; + mBootloaderAddress = null; + mFound = false; + + // Add timeout + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(BootloaderScanner.TIMEOUT); + } catch (final InterruptedException e) { + // do nothing + } + + if (mFound) + return; + + mBootloaderAddress = null; + mFound = true; + + // Notify the waiting thread + synchronized (mLock) { + mLock.notifyAll(); + } + } + }, "Scanner timer").start(); + + final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); + if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) + return null; + final BluetoothLeScanner scanner = adapter.getBluetoothLeScanner(); + if (scanner == null) + return null; + /* + * Android 8.1 onwards, stops unfiltered BLE scanning on screen off. Therefore we must add a filter to + * get scan results in case the device screen is turned off as this may affect users wanting scan/connect to the device in background. + * See {@linktourl https://android.googlesource.com/platform/packages/apps/Bluetooth/+/319aeae6f4ebd13678b4f77375d1804978c4a1e1} + */ + final ScanSettings settings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build(); + if (adapter.isOffloadedFilteringSupported() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + final List filters = new ArrayList<>(); + filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddress).build()); + filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddressIncremented).build()); + scanner.startScan(filters, settings, this); + } else { + /* + * Scanning with filters does not work on Nexus 9 (Android 5.1). No devices are found and scanner terminates on timeout. + * We will match the device address in the callback method instead. It's not like it should be, but at least it works. + */ + scanner.startScan(null, settings, this); + } + + try { + synchronized (mLock) { + while (!mFound) + mLock.wait(); + } + } catch (final InterruptedException e) { + // do nothing + } + + scanner.stopScan(this); + return mBootloaderAddress; + } + + @Override + public void onScanResult(final int callbackType, final ScanResult result) { + final String address = result.getDevice().getAddress(); + + if (mDeviceAddress.equals(address) || mDeviceAddressIncremented.equals(address)) { + mBootloaderAddress = address; + mFound = true; + + // Notify the waiting thread + synchronized (mLock) { + mLock.notifyAll(); + } + } + } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 92ed9c13..72c50a7e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ # org.gradle.parallel=true android.useAndroidX=true -VERSION_NAME=1.11.0 +VERSION_NAME=1.11.1 GROUP=no.nordicsemi.android POM_DESCRIPTION=Device Firmware Update library