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