2626import android .bluetooth .BluetoothAdapter ;
2727import android .bluetooth .le .BluetoothLeScanner ;
2828import android .bluetooth .le .ScanCallback ;
29+ import android .bluetooth .le .ScanFilter ;
2930import android .bluetooth .le .ScanResult ;
3031import android .bluetooth .le .ScanSettings ;
3132import android .os .Build ;
3233
34+ import java .util .ArrayList ;
35+ import java .util .List ;
3336import java .util .Locale ;
3437
3538/**
3639 * @see BootloaderScanner
3740 */
3841@ TargetApi (Build .VERSION_CODES .LOLLIPOP )
3942public class BootloaderScannerLollipop extends ScanCallback implements BootloaderScanner {
40- private final Object mLock = new Object ();
41- private String mDeviceAddress ;
42- private String mDeviceAddressIncremented ;
43- private String mBootloaderAddress ;
44- private boolean mFound ;
45-
46- @ Override
47- public String searchFor (final String deviceAddress ) {
48- final String firstBytes = deviceAddress .substring (0 , 15 );
49- final String lastByte = deviceAddress .substring (15 ); // assuming that the device address is correct
50- final String lastByteIncremented = String .format (Locale .US , "%02X" , (Integer .valueOf (lastByte , 16 ) + ADDRESS_DIFF ) & 0xFF );
51-
52- mDeviceAddress = deviceAddress ;
53- mDeviceAddressIncremented = firstBytes + lastByteIncremented ;
54- mBootloaderAddress = null ;
55- mFound = false ;
56-
57- // Add timeout
58- new Thread (new Runnable () {
59- @ Override
60- public void run () {
61- try {
62- Thread .sleep (BootloaderScanner .TIMEOUT );
63- } catch (final InterruptedException e ) {
64- // do nothing
65- }
66-
67- if (mFound )
68- return ;
69-
70- mBootloaderAddress = null ;
71- mFound = true ;
72-
73- // Notify the waiting thread
74- synchronized (mLock ) {
75- mLock .notifyAll ();
76- }
77- }
78- }, "Scanner timer" ).start ();
79-
80- final BluetoothAdapter adapter = BluetoothAdapter .getDefaultAdapter ();
81- if (adapter == null || adapter .getState () != BluetoothAdapter .STATE_ON )
82- return null ;
83- final BluetoothLeScanner scanner = adapter .getBluetoothLeScanner ();
84- if (scanner == null )
85- return null ;
86- /*
87- * Scanning with filters does not work on Nexus 9 (Android 5.1). No devices are found and scanner terminates on timeout.
88- * We will match the device address in the callback method instead. It's not like it should be, but at least it works.
89- */
90- //final List<ScanFilter> filters = new ArrayList<>();
91- //filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddress).build());
92- //filters.add(new ScanFilter.Builder().setDeviceAddress(mDeviceAddressIncremented).build());
93- final ScanSettings settings = new ScanSettings .Builder ().setScanMode (ScanSettings .SCAN_MODE_LOW_LATENCY ).build ();
94- scanner .startScan (/*filters*/ null , settings , this );
95-
96- try {
97- synchronized (mLock ) {
98- while (!mFound )
99- mLock .wait ();
100- }
101- } catch (final InterruptedException e ) {
102- // do nothing
103- }
104-
105- scanner .stopScan (this );
106- return mBootloaderAddress ;
107- }
108-
109- @ Override
110- public void onScanResult (final int callbackType , final ScanResult result ) {
111- final String address = result .getDevice ().getAddress ();
112-
113- if (mDeviceAddress .equals (address ) || mDeviceAddressIncremented .equals (address )) {
114- mBootloaderAddress = address ;
115- mFound = true ;
116-
117- // Notify the waiting thread
118- synchronized (mLock ) {
119- mLock .notifyAll ();
120- }
121- }
122- }
43+ private final Object mLock = new Object ();
44+ private String mDeviceAddress ;
45+ private String mDeviceAddressIncremented ;
46+ private String mBootloaderAddress ;
47+ private boolean mFound ;
48+
49+ @ Override
50+ public String searchFor (final String deviceAddress ) {
51+ final String firstBytes = deviceAddress .substring (0 , 15 );
52+ final String lastByte = deviceAddress .substring (15 ); // assuming that the device address is correct
53+ final String lastByteIncremented = String .format (Locale .US , "%02X" , (Integer .valueOf (lastByte , 16 ) + ADDRESS_DIFF ) & 0xFF );
54+
55+ mDeviceAddress = deviceAddress ;
56+ mDeviceAddressIncremented = firstBytes + lastByteIncremented ;
57+ mBootloaderAddress = null ;
58+ mFound = false ;
59+
60+ // Add timeout
61+ new Thread (new Runnable () {
62+ @ Override
63+ public void run () {
64+ try {
65+ Thread .sleep (BootloaderScanner .TIMEOUT );
66+ } catch (final InterruptedException e ) {
67+ // do nothing
68+ }
69+
70+ if (mFound )
71+ return ;
72+
73+ mBootloaderAddress = null ;
74+ mFound = true ;
75+
76+ // Notify the waiting thread
77+ synchronized (mLock ) {
78+ mLock .notifyAll ();
79+ }
80+ }
81+ }, "Scanner timer" ).start ();
82+
83+ final BluetoothAdapter adapter = BluetoothAdapter .getDefaultAdapter ();
84+ if (adapter == null || adapter .getState () != BluetoothAdapter .STATE_ON )
85+ return null ;
86+ final BluetoothLeScanner scanner = adapter .getBluetoothLeScanner ();
87+ if (scanner == null )
88+ return null ;
89+ /*
90+ * Android 8.1 onwards, stops unfiltered BLE scanning on screen off. Therefore we must add a filter to
91+ * get scan results in case the device screen is turned off as this may affect users wanting scan/connect to the device in background.
92+ * See {@linktourl https://android.googlesource.com/platform/packages/apps/Bluetooth/+/319aeae6f4ebd13678b4f77375d1804978c4a1e1}
93+ */
94+ final ScanSettings settings = new ScanSettings .Builder ().setScanMode (ScanSettings .SCAN_MODE_LOW_LATENCY ).build ();
95+ if (adapter .isOffloadedFilteringSupported () && Build .VERSION .SDK_INT >= Build .VERSION_CODES .O_MR1 ) {
96+ final List <ScanFilter > filters = new ArrayList <>();
97+ filters .add (new ScanFilter .Builder ().setDeviceAddress (mDeviceAddress ).build ());
98+ filters .add (new ScanFilter .Builder ().setDeviceAddress (mDeviceAddressIncremented ).build ());
99+ scanner .startScan (filters , settings , this );
100+ } else {
101+ /*
102+ * Scanning with filters does not work on Nexus 9 (Android 5.1). No devices are found and scanner terminates on timeout.
103+ * We will match the device address in the callback method instead. It's not like it should be, but at least it works.
104+ */
105+ scanner .startScan (null , settings , this );
106+ }
107+
108+ try {
109+ synchronized (mLock ) {
110+ while (!mFound )
111+ mLock .wait ();
112+ }
113+ } catch (final InterruptedException e ) {
114+ // do nothing
115+ }
116+
117+ scanner .stopScan (this );
118+ return mBootloaderAddress ;
119+ }
120+
121+ @ Override
122+ public void onScanResult (final int callbackType , final ScanResult result ) {
123+ final String address = result .getDevice ().getAddress ();
124+
125+ if (mDeviceAddress .equals (address ) || mDeviceAddressIncremented .equals (address )) {
126+ mBootloaderAddress = address ;
127+ mFound = true ;
128+
129+ // Notify the waiting thread
130+ synchronized (mLock ) {
131+ mLock .notifyAll ();
132+ }
133+ }
134+ }
123135}
0 commit comments