• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.gatt;
18 
19 import android.annotation.RequiresPermission;
20 import android.app.ActivityManager;
21 import android.app.AlarmManager;
22 import android.app.PendingIntent;
23 import android.bluetooth.le.ScanCallback;
24 import android.bluetooth.le.ScanFilter;
25 import android.bluetooth.le.ScanSettings;
26 import android.content.BroadcastReceiver;
27 import android.content.ContentResolver;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.hardware.display.DisplayManager;
32 import android.location.LocationManager;
33 import android.os.Handler;
34 import android.os.HandlerThread;
35 import android.os.Looper;
36 import android.os.Message;
37 import android.os.RemoteException;
38 import android.os.SystemClock;
39 import android.provider.Settings;
40 import android.util.Log;
41 import android.util.SparseBooleanArray;
42 import android.util.SparseIntArray;
43 import android.view.Display;
44 
45 import com.android.bluetooth.Utils;
46 import com.android.bluetooth.btservice.AdapterService;
47 import com.android.bluetooth.btservice.BluetoothAdapterProxy;
48 import com.android.internal.annotations.VisibleForTesting;
49 
50 import java.util.ArrayDeque;
51 import java.util.Collections;
52 import java.util.Deque;
53 import java.util.HashMap;
54 import java.util.HashSet;
55 import java.util.Map;
56 import java.util.Set;
57 import java.util.UUID;
58 import java.util.concurrent.ConcurrentHashMap;
59 import java.util.concurrent.CountDownLatch;
60 import java.util.concurrent.TimeUnit;
61 
62 /**
63  * Class that handles Bluetooth LE scan related operations.
64  *
65  * @hide
66  */
67 public class ScanManager {
68     private static final boolean DBG = GattServiceConfig.DBG;
69     private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager";
70 
71     /**
72      * Scan params corresponding to regular scan setting
73      */
74     private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 140;
75     private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 1400;
76     private static final int SCAN_MODE_BALANCED_WINDOW_MS = 183;
77     private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 730;
78     private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 100;
79     private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 100;
80     public static final int SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS = 512;
81     public static final int SCAN_MODE_SCREEN_OFF_LOW_POWER_INTERVAL_MS = 10240;
82     public static final int SCAN_MODE_SCREEN_OFF_BALANCED_WINDOW_MS = 183;
83     public static final int SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS = 730;
84 
85     // Result type defined in bt stack. Need to be accessed by GattService.
86     static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
87     static final int SCAN_RESULT_TYPE_FULL = 2;
88     static final int SCAN_RESULT_TYPE_BOTH = 3;
89 
90     // Messages for handling BLE scan operations.
91     @VisibleForTesting
92     static final int MSG_START_BLE_SCAN = 0;
93     static final int MSG_STOP_BLE_SCAN = 1;
94     static final int MSG_FLUSH_BATCH_RESULTS = 2;
95     static final int MSG_SCAN_TIMEOUT = 3;
96     static final int MSG_SUSPEND_SCANS = 4;
97     static final int MSG_RESUME_SCANS = 5;
98     static final int MSG_IMPORTANCE_CHANGE = 6;
99     static final int MSG_SCREEN_ON = 7;
100     static final int MSG_SCREEN_OFF = 8;
101     static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9;
102     private static final String ACTION_REFRESH_BATCHED_SCAN =
103             "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";
104 
105     // Timeout for each controller operation.
106     private static final int OPERATION_TIME_OUT_MILLIS = 500;
107     private static final int MAX_IS_UID_FOREGROUND_MAP_SIZE = 500;
108 
109     private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
110     // Scan parameters for batch scan.
111     private BatchScanParams mBatchScanParms;
112 
113     private Integer mCurUsedTrackableAdvertisements;
114     private final GattService mService;
115     private final AdapterService mAdapterService;
116     private BroadcastReceiver mBatchAlarmReceiver;
117     private boolean mBatchAlarmReceiverRegistered;
118     private ScanNative mScanNative;
119     private volatile ClientHandler mHandler;
120     private BluetoothAdapterProxy mBluetoothAdapterProxy;
121 
122     private Set<ScanClient> mRegularScanClients;
123     private Set<ScanClient> mBatchClients;
124     private Set<ScanClient> mSuspendedScanClients;
125     private SparseIntArray mPriorityMap = new SparseIntArray();
126 
127     private CountDownLatch mLatch;
128 
129     private DisplayManager mDm;
130 
131     private ActivityManager mActivityManager;
132     private LocationManager mLocationManager;
133     private static final int FOREGROUND_IMPORTANCE_CUTOFF =
134             ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE;
135     private static final boolean DEFAULT_UID_IS_FOREGROUND = true;
136     private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER;
137     private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray();
138     private boolean mScreenOn = false;
139 
140     @VisibleForTesting
141     static class UidImportance {
142         public int uid;
143         public int importance;
144 
UidImportance(int uid, int importance)145         UidImportance(int uid, int importance) {
146             this.uid = uid;
147             this.importance = importance;
148         }
149     }
150 
ScanManager(GattService service, AdapterService adapterService, BluetoothAdapterProxy bluetoothAdapterProxy)151     ScanManager(GattService service, AdapterService adapterService,
152             BluetoothAdapterProxy bluetoothAdapterProxy) {
153         mRegularScanClients =
154                 Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
155         mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
156         mSuspendedScanClients =
157                 Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>());
158         mService = service;
159         mScanNative = new ScanNative();
160         mCurUsedTrackableAdvertisements = 0;
161         mDm = mService.getSystemService(DisplayManager.class);
162         mActivityManager = mService.getSystemService(ActivityManager.class);
163         mLocationManager = mService.getSystemService(LocationManager.class);
164         mAdapterService = adapterService;
165         mBluetoothAdapterProxy = bluetoothAdapterProxy;
166 
167         mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0);
168         mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1);
169         mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_POWER, 2);
170         mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED, 3);
171         // BALANCED and AMBIENT_DISCOVERY now have the same settings and priority.
172         mPriorityMap.put(ScanSettings.SCAN_MODE_BALANCED, 4);
173         mPriorityMap.put(ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY, 4);
174         mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_LATENCY, 5);
175     }
176 
start()177     void start() {
178         HandlerThread thread = new HandlerThread("BluetoothScanManager");
179         thread.start();
180         mHandler = new ClientHandler(thread.getLooper());
181         if (mDm != null) {
182             mDm.registerDisplayListener(mDisplayListener, null);
183         }
184         mScreenOn = isScreenOn();
185         if (mActivityManager != null) {
186             mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
187                     FOREGROUND_IMPORTANCE_CUTOFF);
188         }
189         IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
190         mService.registerReceiver(mLocationReceiver, locationIntentFilter);
191     }
192 
cleanup()193     void cleanup() {
194         mRegularScanClients.clear();
195         mBatchClients.clear();
196         mSuspendedScanClients.clear();
197         mScanNative.cleanup();
198 
199         if (mActivityManager != null) {
200             try {
201                 mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
202             } catch (IllegalArgumentException e) {
203                 Log.w(TAG, "exception when invoking removeOnUidImportanceListener", e);
204             }
205         }
206 
207         if (mDm != null) {
208             mDm.unregisterDisplayListener(mDisplayListener);
209         }
210 
211         if (mHandler != null) {
212             // Shut down the thread
213             mHandler.removeCallbacksAndMessages(null);
214             Looper looper = mHandler.getLooper();
215             if (looper != null) {
216                 looper.quitSafely();
217             }
218             mHandler = null;
219         }
220 
221         try {
222             mService.unregisterReceiver(mLocationReceiver);
223         } catch (IllegalArgumentException e) {
224             Log.w(TAG, "exception when invoking unregisterReceiver(mLocationReceiver)", e);
225         }
226     }
227 
registerScanner(UUID uuid)228     void registerScanner(UUID uuid) {
229         mScanNative.registerScannerNative(uuid.getLeastSignificantBits(),
230                 uuid.getMostSignificantBits());
231     }
232 
unregisterScanner(int scannerId)233     void unregisterScanner(int scannerId) {
234         mScanNative.unregisterScannerNative(scannerId);
235     }
236 
237     /**
238      * Returns the regular scan queue.
239      */
getRegularScanQueue()240     Set<ScanClient> getRegularScanQueue() {
241         return mRegularScanClients;
242     }
243 
244     /**
245      * Returns the suspended scan queue.
246      */
getSuspendedScanQueue()247     Set<ScanClient> getSuspendedScanQueue() {
248         return mSuspendedScanClients;
249     }
250 
251     /**
252      * Returns batch scan queue.
253      */
getBatchScanQueue()254     Set<ScanClient> getBatchScanQueue() {
255         return mBatchClients;
256     }
257 
258     /**
259      * Returns a set of full batch scan clients.
260      */
getFullBatchScanQueue()261     Set<ScanClient> getFullBatchScanQueue() {
262         // TODO: split full batch scan clients and truncated batch clients so we don't need to
263         // construct this every time.
264         Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
265         for (ScanClient client : mBatchClients) {
266             if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
267                 fullBatchClients.add(client);
268             }
269         }
270         return fullBatchClients;
271     }
272 
startScan(ScanClient client)273     void startScan(ScanClient client) {
274         if (DBG) {
275             Log.d(TAG, "startScan() " + client);
276         }
277         sendMessage(MSG_START_BLE_SCAN, client);
278     }
279 
stopScan(int scannerId)280     void stopScan(int scannerId) {
281         ScanClient client = mScanNative.getBatchScanClient(scannerId);
282         if (client == null) {
283             client = mScanNative.getRegularScanClient(scannerId);
284         }
285         if (client == null) {
286             client = mScanNative.getSuspendedScanClient(scannerId);
287         }
288         sendMessage(MSG_STOP_BLE_SCAN, client);
289     }
290 
flushBatchScanResults(ScanClient client)291     void flushBatchScanResults(ScanClient client) {
292         sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
293     }
294 
callbackDone(int scannerId, int status)295     void callbackDone(int scannerId, int status) {
296         if (DBG) {
297             Log.d(TAG, "callback done for scannerId - " + scannerId + " status - " + status);
298         }
299         if (status == 0) {
300             mLatch.countDown();
301         }
302         // TODO: add a callback for scan failure.
303     }
304 
sendMessage(int what, ScanClient client)305     private void sendMessage(int what, ScanClient client) {
306         final ClientHandler handler = mHandler;
307         if (handler == null) {
308             Log.d(TAG, "sendMessage: mHandler is null.");
309             return;
310         }
311         Message message = new Message();
312         message.what = what;
313         message.obj = client;
314         handler.sendMessage(message);
315     }
316 
isFilteringSupported()317     private boolean isFilteringSupported() {
318         if (mBluetoothAdapterProxy == null) {
319             Log.e(TAG, "mBluetoothAdapterProxy is null");
320             return false;
321         }
322         return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported();
323     }
324 
325     // Handler class that handles BLE scan operations.
326     private class ClientHandler extends Handler {
327 
ClientHandler(Looper looper)328         ClientHandler(Looper looper) {
329             super(looper);
330         }
331 
332         @Override
handleMessage(Message msg)333         public void handleMessage(Message msg) {
334             switch (msg.what) {
335                 case MSG_START_BLE_SCAN:
336                     handleStartScan((ScanClient) msg.obj);
337                     break;
338                 case MSG_STOP_BLE_SCAN:
339                     handleStopScan((ScanClient) msg.obj);
340                     break;
341                 case MSG_FLUSH_BATCH_RESULTS:
342                     handleFlushBatchResults((ScanClient) msg.obj);
343                     break;
344                 case MSG_SCAN_TIMEOUT:
345                     mScanNative.regularScanTimeout((ScanClient) msg.obj);
346                     break;
347                 case MSG_SUSPEND_SCANS:
348                     handleSuspendScans();
349                     break;
350                 case MSG_RESUME_SCANS:
351                     handleResumeScans();
352                     break;
353                 case MSG_SCREEN_OFF:
354                     handleScreenOff();
355                     break;
356                 case MSG_SCREEN_ON:
357                     handleScreenOn();
358                     break;
359                 case MSG_REVERT_SCAN_MODE_UPGRADE:
360                     revertScanModeUpgrade((ScanClient) msg.obj);
361                     break;
362                 case MSG_IMPORTANCE_CHANGE:
363                     handleImportanceChange((UidImportance) msg.obj);
364                     break;
365                 default:
366                     // Shouldn't happen.
367                     Log.e(TAG, "received an unkown message : " + msg.what);
368             }
369         }
370 
handleStartScan(ScanClient client)371         void handleStartScan(ScanClient client) {
372             if (DBG) {
373                 Log.d(TAG, "handling starting scan");
374             }
375 
376             if (!isScanSupported(client)) {
377                 Log.e(TAG, "Scan settings not supported");
378                 return;
379             }
380 
381             if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) {
382                 Log.e(TAG, "Scan already started");
383                 return;
384             }
385 
386             if (requiresScreenOn(client) && !mScreenOn) {
387                 Log.w(TAG, "Cannot start unfiltered scan in screen-off. This scan will be resumed "
388                         + "later: " + client.scannerId);
389                 mSuspendedScanClients.add(client);
390                 if (client.stats != null) {
391                     client.stats.recordScanSuspend(client.scannerId);
392                 }
393                 return;
394             }
395 
396             final boolean locationEnabled = mLocationManager.isLocationEnabled();
397             if (requiresLocationOn(client) && !locationEnabled) {
398                 Log.i(TAG, "Cannot start unfiltered scan in location-off. This scan will be"
399                         + " resumed when location is on: " + client.scannerId);
400                 mSuspendedScanClients.add(client);
401                 if (client.stats != null) {
402                     client.stats.recordScanSuspend(client.scannerId);
403                 }
404                 return;
405             }
406 
407             // Begin scan operations.
408             if (isBatchClient(client)) {
409                 mBatchClients.add(client);
410                 mScanNative.startBatchScan(client);
411             } else {
412                 updateScanModeBeforeStart(client);
413                 mRegularScanClients.add(client);
414                 mScanNative.startRegularScan(client);
415                 if (!mScanNative.isOpportunisticScanClient(client)) {
416                     mScanNative.configureRegularScanParams();
417 
418                     if (!mScanNative.isExemptFromScanDowngrade(client)) {
419                         Message msg = obtainMessage(MSG_SCAN_TIMEOUT);
420                         msg.obj = client;
421                         // Only one timeout message should exist at any time
422                         sendMessageDelayed(msg, mAdapterService.getScanTimeoutMillis());
423                         if (DBG) {
424                             Log.d(TAG,
425                                     "apply scan timeout (" + mAdapterService.getScanTimeoutMillis()
426                                             + ")" + "to scannerId " + client.scannerId);
427                         }
428                     }
429                 }
430             }
431             client.started = true;
432         }
433 
requiresScreenOn(ScanClient client)434         private boolean requiresScreenOn(ScanClient client) {
435             boolean isFiltered = (client.filters != null) && !client.filters.isEmpty();
436             return !mScanNative.isOpportunisticScanClient(client) && !isFiltered;
437         }
438 
requiresLocationOn(ScanClient client)439         private boolean requiresLocationOn(ScanClient client) {
440             boolean isFiltered = (client.filters != null) && !client.filters.isEmpty();
441             return !client.hasDisavowedLocation && !isFiltered;
442         }
443 
444         @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
handleStopScan(ScanClient client)445         void handleStopScan(ScanClient client) {
446             if (client == null) {
447                 return;
448             }
449             if (DBG) {
450                 Log.d(TAG, "handling stopping scan " + client);
451             }
452 
453             if (mSuspendedScanClients.contains(client)) {
454                 mSuspendedScanClients.remove(client);
455             }
456             removeMessages(MSG_REVERT_SCAN_MODE_UPGRADE, client);
457             removeMessages(MSG_SCAN_TIMEOUT, client);
458             if (mRegularScanClients.contains(client)) {
459                 mScanNative.stopRegularScan(client);
460 
461                 if (!mScanNative.isOpportunisticScanClient(client)) {
462                     mScanNative.configureRegularScanParams();
463                 }
464             } else {
465                 mScanNative.stopBatchScan(client);
466             }
467             if (client.appDied) {
468                 if (DBG) {
469                     Log.d(TAG, "app died, unregister scanner - " + client.scannerId);
470                 }
471                 mService.unregisterScanner(client.scannerId, mService.getAttributionSource());
472             }
473         }
474 
handleFlushBatchResults(ScanClient client)475         void handleFlushBatchResults(ScanClient client) {
476             if (!mBatchClients.contains(client)) {
477                 return;
478             }
479             mScanNative.flushBatchResults(client.scannerId);
480         }
481 
isBatchClient(ScanClient client)482         private boolean isBatchClient(ScanClient client) {
483             if (client == null || client.settings == null) {
484                 return false;
485             }
486             ScanSettings settings = client.settings;
487             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
488                     && settings.getReportDelayMillis() != 0;
489         }
490 
isScanSupported(ScanClient client)491         private boolean isScanSupported(ScanClient client) {
492             if (client == null || client.settings == null) {
493                 return true;
494             }
495             ScanSettings settings = client.settings;
496             if (isFilteringSupported()) {
497                 return true;
498             }
499             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
500                     && settings.getReportDelayMillis() == 0;
501         }
502 
handleScreenOff()503         void handleScreenOff() {
504             if (!mScreenOn) {
505                 return;
506             }
507             mScreenOn = false;
508             if (DBG) {
509                 Log.d(TAG, "handleScreenOff()");
510             }
511             handleSuspendScans();
512             updateRegularScanClientsScreenOff();
513         }
514 
515         @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN)
handleSuspendScans()516         void handleSuspendScans() {
517             for (ScanClient client : mRegularScanClients) {
518                 if ((requiresScreenOn(client) && !mScreenOn)
519                         || (requiresLocationOn(client) && !mLocationManager.isLocationEnabled())) {
520                     /*Suspend unfiltered scans*/
521                     if (client.stats != null) {
522                         client.stats.recordScanSuspend(client.scannerId);
523                     }
524                     if (DBG) {
525                         Log.d(TAG, "suspend scan " + client);
526                     }
527                     handleStopScan(client);
528                     mSuspendedScanClients.add(client);
529                 }
530             }
531         }
532 
updateRegularScanClientsScreenOff()533         private void updateRegularScanClientsScreenOff() {
534             boolean updatedScanParams = false;
535             for (ScanClient client : mRegularScanClients) {
536                 if (updateScanModeScreenOff(client)) {
537                     updatedScanParams = true;
538                     if (DBG) {
539                         Log.d(TAG, "Scan mode update during screen off" + client);
540                     }
541                 }
542             }
543             if (updatedScanParams) {
544                 mScanNative.configureRegularScanParams();
545             }
546         }
547 
updateScanModeScreenOff(ScanClient client)548         private boolean updateScanModeScreenOff(ScanClient client) {
549             if (mScanNative.isTimeoutScanClient(client)) {
550                 return false;
551             }
552             if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) {
553                 return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF);
554             }
555 
556             // The following codes are effectively only for services
557             // Apps are either already or will be soon handled by handleImportanceChange().
558             switch (client.scanModeApp) {
559                 case ScanSettings.SCAN_MODE_LOW_POWER:
560                     return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF);
561                 case ScanSettings.SCAN_MODE_BALANCED:
562                 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY:
563                     return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED);
564                 case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
565                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
566                 default:
567                     return false;
568             }
569         }
570 
571         /**
572          * Services and Apps are assumed to be in the foreground by default unless it changes to the
573          * background triggering onUidImportance().
574          */
isAppForeground(ScanClient client)575         private boolean isAppForeground(ScanClient client) {
576             return mIsUidForegroundMap.get(client.appUid, DEFAULT_UID_IS_FOREGROUND);
577         }
578 
updateScanModeBeforeStart(ScanClient client)579         private boolean updateScanModeBeforeStart(ScanClient client) {
580             if (upgradeScanModeBeforeStart(client)) {
581                 return true;
582             }
583             if (mScreenOn) {
584                 return updateScanModeScreenOn(client);
585             } else {
586                 return updateScanModeScreenOff(client);
587             }
588         }
589 
upgradeScanModeBeforeStart(ScanClient client)590         private boolean upgradeScanModeBeforeStart(ScanClient client) {
591             if (client.started || mAdapterService.getScanUpgradeDurationMillis() == 0) {
592                 return false;
593             }
594             if (client.stats == null || client.stats.hasRecentScan()) {
595                 return false;
596             }
597             if (!isAppForeground(client) || isBatchClient(client)) {
598                 return false;
599             }
600 
601             if (upgradeScanModeByOneLevel(client)) {
602                 Message msg = obtainMessage(MSG_REVERT_SCAN_MODE_UPGRADE);
603                 msg.obj = client;
604                 if (DBG) {
605                     Log.d(TAG, "scanMode is upgraded for " + client);
606                 }
607                 sendMessageDelayed(msg, mAdapterService.getScanUpgradeDurationMillis());
608                 return true;
609             }
610             return false;
611         }
612 
upgradeScanModeByOneLevel(ScanClient client)613         private boolean upgradeScanModeByOneLevel(ScanClient client) {
614             switch (client.scanModeApp) {
615                 case ScanSettings.SCAN_MODE_LOW_POWER:
616                     return client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED);
617                 case ScanSettings.SCAN_MODE_BALANCED:
618                 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY:
619                     return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
620                 case ScanSettings.SCAN_MODE_OPPORTUNISTIC:
621                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
622                 default:
623                     return false;
624             }
625         }
626 
revertScanModeUpgrade(ScanClient client)627         void revertScanModeUpgrade(ScanClient client) {
628             if (mPriorityMap.get(client.settings.getScanMode())
629                     <= mPriorityMap.get(client.scanModeApp)) {
630                 return;
631             }
632             if (client.updateScanMode(client.scanModeApp)) {
633                 if (DBG) {
634                     Log.d(TAG, "scanMode upgrade is reverted for " + client);
635                 }
636                 mScanNative.configureRegularScanParams();
637             }
638         }
639 
updateScanModeScreenOn(ScanClient client)640         private boolean updateScanModeScreenOn(ScanClient client) {
641             if (mScanNative.isTimeoutScanClient(client)) {
642                 return false;
643             }
644 
645             int newScanMode =  (isAppForeground(client)
646                     || mScanNative.isOpportunisticScanClient(client))
647                     ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND;
648             return client.updateScanMode(newScanMode);
649         }
650 
handleScreenOn()651         void handleScreenOn() {
652             if (mScreenOn) {
653                 return;
654             }
655             mScreenOn = true;
656             if (DBG) {
657                 Log.d(TAG, "handleScreenOn()");
658             }
659             handleResumeScans();
660             updateRegularScanClientsScreenOn();
661         }
662 
handleResumeScans()663         void handleResumeScans() {
664             for (ScanClient client : mSuspendedScanClients) {
665                 if ((!requiresScreenOn(client) || mScreenOn)
666                         && (!requiresLocationOn(client) || mLocationManager.isLocationEnabled())) {
667                     if (client.stats != null) {
668                         client.stats.recordScanResume(client.scannerId);
669                     }
670                     if (DBG) {
671                         Log.d(TAG, "resume scan " + client);
672                     }
673                     handleStartScan(client);
674                 }
675             }
676             mSuspendedScanClients.clear();
677         }
678 
updateRegularScanClientsScreenOn()679         private void updateRegularScanClientsScreenOn() {
680             boolean updatedScanParams = false;
681             for (ScanClient client : mRegularScanClients) {
682                 if (updateScanModeScreenOn(client)) {
683                     updatedScanParams = true;
684                 }
685             }
686             if (updatedScanParams) {
687                 mScanNative.configureRegularScanParams();
688             }
689         }
690     }
691 
692     /**
693      * Parameters for batch scans.
694      */
695     class BatchScanParams {
696         public int scanMode;
697         public int fullScanscannerId;
698         public int truncatedScanscannerId;
699 
BatchScanParams()700         BatchScanParams() {
701             scanMode = -1;
702             fullScanscannerId = -1;
703             truncatedScanscannerId = -1;
704         }
705 
706         @Override
equals(Object obj)707         public boolean equals(Object obj) {
708             if (this == obj) {
709                 return true;
710             }
711             if (obj == null || getClass() != obj.getClass()) {
712                 return false;
713             }
714             BatchScanParams other = (BatchScanParams) obj;
715             return scanMode == other.scanMode && fullScanscannerId == other.fullScanscannerId
716                     && truncatedScanscannerId == other.truncatedScanscannerId;
717 
718         }
719     }
720 
getCurrentUsedTrackingAdvertisement()721     public int getCurrentUsedTrackingAdvertisement() {
722         return mCurUsedTrackableAdvertisements;
723     }
724 
725     private class ScanNative {
726 
727         // Delivery mode defined in bt stack.
728         private static final int DELIVERY_MODE_IMMEDIATE = 0;
729         private static final int DELIVERY_MODE_ON_FOUND_LOST = 1;
730         private static final int DELIVERY_MODE_BATCH = 2;
731 
732         private static final int ONFOUND_SIGHTINGS_AGGRESSIVE = 1;
733         private static final int ONFOUND_SIGHTINGS_STICKY = 4;
734 
735         private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1;
736         private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2;
737         private static final int ALL_PASS_FILTER_SELECTION = 0;
738 
739         private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0;
740 
741 
742         /**
743          * Onfound/onlost for scan settings
744          */
745         private static final int MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR = (1);
746         private static final int MATCH_MODE_STICKY_TIMEOUT_FACTOR = (3);
747         private static final int ONLOST_FACTOR = 2;
748         private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500;
749 
750         // The logic is AND for each filter field.
751         private static final int LIST_LOGIC_TYPE = 0x1111111;
752         private static final int FILTER_LOGIC_TYPE = 1;
753         // Filter indices that are available to user. It's sad we need to maintain filter index.
754         private final Deque<Integer> mFilterIndexStack;
755         // Map of scannerId and Filter indices used by client.
756         private final Map<Integer, Deque<Integer>> mClientFilterIndexMap;
757         // Keep track of the clients that uses ALL_PASS filters.
758         private final Set<Integer> mAllPassRegularClients = new HashSet<>();
759         private final Set<Integer> mAllPassBatchClients = new HashSet<>();
760 
761         private AlarmManager mAlarmManager;
762         private PendingIntent mBatchScanIntervalIntent;
763 
ScanNative()764         ScanNative() {
765             mFilterIndexStack = new ArrayDeque<Integer>();
766             mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
767 
768             mAlarmManager = mService.getSystemService(AlarmManager.class);
769             Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
770             mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent,
771                     PendingIntent.FLAG_IMMUTABLE);
772             IntentFilter filter = new IntentFilter();
773             filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
774             mBatchAlarmReceiver = new BroadcastReceiver() {
775                 @Override
776                 public void onReceive(Context context, Intent intent) {
777                     Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime());
778                     String action = intent.getAction();
779 
780                     if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
781                         if (mBatchClients.isEmpty()) {
782                             return;
783                         }
784                         // Note this actually flushes all pending batch data.
785                         if (mBatchClients.iterator().hasNext()) {
786                             flushBatchScanResults(mBatchClients.iterator().next());
787                         }
788                     }
789                 }
790             };
791             mService.registerReceiver(mBatchAlarmReceiver, filter);
792             mBatchAlarmReceiverRegistered = true;
793         }
794 
resetCountDownLatch()795         private void resetCountDownLatch() {
796             mLatch = new CountDownLatch(1);
797         }
798 
799         // Returns true if mLatch reaches 0, false if timeout or interrupted.
waitForCallback()800         private boolean waitForCallback() {
801             try {
802                 return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
803             } catch (InterruptedException e) {
804                 return false;
805             }
806         }
807 
configureRegularScanParams()808         void configureRegularScanParams() {
809             if (DBG) {
810                 Log.d(TAG, "configureRegularScanParams() - queue=" + mRegularScanClients.size());
811             }
812             int curScanSetting = Integer.MIN_VALUE;
813             ScanClient client = getAggressiveClient(mRegularScanClients);
814             if (client != null) {
815                 curScanSetting = client.settings.getScanMode();
816             }
817 
818             if (curScanSetting != Integer.MIN_VALUE
819                     && curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
820                 if (curScanSetting != mLastConfiguredScanSetting) {
821                     int scanWindowMs = getScanWindowMillis(client.settings);
822                     int scanIntervalMs = getScanIntervalMillis(client.settings);
823 
824                     // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
825                     int scanWindow = Utils.millsToUnit(scanWindowMs);
826                     int scanInterval = Utils.millsToUnit(scanIntervalMs);
827                     gattClientScanNative(false);
828                     if (DBG) {
829                         Log.d(TAG, "Start gattClientScanNative with"
830                                 + " old scanMode " + mLastConfiguredScanSetting
831                                 + " new scanMode " + curScanSetting
832                                 + " ( in MS: " + scanIntervalMs + " / " + scanWindowMs
833                                 + ", in scan unit: " + scanInterval + " / " + scanWindow + " )"
834                                 + client);
835                     }
836                     gattSetScanParametersNative(client.scannerId, scanInterval, scanWindow);
837                     gattClientScanNative(true);
838                     mLastConfiguredScanSetting = curScanSetting;
839                 }
840             } else {
841                 mLastConfiguredScanSetting = curScanSetting;
842                 if (DBG) {
843                     Log.d(TAG, "configureRegularScanParams() - queue empty, scan stopped");
844                 }
845             }
846         }
847 
getAggressiveClient(Set<ScanClient> cList)848         ScanClient getAggressiveClient(Set<ScanClient> cList) {
849             ScanClient result = null;
850             int currentScanModePriority = Integer.MIN_VALUE;
851             for (ScanClient client : cList) {
852                 int priority = mPriorityMap.get(client.settings.getScanMode());
853                 if (priority > currentScanModePriority) {
854                     result = client;
855                     currentScanModePriority = priority;
856                 }
857             }
858             return result;
859         }
860 
startRegularScan(ScanClient client)861         void startRegularScan(ScanClient client) {
862             if (isFilteringSupported() && mFilterIndexStack.isEmpty()
863                     && mClientFilterIndexMap.isEmpty()) {
864                 initFilterIndexStack();
865             }
866             if (isFilteringSupported()) {
867                 configureScanFilters(client);
868             }
869             // Start scan native only for the first client.
870             if (numRegularScanClients() == 1
871                     && client.settings != null
872                     && client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
873                 if (DBG) {
874                     Log.d(TAG, "start gattClientScanNative from startRegularScan()");
875                 }
876                 gattClientScanNative(true);
877             }
878         }
879 
numRegularScanClients()880         private int numRegularScanClients() {
881             int num = 0;
882             for (ScanClient client : mRegularScanClients) {
883                 if (client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
884                     num++;
885                 }
886             }
887             return num;
888         }
889 
startBatchScan(ScanClient client)890         void startBatchScan(ScanClient client) {
891             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
892                 initFilterIndexStack();
893             }
894             configureScanFilters(client);
895             if (!isOpportunisticScanClient(client)) {
896                 // Reset batch scan. May need to stop the existing batch scan and update scan
897                 // params.
898                 resetBatchScan(client);
899             }
900         }
901 
isExemptFromScanDowngrade(ScanClient client)902         private boolean isExemptFromScanDowngrade(ScanClient client) {
903             return isOpportunisticScanClient(client) || isFirstMatchScanClient(client);
904         }
905 
isOpportunisticScanClient(ScanClient client)906         private boolean isOpportunisticScanClient(ScanClient client) {
907             return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
908         }
909 
isTimeoutScanClient(ScanClient client)910         private boolean isTimeoutScanClient(ScanClient client) {
911             return (client.stats != null) && client.stats.isScanTimeout(client.scannerId);
912         }
913 
isFirstMatchScanClient(ScanClient client)914         private boolean isFirstMatchScanClient(ScanClient client) {
915             return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
916                     != 0;
917         }
918 
resetBatchScan(ScanClient client)919         private void resetBatchScan(ScanClient client) {
920             int scannerId = client.scannerId;
921             BatchScanParams batchScanParams = getBatchScanParams();
922             // Stop batch if batch scan params changed and previous params is not null.
923             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
924                 if (DBG) {
925                     Log.d(TAG, "stopping BLe Batch");
926                 }
927                 resetCountDownLatch();
928                 gattClientStopBatchScanNative(scannerId);
929                 waitForCallback();
930                 // Clear pending results as it's illegal to config storage if there are still
931                 // pending results.
932                 flushBatchResults(scannerId);
933             }
934             // Start batch if batchScanParams changed and current params is not null.
935             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
936                 int notifyThreshold = 95;
937                 if (DBG) {
938                     Log.d(TAG, "Starting BLE batch scan");
939                 }
940                 int resultType = getResultType(batchScanParams);
941                 int fullScanPercent = getFullScanStoragePercent(resultType);
942                 resetCountDownLatch();
943                 if (DBG) {
944                     Log.d(TAG, "configuring batch scan storage, appIf " + client.scannerId);
945                 }
946                 gattClientConfigBatchScanStorageNative(client.scannerId, fullScanPercent,
947                         100 - fullScanPercent, notifyThreshold);
948                 waitForCallback();
949                 resetCountDownLatch();
950                 int scanInterval =
951                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
952                 int scanWindow =
953                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
954                 gattClientStartBatchScanNative(scannerId, resultType, scanInterval, scanWindow, 0,
955                         DISCARD_OLDEST_WHEN_BUFFER_FULL);
956                 waitForCallback();
957             }
958             mBatchScanParms = batchScanParams;
959             setBatchAlarm();
960         }
961 
getFullScanStoragePercent(int resultType)962         private int getFullScanStoragePercent(int resultType) {
963             switch (resultType) {
964                 case SCAN_RESULT_TYPE_FULL:
965                     return 100;
966                 case SCAN_RESULT_TYPE_TRUNCATED:
967                     return 0;
968                 case SCAN_RESULT_TYPE_BOTH:
969                     return 50;
970                 default:
971                     return 50;
972             }
973         }
974 
getBatchScanParams()975         private BatchScanParams getBatchScanParams() {
976             if (mBatchClients.isEmpty()) {
977                 return null;
978             }
979             BatchScanParams params = new BatchScanParams();
980             ScanClient winner = getAggressiveClient(mBatchClients);
981             if (winner != null) {
982                 params.scanMode = winner.settings.getScanMode();
983             }
984             // TODO: split full batch scan results and truncated batch scan results to different
985             // collections.
986             for (ScanClient client : mBatchClients) {
987                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
988                     params.fullScanscannerId = client.scannerId;
989                 } else {
990                     params.truncatedScanscannerId = client.scannerId;
991                 }
992             }
993             return params;
994         }
995 
996         // Batched scan doesn't require high duty cycle scan because scan result is reported
997         // infrequently anyway. To avoid redefining paramete sets, map to the low duty cycle
998         // parameter set as follows.
getBatchScanWindowMillis(int scanMode)999         private int getBatchScanWindowMillis(int scanMode) {
1000             ContentResolver resolver = mService.getContentResolver();
1001             switch (scanMode) {
1002                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
1003                     return Settings.Global.getInt(
1004                         resolver,
1005                         Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS,
1006                         SCAN_MODE_BALANCED_WINDOW_MS);
1007                 default:
1008                     return Settings.Global.getInt(
1009                         resolver,
1010                         Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
1011                         SCAN_MODE_LOW_POWER_WINDOW_MS);
1012             }
1013         }
1014 
getBatchScanIntervalMillis(int scanMode)1015         private int getBatchScanIntervalMillis(int scanMode) {
1016             ContentResolver resolver = mService.getContentResolver();
1017             switch (scanMode) {
1018                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
1019                     return Settings.Global.getInt(
1020                         resolver,
1021                         Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
1022                         SCAN_MODE_BALANCED_INTERVAL_MS);
1023                 default:
1024                     return Settings.Global.getInt(
1025                         resolver,
1026                         Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
1027                         SCAN_MODE_LOW_POWER_INTERVAL_MS);
1028             }
1029         }
1030 
1031         // Set the batch alarm to be triggered within a short window after batch interval. This
1032         // allows system to optimize wake up time while still allows a degree of precise control.
setBatchAlarm()1033         private void setBatchAlarm() {
1034             // Cancel any pending alarm just in case.
1035             mAlarmManager.cancel(mBatchScanIntervalIntent);
1036             if (mBatchClients.isEmpty()) {
1037                 return;
1038             }
1039             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
1040             // Allows the alarm to be triggered within
1041             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
1042             long windowLengthMillis = batchTriggerIntervalMillis / 10;
1043             long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
1044             mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, windowStartMillis,
1045                     windowLengthMillis, mBatchScanIntervalIntent);
1046         }
1047 
stopRegularScan(ScanClient client)1048         void stopRegularScan(ScanClient client) {
1049             // Remove scan filters and recycle filter indices.
1050             if (client == null) {
1051                 return;
1052             }
1053             int deliveryMode = getDeliveryMode(client);
1054             if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
1055                 for (ScanFilter filter : client.filters) {
1056                     int entriesToFree = getNumOfTrackingAdvertisements(client.settings);
1057                     if (!manageAllocationOfTrackingAdvertisement(entriesToFree, false)) {
1058                         Log.e(TAG, "Error freeing for onfound/onlost filter resources "
1059                                 + entriesToFree);
1060                         try {
1061                             mService.onScanManagerErrorCallback(client.scannerId,
1062                                     ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
1063                         } catch (RemoteException e) {
1064                             Log.e(TAG, "failed on onScanManagerCallback at freeing", e);
1065                         }
1066                     }
1067                 }
1068             }
1069             mRegularScanClients.remove(client);
1070             if (numRegularScanClients() == 0) {
1071                 if (DBG) {
1072                     Log.d(TAG, "stop gattClientScanNative");
1073                 }
1074                 gattClientScanNative(false);
1075             }
1076             removeScanFilters(client.scannerId);
1077         }
1078 
regularScanTimeout(ScanClient client)1079         void regularScanTimeout(ScanClient client) {
1080             if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) {
1081                 if (DBG) {
1082                     Log.d(TAG, "regularScanTimeout - client scan time was too long");
1083                 }
1084                 if (client.filters == null || client.filters.isEmpty()) {
1085                     Log.w(TAG,
1086                             "Moving unfiltered scan client to opportunistic scan (scannerId "
1087                                     + client.scannerId + ")");
1088                     setOpportunisticScanClient(client);
1089                     removeScanFilters(client.scannerId);
1090                     client.stats.setScanTimeout(client.scannerId);
1091                 } else {
1092                     Log.w(TAG,
1093                             "Moving filtered scan client to downgraded scan (scannerId "
1094                                     + client.scannerId + ")");
1095                     client.updateScanMode(ScanSettings.SCAN_MODE_LOW_POWER);
1096                     client.stats.setScanTimeout(client.scannerId);
1097                 }
1098             }
1099 
1100             // The scan should continue for background scans
1101             configureRegularScanParams();
1102             if (numRegularScanClients() == 0) {
1103                 if (DBG) {
1104                     Log.d(TAG, "stop gattClientScanNative");
1105                 }
1106                 gattClientScanNative(false);
1107             }
1108         }
1109 
setOpportunisticScanClient(ScanClient client)1110         void setOpportunisticScanClient(ScanClient client) {
1111             // TODO: Add constructor to ScanSettings.Builder
1112             // that can copy values from an existing ScanSettings object
1113             ScanSettings.Builder builder = new ScanSettings.Builder();
1114             ScanSettings settings = client.settings;
1115             builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC);
1116             builder.setCallbackType(settings.getCallbackType());
1117             builder.setScanResultType(settings.getScanResultType());
1118             builder.setReportDelay(settings.getReportDelayMillis());
1119             builder.setNumOfMatches(settings.getNumOfMatches());
1120             client.settings = builder.build();
1121         }
1122 
1123         // Find the regular scan client information.
getRegularScanClient(int scannerId)1124         ScanClient getRegularScanClient(int scannerId) {
1125             for (ScanClient client : mRegularScanClients) {
1126                 if (client.scannerId == scannerId) {
1127                     return client;
1128                 }
1129             }
1130             return null;
1131         }
1132 
getSuspendedScanClient(int scannerId)1133         ScanClient getSuspendedScanClient(int scannerId) {
1134             for (ScanClient client : mSuspendedScanClients) {
1135                 if (client.scannerId == scannerId) {
1136                     return client;
1137                 }
1138             }
1139             return null;
1140         }
1141 
stopBatchScan(ScanClient client)1142         void stopBatchScan(ScanClient client) {
1143             mBatchClients.remove(client);
1144             removeScanFilters(client.scannerId);
1145             if (!isOpportunisticScanClient(client)) {
1146                 resetBatchScan(client);
1147             }
1148         }
1149 
flushBatchResults(int scannerId)1150         void flushBatchResults(int scannerId) {
1151             if (DBG) {
1152                 Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId);
1153             }
1154             if (mBatchScanParms.fullScanscannerId != -1) {
1155                 resetCountDownLatch();
1156                 gattClientReadScanReportsNative(mBatchScanParms.fullScanscannerId,
1157                         SCAN_RESULT_TYPE_FULL);
1158                 waitForCallback();
1159             }
1160             if (mBatchScanParms.truncatedScanscannerId != -1) {
1161                 resetCountDownLatch();
1162                 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanscannerId,
1163                         SCAN_RESULT_TYPE_TRUNCATED);
1164                 waitForCallback();
1165             }
1166             setBatchAlarm();
1167         }
1168 
cleanup()1169         void cleanup() {
1170             mAlarmManager.cancel(mBatchScanIntervalIntent);
1171             // Protect against multiple calls of cleanup.
1172             if (mBatchAlarmReceiverRegistered) {
1173                 mService.unregisterReceiver(mBatchAlarmReceiver);
1174             }
1175             mBatchAlarmReceiverRegistered = false;
1176         }
1177 
getBatchTriggerIntervalMillis()1178         private long getBatchTriggerIntervalMillis() {
1179             long intervalMillis = Long.MAX_VALUE;
1180             for (ScanClient client : mBatchClients) {
1181                 if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
1182                     intervalMillis =
1183                             Math.min(intervalMillis, client.settings.getReportDelayMillis());
1184                 }
1185             }
1186             return intervalMillis;
1187         }
1188 
1189         // Add scan filters. The logic is:
1190         // If no offload filter can/needs to be set, set ALL_PASS filter.
1191         // Otherwise offload all filters to hardware and enable all filters.
configureScanFilters(ScanClient client)1192         private void configureScanFilters(ScanClient client) {
1193             int scannerId = client.scannerId;
1194             int deliveryMode = getDeliveryMode(client);
1195             int trackEntries = 0;
1196 
1197             // Do not add any filters set by opportunistic scan clients
1198             if (isOpportunisticScanClient(client)) {
1199                 return;
1200             }
1201 
1202             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
1203                 return;
1204             }
1205 
1206             resetCountDownLatch();
1207             gattClientScanFilterEnableNative(scannerId, true);
1208             waitForCallback();
1209 
1210             if (shouldUseAllPassFilter(client)) {
1211                 int filterIndex =
1212                         (deliveryMode == DELIVERY_MODE_BATCH) ? ALL_PASS_FILTER_INDEX_BATCH_SCAN
1213                                 : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
1214                 resetCountDownLatch();
1215                 // Don't allow Onfound/onlost with all pass
1216                 configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION, filterIndex,
1217                         0);
1218                 waitForCallback();
1219             } else {
1220                 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
1221                 for (ScanFilter filter : client.filters) {
1222                     ScanFilterQueue queue = new ScanFilterQueue();
1223                     queue.addScanFilter(filter);
1224                     int featureSelection = queue.getFeatureSelection();
1225                     int filterIndex = mFilterIndexStack.pop();
1226 
1227                     resetCountDownLatch();
1228                     gattClientScanFilterAddNative(scannerId, queue.toArray(), filterIndex);
1229                     waitForCallback();
1230 
1231                     resetCountDownLatch();
1232                     if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
1233                         trackEntries = getNumOfTrackingAdvertisements(client.settings);
1234                         if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) {
1235                             Log.e(TAG, "No hardware resources for onfound/onlost filter "
1236                                     + trackEntries);
1237                             try {
1238                                 mService.onScanManagerErrorCallback(scannerId,
1239                                         ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
1240                             } catch (RemoteException e) {
1241                                 Log.e(TAG, "failed on onScanManagerCallback", e);
1242                             }
1243                         }
1244                     }
1245                     configureFilterParamter(scannerId, client, featureSelection, filterIndex,
1246                             trackEntries);
1247                     waitForCallback();
1248                     clientFilterIndices.add(filterIndex);
1249                 }
1250                 mClientFilterIndexMap.put(scannerId, clientFilterIndices);
1251             }
1252         }
1253 
1254         // Check whether the filter should be added to controller.
1255         // Note only on ALL_PASS filter should be added.
shouldAddAllPassFilterToController(ScanClient client, int deliveryMode)1256         private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
1257             // Not an ALL_PASS client, need to add filter.
1258             if (!shouldUseAllPassFilter(client)) {
1259                 return true;
1260             }
1261 
1262             if (deliveryMode == DELIVERY_MODE_BATCH) {
1263                 mAllPassBatchClients.add(client.scannerId);
1264                 return mAllPassBatchClients.size() == 1;
1265             } else {
1266                 mAllPassRegularClients.add(client.scannerId);
1267                 return mAllPassRegularClients.size() == 1;
1268             }
1269         }
1270 
removeScanFilters(int scannerId)1271         private void removeScanFilters(int scannerId) {
1272             Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId);
1273             if (filterIndices != null) {
1274                 mFilterIndexStack.addAll(filterIndices);
1275                 for (Integer filterIndex : filterIndices) {
1276                     resetCountDownLatch();
1277                     gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
1278                     waitForCallback();
1279                 }
1280             }
1281             // Remove if ALL_PASS filters are used.
1282             removeFilterIfExisits(mAllPassRegularClients, scannerId,
1283                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
1284             removeFilterIfExisits(mAllPassBatchClients, scannerId,
1285                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
1286         }
1287 
removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex)1288         private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) {
1289             if (!clients.contains(scannerId)) {
1290                 return;
1291             }
1292             clients.remove(scannerId);
1293             // Remove ALL_PASS filter iff no app is using it.
1294             if (clients.isEmpty()) {
1295                 resetCountDownLatch();
1296                 gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
1297                 waitForCallback();
1298             }
1299         }
1300 
getBatchScanClient(int scannerId)1301         private ScanClient getBatchScanClient(int scannerId) {
1302             for (ScanClient client : mBatchClients) {
1303                 if (client.scannerId == scannerId) {
1304                     return client;
1305                 }
1306             }
1307             return null;
1308         }
1309 
1310         /**
1311          * Return batch scan result type value defined in bt stack.
1312          */
getResultType(BatchScanParams params)1313         private int getResultType(BatchScanParams params) {
1314             if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) {
1315                 return SCAN_RESULT_TYPE_BOTH;
1316             }
1317             if (params.truncatedScanscannerId != -1) {
1318                 return SCAN_RESULT_TYPE_TRUNCATED;
1319             }
1320             if (params.fullScanscannerId != -1) {
1321                 return SCAN_RESULT_TYPE_FULL;
1322             }
1323             return -1;
1324         }
1325 
1326         // Check if ALL_PASS filter should be used for the client.
shouldUseAllPassFilter(ScanClient client)1327         private boolean shouldUseAllPassFilter(ScanClient client) {
1328             if (client == null) {
1329                 return true;
1330             }
1331             if (client.filters == null || client.filters.isEmpty()) {
1332                 return true;
1333             }
1334             return client.filters.size() > mFilterIndexStack.size();
1335         }
1336 
initFilterIndexStack()1337         private void initFilterIndexStack() {
1338             int maxFiltersSupported =
1339                     AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
1340             // Start from index 4 as:
1341             // index 0 is reserved for ALL_PASS filter in Settings app.
1342             // index 1 is reserved for ALL_PASS filter for regular scan apps.
1343             // index 2 is reserved for ALL_PASS filter for batch scan apps.
1344             // index 3 is reserved for BAP/CAP Announcements
1345             for (int i = 4; i < maxFiltersSupported; ++i) {
1346                 mFilterIndexStack.add(i);
1347             }
1348         }
1349 
1350         // Configure filter parameters.
configureFilterParamter(int scannerId, ScanClient client, int featureSelection, int filterIndex, int numOfTrackingEntries)1351         private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection,
1352                 int filterIndex, int numOfTrackingEntries) {
1353             int deliveryMode = getDeliveryMode(client);
1354             int rssiThreshold = Byte.MIN_VALUE;
1355             ScanSettings settings = client.settings;
1356             int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true);
1357             int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false);
1358             int onFoundCount = getOnFoundOnLostSightings(settings);
1359             onLostTimeout = 10000;
1360             if (DBG) {
1361                 Log.d(TAG, "configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " "
1362                         + onFoundCount + " " + numOfTrackingEntries);
1363             }
1364             FilterParams filtValue =
1365                     new FilterParams(scannerId, filterIndex, featureSelection, LIST_LOGIC_TYPE,
1366                             FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
1367                             onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries);
1368             gattClientScanFilterParamAddNative(filtValue);
1369         }
1370 
1371         // Get delivery mode based on scan settings.
getDeliveryMode(ScanClient client)1372         private int getDeliveryMode(ScanClient client) {
1373             if (client == null) {
1374                 return DELIVERY_MODE_IMMEDIATE;
1375             }
1376             ScanSettings settings = client.settings;
1377             if (settings == null) {
1378                 return DELIVERY_MODE_IMMEDIATE;
1379             }
1380             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
1381                     || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
1382                 return DELIVERY_MODE_ON_FOUND_LOST;
1383             }
1384             return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
1385                     : DELIVERY_MODE_BATCH;
1386         }
1387 
getScanWindowMillis(ScanSettings settings)1388         private int getScanWindowMillis(ScanSettings settings) {
1389             ContentResolver resolver = mService.getContentResolver();
1390             if (settings == null) {
1391                 return Settings.Global.getInt(
1392                     resolver,
1393                     Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
1394                     SCAN_MODE_LOW_POWER_WINDOW_MS);
1395             }
1396 
1397             switch (settings.getScanMode()) {
1398                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
1399                     return Settings.Global.getInt(
1400                         resolver,
1401                         Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS,
1402                         SCAN_MODE_LOW_LATENCY_WINDOW_MS);
1403                 case ScanSettings.SCAN_MODE_BALANCED:
1404                 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY:
1405                     return Settings.Global.getInt(
1406                         resolver,
1407                         Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS,
1408                         SCAN_MODE_BALANCED_WINDOW_MS);
1409                 case ScanSettings.SCAN_MODE_LOW_POWER:
1410                     return Settings.Global.getInt(
1411                         resolver,
1412                         Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
1413                         SCAN_MODE_LOW_POWER_WINDOW_MS);
1414                 case ScanSettings.SCAN_MODE_SCREEN_OFF:
1415                     return mAdapterService.getScreenOffLowPowerWindowMillis();
1416                 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED:
1417                     return mAdapterService.getScreenOffBalancedWindowMillis();
1418                 default:
1419                     return Settings.Global.getInt(
1420                         resolver,
1421                         Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS,
1422                         SCAN_MODE_LOW_POWER_WINDOW_MS);
1423             }
1424         }
1425 
getScanIntervalMillis(ScanSettings settings)1426         private int getScanIntervalMillis(ScanSettings settings) {
1427             ContentResolver resolver = mService.getContentResolver();
1428             if (settings == null) {
1429                 return Settings.Global.getInt(
1430                     resolver,
1431                     Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
1432                     SCAN_MODE_LOW_POWER_INTERVAL_MS);
1433             }
1434             switch (settings.getScanMode()) {
1435                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
1436                     return Settings.Global.getInt(
1437                         resolver,
1438                         Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS,
1439                         SCAN_MODE_LOW_LATENCY_INTERVAL_MS);
1440                 case ScanSettings.SCAN_MODE_BALANCED:
1441                 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY:
1442                     return Settings.Global.getInt(
1443                         resolver,
1444                         Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS,
1445                         SCAN_MODE_BALANCED_INTERVAL_MS);
1446                 case ScanSettings.SCAN_MODE_LOW_POWER:
1447                     return Settings.Global.getInt(
1448                         resolver,
1449                         Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
1450                         SCAN_MODE_LOW_POWER_INTERVAL_MS);
1451                 case ScanSettings.SCAN_MODE_SCREEN_OFF:
1452                     return mAdapterService.getScreenOffLowPowerIntervalMillis();
1453                 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED:
1454                     return mAdapterService.getScreenOffBalancedIntervalMillis();
1455                 default:
1456                     return Settings.Global.getInt(
1457                         resolver,
1458                         Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS,
1459                         SCAN_MODE_LOW_POWER_INTERVAL_MS);
1460             }
1461         }
1462 
getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound)1463         private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) {
1464             int factor;
1465             int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS;
1466 
1467             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1468                 factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR;
1469             } else {
1470                 factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR;
1471             }
1472             if (!onFound) {
1473                 factor = factor * ONLOST_FACTOR;
1474             }
1475             return (timeout * factor);
1476         }
1477 
getOnFoundOnLostSightings(ScanSettings settings)1478         private int getOnFoundOnLostSightings(ScanSettings settings) {
1479             if (settings == null) {
1480                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
1481             }
1482             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1483                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
1484             } else {
1485                 return ONFOUND_SIGHTINGS_STICKY;
1486             }
1487         }
1488 
getNumOfTrackingAdvertisements(ScanSettings settings)1489         private int getNumOfTrackingAdvertisements(ScanSettings settings) {
1490             if (settings == null) {
1491                 return 0;
1492             }
1493             int val = 0;
1494             int maxTotalTrackableAdvertisements =
1495                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1496             // controller based onfound onlost resources are scarce commodity; the
1497             // assignment of filters to num of beacons to track is configurable based
1498             // on hw capabilities. Apps give an intent and allocation of onfound
1499             // resources or failure there of is done based on availibility - FCFS model
1500             switch (settings.getNumOfMatches()) {
1501                 case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT:
1502                     val = 1;
1503                     break;
1504                 case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT:
1505                     val = 2;
1506                     break;
1507                 case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT:
1508                     val = maxTotalTrackableAdvertisements / 2;
1509                     break;
1510                 default:
1511                     val = 1;
1512                     if (DBG) {
1513                         Log.d(TAG, "Invalid setting for getNumOfMatches() "
1514                                 + settings.getNumOfMatches());
1515                     }
1516             }
1517             return val;
1518         }
1519 
manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement, boolean allocate)1520         private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement,
1521                 boolean allocate) {
1522             int maxTotalTrackableAdvertisements =
1523                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1524             synchronized (mCurUsedTrackableAdvertisements) {
1525                 int availableEntries =
1526                         maxTotalTrackableAdvertisements - mCurUsedTrackableAdvertisements;
1527                 if (allocate) {
1528                     if (availableEntries >= numOfTrackableAdvertisement) {
1529                         mCurUsedTrackableAdvertisements += numOfTrackableAdvertisement;
1530                         return true;
1531                     } else {
1532                         return false;
1533                     }
1534                 } else {
1535                     if (numOfTrackableAdvertisement > mCurUsedTrackableAdvertisements) {
1536                         return false;
1537                     } else {
1538                         mCurUsedTrackableAdvertisements -= numOfTrackableAdvertisement;
1539                         return true;
1540                     }
1541                 }
1542             }
1543         }
1544 
1545 
1546         /************************** Regular scan related native methods **************************/
registerScannerNative(long appUuidLsb, long appUuidMsb)1547         private native void registerScannerNative(long appUuidLsb, long appUuidMsb);
1548 
unregisterScannerNative(int scannerId)1549         private native void unregisterScannerNative(int scannerId);
1550 
gattClientScanNative(boolean start)1551         private native void gattClientScanNative(boolean start);
1552 
gattSetScanParametersNative(int clientIf, int scanInterval, int scanWindow)1553         private native void gattSetScanParametersNative(int clientIf, int scanInterval,
1554                 int scanWindow);
1555 
1556         /************************** Filter related native methods ********************************/
gattClientScanFilterAddNative(int clientId, ScanFilterQueue.Entry[] entries, int filterIndex)1557         private native void gattClientScanFilterAddNative(int clientId,
1558                 ScanFilterQueue.Entry[] entries, int filterIndex);
1559 
gattClientScanFilterParamAddNative(FilterParams filtValue)1560         private native void gattClientScanFilterParamAddNative(FilterParams filtValue);
1561 
1562         // Note this effectively remove scan filters for ALL clients.
gattClientScanFilterParamClearAllNative(int clientIf)1563         private native void gattClientScanFilterParamClearAllNative(int clientIf);
1564 
gattClientScanFilterParamDeleteNative(int clientIf, int filtIndex)1565         private native void gattClientScanFilterParamDeleteNative(int clientIf, int filtIndex);
1566 
gattClientScanFilterClearNative(int clientIf, int filterIndex)1567         private native void gattClientScanFilterClearNative(int clientIf, int filterIndex);
1568 
gattClientScanFilterEnableNative(int clientIf, boolean enable)1569         private native void gattClientScanFilterEnableNative(int clientIf, boolean enable);
1570 
1571         /************************** Batch related native methods *********************************/
gattClientConfigBatchScanStorageNative(int clientIf, int maxFullReportsPercent, int maxTruncatedReportsPercent, int notifyThresholdPercent)1572         private native void gattClientConfigBatchScanStorageNative(int clientIf,
1573                 int maxFullReportsPercent, int maxTruncatedReportsPercent,
1574                 int notifyThresholdPercent);
1575 
gattClientStartBatchScanNative(int clientIf, int scanMode, int scanIntervalUnit, int scanWindowUnit, int addressType, int discardRule)1576         private native void gattClientStartBatchScanNative(int clientIf, int scanMode,
1577                 int scanIntervalUnit, int scanWindowUnit, int addressType, int discardRule);
1578 
gattClientStopBatchScanNative(int clientIf)1579         private native void gattClientStopBatchScanNative(int clientIf);
1580 
gattClientReadScanReportsNative(int clientIf, int scanType)1581         private native void gattClientReadScanReportsNative(int clientIf, int scanType);
1582     }
1583 
1584     @VisibleForTesting
getClientHandler()1585     ClientHandler getClientHandler() {
1586         return mHandler;
1587     }
1588 
isScreenOn()1589     private boolean isScreenOn() {
1590         Display[] displays = mDm.getDisplays();
1591 
1592         if (displays == null) {
1593             return false;
1594         }
1595 
1596         for (Display display : displays) {
1597             if (display.getState() == Display.STATE_ON) {
1598                 return true;
1599             }
1600         }
1601 
1602         return false;
1603     }
1604 
1605     private final DisplayManager.DisplayListener mDisplayListener =
1606             new DisplayManager.DisplayListener() {
1607                 @Override
1608                 public void onDisplayAdded(int displayId) {}
1609 
1610                 @Override
1611                 public void onDisplayRemoved(int displayId) {}
1612 
1613                 @Override
1614                 public void onDisplayChanged(int displayId) {
1615                     if (isScreenOn()) {
1616                         sendMessage(MSG_SCREEN_ON, null);
1617                     } else {
1618                         sendMessage(MSG_SCREEN_OFF, null);
1619                     }
1620                 }
1621             };
1622 
1623     private ActivityManager.OnUidImportanceListener mUidImportanceListener =
1624             new ActivityManager.OnUidImportanceListener() {
1625                 @Override
1626                 public void onUidImportance(final int uid, final int importance) {
1627                     if (mService.mScannerMap.getAppScanStatsByUid(uid) != null) {
1628                         Message message = new Message();
1629                         message.what = MSG_IMPORTANCE_CHANGE;
1630                         message.obj = new UidImportance(uid, importance);
1631                         mHandler.sendMessage(message);
1632                     }
1633                 }
1634             };
1635 
1636     private BroadcastReceiver mLocationReceiver =
1637             new BroadcastReceiver() {
1638                 @Override
1639                 public void onReceive(Context context, Intent intent) {
1640                     String action = intent.getAction();
1641                     if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
1642                         final boolean locationEnabled = mLocationManager.isLocationEnabled();
1643                         if (locationEnabled) {
1644                             sendMessage(MSG_RESUME_SCANS, null);
1645                         } else {
1646                             sendMessage(MSG_SUSPEND_SCANS, null);
1647                         }
1648                     }
1649                 }
1650             };
1651 
handleImportanceChange(UidImportance imp)1652     private void handleImportanceChange(UidImportance imp) {
1653         if (imp == null) {
1654             return;
1655         }
1656         int uid = imp.uid;
1657         int importance = imp.importance;
1658         boolean updatedScanParams = false;
1659         boolean isForeground = importance <= ActivityManager.RunningAppProcessInfo
1660                 .IMPORTANCE_FOREGROUND_SERVICE;
1661 
1662         if (mIsUidForegroundMap.size() < MAX_IS_UID_FOREGROUND_MAP_SIZE) {
1663             mIsUidForegroundMap.put(uid, isForeground);
1664         }
1665 
1666         for (ScanClient client : mRegularScanClients) {
1667             if (client.appUid != uid || mScanNative.isTimeoutScanClient(client)) {
1668                 continue;
1669             }
1670             if (isForeground) {
1671                 if (client.updateScanMode(client.scanModeApp)) {
1672                     updatedScanParams = true;
1673                 }
1674             } else {
1675                 // Skip scan mode update in any of following cases
1676                 //   1. screen is already off which triggers handleScreenOff()
1677                 //   2. opportunistics scan
1678                 if (mScreenOn && !mScanNative.isOpportunisticScanClient(client)
1679                         && client.updateScanMode(SCAN_MODE_APP_IN_BACKGROUND)) {
1680                     updatedScanParams = true;
1681                 }
1682             }
1683             if (DBG) {
1684                 Log.d(TAG, "uid " + uid + " isForeground " + isForeground
1685                         + " scanMode " + client.settings.getScanMode());
1686             }
1687         }
1688 
1689         if (updatedScanParams) {
1690             mScanNative.configureRegularScanParams();
1691         }
1692     }
1693 }
1694