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