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