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