• 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.AlarmManager;
20 import android.app.PendingIntent;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.le.ScanFilter;
23 import android.bluetooth.le.ScanSettings;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.os.Handler;
29 import android.os.HandlerThread;
30 import android.os.Looper;
31 import android.os.Message;
32 import android.os.SystemClock;
33 import android.util.Log;
34 
35 import com.android.bluetooth.Utils;
36 import com.android.bluetooth.btservice.AdapterService;
37 
38 import java.util.ArrayDeque;
39 import java.util.Deque;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.concurrent.CountDownLatch;
45 import java.util.concurrent.TimeUnit;
46 
47 /**
48  * Class that handles Bluetooth LE scan related operations.
49  *
50  * @hide
51  */
52 public class ScanManager {
53     private static final boolean DBG = GattServiceConfig.DBG;
54     private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager";
55 
56     // Result type defined in bt stack. Need to be accessed by GattService.
57     static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
58     static final int SCAN_RESULT_TYPE_FULL = 2;
59     static final int SCAN_RESULT_TYPE_BOTH = 3;
60 
61     // Internal messages for handling BLE scan operations.
62     private static final int MSG_START_BLE_SCAN = 0;
63     private static final int MSG_STOP_BLE_SCAN = 1;
64     private static final int MSG_FLUSH_BATCH_RESULTS = 2;
65 
66     private static final String ACTION_REFRESH_BATCHED_SCAN =
67             "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";
68 
69     // Timeout for each controller operation.
70     private static final int OPERATION_TIME_OUT_MILLIS = 500;
71 
72     private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
73     // Scan parameters for batch scan.
74     private BatchScanParams mBatchScanParms;
75 
76     private GattService mService;
77     private BroadcastReceiver mBatchAlarmReceiver;
78     private boolean mBatchAlarmReceiverRegistered;
79     private ScanNative mScanNative;
80     private ClientHandler mHandler;
81 
82     private Set<ScanClient> mRegularScanClients;
83     private Set<ScanClient> mBatchClients;
84 
85     private CountDownLatch mLatch;
86 
ScanManager(GattService service)87     ScanManager(GattService service) {
88         mRegularScanClients = new HashSet<ScanClient>();
89         mBatchClients = new HashSet<ScanClient>();
90         mService = service;
91         mScanNative = new ScanNative();
92     }
93 
start()94     void start() {
95         HandlerThread thread = new HandlerThread("BluetoothScanManager");
96         thread.start();
97         mHandler = new ClientHandler(thread.getLooper());
98     }
99 
cleanup()100     void cleanup() {
101         mRegularScanClients.clear();
102         mBatchClients.clear();
103         mScanNative.cleanup();
104     }
105 
106     /**
107      * Returns the regular scan queue.
108      */
getRegularScanQueue()109     Set<ScanClient> getRegularScanQueue() {
110         return mRegularScanClients;
111     }
112 
113     /**
114      * Returns batch scan queue.
115      */
getBatchScanQueue()116     Set<ScanClient> getBatchScanQueue() {
117         return mBatchClients;
118     }
119 
120     /**
121      * Returns a set of full batch scan clients.
122      */
getFullBatchScanQueue()123     Set<ScanClient> getFullBatchScanQueue() {
124         // TODO: split full batch scan clients and truncated batch clients so we don't need to
125         // construct this every time.
126         Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
127         for (ScanClient client : mBatchClients) {
128             if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
129                 fullBatchClients.add(client);
130             }
131         }
132         return fullBatchClients;
133     }
134 
startScan(ScanClient client)135     void startScan(ScanClient client) {
136         sendMessage(MSG_START_BLE_SCAN, client);
137     }
138 
stopScan(ScanClient client)139     void stopScan(ScanClient client) {
140         sendMessage(MSG_STOP_BLE_SCAN, client);
141     }
142 
flushBatchScanResults(ScanClient client)143     void flushBatchScanResults(ScanClient client) {
144         sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
145     }
146 
callbackDone(int clientIf, int status)147     void callbackDone(int clientIf, int status) {
148         logd("callback done for clientIf - " + clientIf + " status - " + status);
149         if (status == 0) {
150             mLatch.countDown();
151         }
152         // TODO: add a callback for scan failure.
153     }
154 
sendMessage(int what, ScanClient client)155     private void sendMessage(int what, ScanClient client) {
156         Message message = new Message();
157         message.what = what;
158         message.obj = client;
159         mHandler.sendMessage(message);
160     }
161 
isFilteringSupported()162     private boolean isFilteringSupported() {
163         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
164         return adapter.isOffloadedFilteringSupported();
165     }
166 
167     // Handler class that handles BLE scan operations.
168     private class ClientHandler extends Handler {
169 
ClientHandler(Looper looper)170         ClientHandler(Looper looper) {
171             super(looper);
172         }
173 
174         @Override
handleMessage(Message msg)175         public void handleMessage(Message msg) {
176             ScanClient client = (ScanClient) msg.obj;
177             switch (msg.what) {
178                 case MSG_START_BLE_SCAN:
179                     handleStartScan(client);
180                     break;
181                 case MSG_STOP_BLE_SCAN:
182                     handleStopScan(client);
183                     break;
184                 case MSG_FLUSH_BATCH_RESULTS:
185                     handleFlushBatchResults(client);
186                     break;
187                 default:
188                     // Shouldn't happen.
189                     Log.e(TAG, "received an unkown message : " + msg.what);
190             }
191         }
192 
handleStartScan(ScanClient client)193         void handleStartScan(ScanClient client) {
194             Utils.enforceAdminPermission(mService);
195             logd("handling starting scan");
196 
197             if (!isScanSupported(client)) {
198                 Log.e(TAG, "Scan settings not supported");
199                 return;
200             }
201 
202             if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) {
203                 Log.e(TAG, "Scan already started");
204                 return;
205             }
206             // Begin scan operations.
207             if (isBatchClient(client)) {
208                 mBatchClients.add(client);
209                 mScanNative.startBatchScan(client);
210             } else {
211                 mRegularScanClients.add(client);
212                 mScanNative.startRegularScan(client);
213                 mScanNative.configureRegularScanParams();
214             }
215         }
216 
handleStopScan(ScanClient client)217         void handleStopScan(ScanClient client) {
218             Utils.enforceAdminPermission(mService);
219             if (client == null) return;
220             if (mRegularScanClients.contains(client)) {
221                 mScanNative.stopRegularScan(client);
222                 mScanNative.configureRegularScanParams();
223             } else {
224                 mScanNative.stopBatchScan(client);
225             }
226             if (client.appDied) {
227                 logd("app died, unregister client - " + client.clientIf);
228                 mService.unregisterClient(client.clientIf);
229             }
230         }
231 
handleFlushBatchResults(ScanClient client)232         void handleFlushBatchResults(ScanClient client) {
233             Utils.enforceAdminPermission(mService);
234             if (!mBatchClients.contains(client)) {
235                 return;
236             }
237             mScanNative.flushBatchResults(client.clientIf);
238         }
239 
isBatchClient(ScanClient client)240         private boolean isBatchClient(ScanClient client) {
241             if (client == null || client.settings == null) {
242                 return false;
243             }
244             ScanSettings settings = client.settings;
245             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
246                     settings.getReportDelayMillis() != 0;
247         }
248 
isScanSupported(ScanClient client)249         private boolean isScanSupported(ScanClient client) {
250             if (client == null || client.settings == null) {
251                 return true;
252             }
253             ScanSettings settings = client.settings;
254             if (isFilteringSupported()) {
255                 return true;
256             }
257             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
258                     settings.getReportDelayMillis() == 0;
259         }
260     }
261 
262     /**
263      * Parameters for batch scans.
264      */
265     class BatchScanParams {
266         int scanMode;
267         int fullScanClientIf;
268         int truncatedScanClientIf;
269 
BatchScanParams()270         BatchScanParams() {
271             scanMode = -1;
272             fullScanClientIf = -1;
273             truncatedScanClientIf = -1;
274         }
275 
276         @Override
equals(Object obj)277         public boolean equals(Object obj) {
278             if (this == obj) {
279                 return true;
280             }
281             if (obj == null || getClass() != obj.getClass()) {
282                 return false;
283             }
284             BatchScanParams other = (BatchScanParams) obj;
285             return scanMode == other.scanMode && fullScanClientIf == other.fullScanClientIf
286                     && truncatedScanClientIf == other.truncatedScanClientIf;
287 
288         }
289     }
290 
291     private class ScanNative {
292 
293         // Delivery mode defined in bt stack.
294         private static final int DELIVERY_MODE_IMMEDIATE = 0;
295         private static final int DELIVERY_MODE_ON_FOUND_LOST = 1;
296         private static final int DELIVERY_MODE_BATCH = 2;
297 
298         private static final int DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS = 1000;
299         private static final int ONFOUND_SIGHTINGS = 2;
300 
301         private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1;
302         private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2;
303         private static final int ALL_PASS_FILTER_SELECTION = 0;
304 
305         private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0;
306 
307         /**
308          * Scan params corresponding to regular scan setting
309          */
310         private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 500;
311         private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 5000;
312         private static final int SCAN_MODE_BALANCED_WINDOW_MS = 2000;
313         private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 5000;
314         private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 5000;
315         private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 5000;
316 
317         /**
318          * Scan params corresponding to batch scan setting
319          */
320         private static final int SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS = 1500;
321         private static final int SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS = 150000;
322         private static final int SCAN_MODE_BATCH_BALANCED_WINDOW_MS = 1500;
323         private static final int SCAN_MODE_BATCH_BALANCED_INTERVAL_MS = 15000;
324         private static final int SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS = 1500;
325         private static final int SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS = 5000;
326 
327         // The logic is AND for each filter field.
328         private static final int LIST_LOGIC_TYPE = 0x1111111;
329         private static final int FILTER_LOGIC_TYPE = 1;
330         // Filter indices that are available to user. It's sad we need to maintain filter index.
331         private final Deque<Integer> mFilterIndexStack;
332         // Map of clientIf and Filter indices used by client.
333         private final Map<Integer, Deque<Integer>> mClientFilterIndexMap;
334         // Keep track of the clients that uses ALL_PASS filters.
335         private final Set<Integer> mAllPassRegularClients = new HashSet<>();
336         private final Set<Integer> mAllPassBatchClients = new HashSet<>();
337 
338         private AlarmManager mAlarmManager;
339         private PendingIntent mBatchScanIntervalIntent;
340 
ScanNative()341         ScanNative() {
342             mFilterIndexStack = new ArrayDeque<Integer>();
343             mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
344 
345             mAlarmManager = (AlarmManager) mService.getSystemService(Context.ALARM_SERVICE);
346             Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
347             mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent, 0);
348             IntentFilter filter = new IntentFilter();
349             filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
350             mBatchAlarmReceiver = new BroadcastReceiver() {
351                     @Override
352                 public void onReceive(Context context, Intent intent) {
353                     Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime());
354                     String action = intent.getAction();
355 
356                     if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
357                         if (mBatchClients.isEmpty()) {
358                             return;
359                         }
360                         // Note this actually flushes all pending batch data.
361                         flushBatchScanResults(mBatchClients.iterator().next());
362                     }
363                 }
364             };
365             mService.registerReceiver(mBatchAlarmReceiver, filter);
366             mBatchAlarmReceiverRegistered = true;
367         }
368 
resetCountDownLatch()369         private void resetCountDownLatch() {
370             mLatch = new CountDownLatch(1);
371         }
372 
373         // Returns true if mLatch reaches 0, false if timeout or interrupted.
waitForCallback()374         private boolean waitForCallback() {
375             try {
376                 return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
377             } catch (InterruptedException e) {
378                 return false;
379             }
380         }
381 
configureRegularScanParams()382         void configureRegularScanParams() {
383             logd("configureRegularScanParams() - queue=" + mRegularScanClients.size());
384             int curScanSetting = Integer.MIN_VALUE;
385             ScanClient client = getAggressiveClient(mRegularScanClients);
386             if (client != null) {
387                 curScanSetting = client.settings.getScanMode();
388             }
389 
390             logd("configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
391                     " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting);
392 
393             if (curScanSetting != Integer.MIN_VALUE) {
394                 if (curScanSetting != mLastConfiguredScanSetting) {
395                     int scanWindow, scanInterval;
396                     switch (curScanSetting) {
397                         case ScanSettings.SCAN_MODE_LOW_POWER:
398                             scanWindow = SCAN_MODE_LOW_POWER_WINDOW_MS;
399                             scanInterval = SCAN_MODE_LOW_POWER_INTERVAL_MS;
400                             break;
401                         case ScanSettings.SCAN_MODE_BALANCED:
402                             scanWindow = SCAN_MODE_BALANCED_WINDOW_MS;
403                             scanInterval = SCAN_MODE_BALANCED_INTERVAL_MS;
404                             break;
405                         case ScanSettings.SCAN_MODE_LOW_LATENCY:
406                             scanWindow = SCAN_MODE_LOW_LATENCY_WINDOW_MS;
407                             scanInterval = SCAN_MODE_LOW_LATENCY_INTERVAL_MS;
408                             break;
409                         default:
410                             Log.e(TAG, "Invalid value for curScanSetting " + curScanSetting);
411                             scanWindow = SCAN_MODE_LOW_POWER_WINDOW_MS;
412                             scanInterval = SCAN_MODE_LOW_POWER_INTERVAL_MS;
413                             break;
414                     }
415                     // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
416                     scanWindow = Utils.millsToUnit(scanWindow);
417                     scanInterval = Utils.millsToUnit(scanInterval);
418                     gattClientScanNative(false);
419                     gattSetScanParametersNative(scanInterval, scanWindow);
420                     gattClientScanNative(true);
421                     mLastConfiguredScanSetting = curScanSetting;
422                 }
423             } else {
424                 mLastConfiguredScanSetting = curScanSetting;
425                 logd("configureRegularScanParams() - queue emtpy, scan stopped");
426             }
427         }
428 
getAggressiveClient(Set<ScanClient> cList)429         ScanClient getAggressiveClient(Set<ScanClient> cList) {
430             ScanClient result = null;
431             int curScanSetting = Integer.MIN_VALUE;
432             for (ScanClient client : cList) {
433                 // ScanClient scan settings are assumed to be monotonically increasing in value for
434                 // more power hungry(higher duty cycle) operation.
435                 if (client.settings.getScanMode() > curScanSetting) {
436                     result = client;
437                     curScanSetting = client.settings.getScanMode();
438                 }
439             }
440             return result;
441         }
442 
startRegularScan(ScanClient client)443         void startRegularScan(ScanClient client) {
444             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
445                 initFilterIndexStack();
446             }
447             if (isFilteringSupported()) {
448                 configureScanFilters(client);
449             }
450             // Start scan native only for the first client.
451             if (mRegularScanClients.size() == 1) {
452                 gattClientScanNative(true);
453             }
454         }
455 
startBatchScan(ScanClient client)456         void startBatchScan(ScanClient client) {
457             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
458                 initFilterIndexStack();
459             }
460             configureScanFilters(client);
461             // Reset batch scan. May need to stop the existing batch scan and update scan params.
462             resetBatchScan(client);
463         }
464 
resetBatchScan(ScanClient client)465         private void resetBatchScan(ScanClient client) {
466             int clientIf = client.clientIf;
467             BatchScanParams batchScanParams = getBatchScanParams();
468             // Stop batch if batch scan params changed and previous params is not null.
469             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
470                 logd("stopping BLe Batch");
471                 resetCountDownLatch();
472                 gattClientStopBatchScanNative(clientIf);
473                 waitForCallback();
474                 // Clear pending results as it's illegal to config storage if there are still
475                 // pending results.
476                 flushBatchResults(clientIf);
477             }
478             // Start batch if batchScanParams changed and current params is not null.
479             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
480                 int notifyThreshold = 95;
481                 logd("Starting BLE batch scan");
482                 int resultType = getResultType(batchScanParams);
483                 int fullScanPercent = getFullScanStoragePercent(resultType);
484                 resetCountDownLatch();
485                 logd("configuring batch scan storage, appIf " + client.clientIf);
486                 gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
487                         100 - fullScanPercent, notifyThreshold);
488                 waitForCallback();
489                 resetCountDownLatch();
490                 int scanInterval =
491                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
492                 int scanWindow =
493                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
494                 gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
495                         scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
496                 waitForCallback();
497             }
498             mBatchScanParms = batchScanParams;
499             setBatchAlarm();
500         }
501 
getFullScanStoragePercent(int resultType)502         private int getFullScanStoragePercent(int resultType) {
503             switch (resultType) {
504                 case SCAN_RESULT_TYPE_FULL:
505                     return 100;
506                 case SCAN_RESULT_TYPE_TRUNCATED:
507                     return 0;
508                 case SCAN_RESULT_TYPE_BOTH:
509                     return 50;
510                 default:
511                     return 50;
512             }
513         }
514 
getBatchScanParams()515         private BatchScanParams getBatchScanParams() {
516             if (mBatchClients.isEmpty()) {
517                 return null;
518             }
519             BatchScanParams params = new BatchScanParams();
520             // TODO: split full batch scan results and truncated batch scan results to different
521             // collections.
522             for (ScanClient client : mBatchClients) {
523                 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
524                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
525                     params.fullScanClientIf = client.clientIf;
526                 } else {
527                     params.truncatedScanClientIf = client.clientIf;
528                 }
529             }
530             return params;
531         }
532 
getBatchScanWindowMillis(int scanMode)533         private int getBatchScanWindowMillis(int scanMode) {
534             switch (scanMode) {
535                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
536                     return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
537                 case ScanSettings.SCAN_MODE_BALANCED:
538                     return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
539                 case ScanSettings.SCAN_MODE_LOW_POWER:
540                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
541                 default:
542                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
543             }
544         }
545 
getBatchScanIntervalMillis(int scanMode)546         private int getBatchScanIntervalMillis(int scanMode) {
547             switch (scanMode) {
548                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
549                     return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
550                 case ScanSettings.SCAN_MODE_BALANCED:
551                     return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
552                 case ScanSettings.SCAN_MODE_LOW_POWER:
553                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
554                 default:
555                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
556             }
557         }
558 
559         // Set the batch alarm to be triggered within a short window after batch interval. This
560         // allows system to optimize wake up time while still allows a degree of precise control.
setBatchAlarm()561         private void setBatchAlarm() {
562             // Cancel any pending alarm just in case.
563             mAlarmManager.cancel(mBatchScanIntervalIntent);
564             if (mBatchClients.isEmpty()) {
565                 return;
566             }
567             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
568             // Allows the alarm to be triggered within
569             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
570             long windowLengthMillis = batchTriggerIntervalMillis / 10;
571             long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
572             mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
573                     windowStartMillis, windowLengthMillis,
574                     mBatchScanIntervalIntent);
575         }
576 
stopRegularScan(ScanClient client)577         void stopRegularScan(ScanClient client) {
578             // Remove scan filters and recycle filter indices.
579             removeScanFilters(client.clientIf);
580             mRegularScanClients.remove(client);
581             if (mRegularScanClients.isEmpty()) {
582                 logd("stop scan");
583                 gattClientScanNative(false);
584             }
585         }
586 
stopBatchScan(ScanClient client)587         void stopBatchScan(ScanClient client) {
588             mBatchClients.remove(client);
589             removeScanFilters(client.clientIf);
590             resetBatchScan(client);
591         }
592 
flushBatchResults(int clientIf)593         void flushBatchResults(int clientIf) {
594             logd("flushPendingBatchResults - clientIf = " + clientIf);
595             if (mBatchScanParms.fullScanClientIf != -1) {
596                 resetCountDownLatch();
597                 gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
598                         SCAN_RESULT_TYPE_FULL);
599                 waitForCallback();
600             }
601             if (mBatchScanParms.truncatedScanClientIf != -1) {
602                 resetCountDownLatch();
603                 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
604                         SCAN_RESULT_TYPE_TRUNCATED);
605                 waitForCallback();
606             }
607             setBatchAlarm();
608         }
609 
cleanup()610         void cleanup() {
611             mAlarmManager.cancel(mBatchScanIntervalIntent);
612             // Protect against multiple calls of cleanup.
613             if (mBatchAlarmReceiverRegistered) {
614                 mService.unregisterReceiver(mBatchAlarmReceiver);
615             }
616             mBatchAlarmReceiverRegistered = false;
617         }
618 
getBatchTriggerIntervalMillis()619         private long getBatchTriggerIntervalMillis() {
620             long intervalMillis = Long.MAX_VALUE;
621             for (ScanClient client : mBatchClients) {
622                 if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
623                     intervalMillis = Math.min(intervalMillis,
624                             client.settings.getReportDelayMillis());
625                 }
626             }
627             return intervalMillis;
628         }
629 
630         // Add scan filters. The logic is:
631         // If no offload filter can/needs to be set, set ALL_PASS filter.
632         // Otherwise offload all filters to hardware and enable all filters.
configureScanFilters(ScanClient client)633         private void configureScanFilters(ScanClient client) {
634             int clientIf = client.clientIf;
635             int deliveryMode = getDeliveryMode(client);
636             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
637                 return;
638             }
639 
640             resetCountDownLatch();
641             gattClientScanFilterEnableNative(clientIf, true);
642             waitForCallback();
643 
644             if (shouldUseAllPassFilter(client)) {
645                 int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
646                         ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
647                 resetCountDownLatch();
648                 configureFilterParamter(clientIf, client, ALL_PASS_FILTER_SELECTION, filterIndex);
649                 waitForCallback();
650             } else {
651                 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
652                 for (ScanFilter filter : client.filters) {
653                     ScanFilterQueue queue = new ScanFilterQueue();
654                     queue.addScanFilter(filter);
655                     int featureSelection = queue.getFeatureSelection();
656                     int filterIndex = mFilterIndexStack.pop();
657                     while (!queue.isEmpty()) {
658                         resetCountDownLatch();
659                         addFilterToController(clientIf, queue.pop(), filterIndex);
660                         waitForCallback();
661                     }
662                     resetCountDownLatch();
663                     configureFilterParamter(clientIf, client, featureSelection, filterIndex);
664                     waitForCallback();
665                     clientFilterIndices.add(filterIndex);
666                 }
667                 mClientFilterIndexMap.put(clientIf, clientFilterIndices);
668             }
669         }
670 
671         // Check whether the filter should be added to controller.
672         // Note only on ALL_PASS filter should be added.
shouldAddAllPassFilterToController(ScanClient client, int deliveryMode)673         private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
674             // Not an ALL_PASS client, need to add filter.
675             if (!shouldUseAllPassFilter(client)) {
676                 return true;
677             }
678 
679             if (deliveryMode == DELIVERY_MODE_BATCH) {
680                 mAllPassBatchClients.add(client.clientIf);
681                 return mAllPassBatchClients.size() == 1;
682             } else {
683                 mAllPassRegularClients.add(client.clientIf);
684                 return mAllPassRegularClients.size() == 1;
685             }
686         }
687 
removeScanFilters(int clientIf)688         private void removeScanFilters(int clientIf) {
689             Deque<Integer> filterIndices = mClientFilterIndexMap.remove(clientIf);
690             if (filterIndices != null) {
691                 mFilterIndexStack.addAll(filterIndices);
692                 for (Integer filterIndex : filterIndices) {
693                     resetCountDownLatch();
694                     gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
695                     waitForCallback();
696                 }
697             }
698             // Remove if ALL_PASS filters are used.
699             removeFilterIfExisits(mAllPassRegularClients, clientIf,
700                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
701             removeFilterIfExisits(mAllPassBatchClients, clientIf,
702                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
703         }
704 
removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex)705         private void removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex) {
706             if (!clients.contains(clientIf)) {
707                 return;
708             }
709             clients.remove(clientIf);
710             // Remove ALL_PASS filter iff no app is using it.
711             if (clients.isEmpty()) {
712                 resetCountDownLatch();
713                 gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
714                 waitForCallback();
715             }
716         }
717 
getBatchScanClient(int clientIf)718         private ScanClient getBatchScanClient(int clientIf) {
719             for (ScanClient client : mBatchClients) {
720                 if (client.clientIf == clientIf) {
721                     return client;
722                 }
723             }
724             return null;
725         }
726 
727         /**
728          * Return batch scan result type value defined in bt stack.
729          */
getResultType(BatchScanParams params)730         private int getResultType(BatchScanParams params) {
731             if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
732                 return SCAN_RESULT_TYPE_BOTH;
733             }
734             if (params.truncatedScanClientIf != -1) {
735                 return SCAN_RESULT_TYPE_TRUNCATED;
736             }
737             if (params.fullScanClientIf != -1) {
738                 return SCAN_RESULT_TYPE_FULL;
739             }
740             return -1;
741         }
742 
743         // Check if ALL_PASS filter should be used for the client.
shouldUseAllPassFilter(ScanClient client)744         private boolean shouldUseAllPassFilter(ScanClient client) {
745             if (client == null) {
746                 return true;
747             }
748             if (client.filters == null || client.filters.isEmpty()) {
749                 return true;
750             }
751             return client.filters.size() < mClientFilterIndexMap.size();
752         }
753 
addFilterToController(int clientIf, ScanFilterQueue.Entry entry, int filterIndex)754         private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry,
755                 int filterIndex) {
756             logd("addFilterToController: " + entry.type);
757             switch (entry.type) {
758                 case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
759                     logd("add address " + entry.address);
760                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
761                             0,
762                             "", entry.address, (byte) 0, new byte[0], new byte[0]);
763                     break;
764 
765                 case ScanFilterQueue.TYPE_SERVICE_DATA:
766                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
767                             0,
768                             "", "", (byte) 0, entry.data, entry.data_mask);
769                     break;
770 
771                 case ScanFilterQueue.TYPE_SERVICE_UUID:
772                 case ScanFilterQueue.TYPE_SOLICIT_UUID:
773                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
774                             entry.uuid.getLeastSignificantBits(),
775                             entry.uuid.getMostSignificantBits(),
776                             entry.uuid_mask.getLeastSignificantBits(),
777                             entry.uuid_mask.getMostSignificantBits(),
778                             "", "", (byte) 0, new byte[0], new byte[0]);
779                     break;
780 
781                 case ScanFilterQueue.TYPE_LOCAL_NAME:
782                     logd("adding filters: " + entry.name);
783                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
784                             0,
785                             entry.name, "", (byte) 0, new byte[0], new byte[0]);
786                     break;
787 
788                 case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
789                     int len = entry.data.length;
790                     if (entry.data_mask.length != len)
791                         return;
792                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
793                             entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
794                             entry.data, entry.data_mask);
795                     break;
796             }
797         }
798 
initFilterIndexStack()799         private void initFilterIndexStack() {
800             int maxFiltersSupported =
801                     AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
802             // Start from index 3 as:
803             // index 0 is reserved for ALL_PASS filter in Settings app.
804             // index 1 is reserved for ALL_PASS filter for regular scan apps.
805             // index 2 is reserved for ALL_PASS filter for batch scan apps.
806             for (int i = 3; i < maxFiltersSupported; ++i) {
807                 mFilterIndexStack.add(i);
808             }
809         }
810 
811         // Configure filter parameters.
configureFilterParamter(int clientIf, ScanClient client, int featureSelection, int filterIndex)812         private void configureFilterParamter(int clientIf, ScanClient client, int featureSelection,
813                 int filterIndex) {
814             int deliveryMode = getDeliveryMode(client);
815             int rssiThreshold = Byte.MIN_VALUE;
816             int timeout = getOnfoundLostTimeout(client);
817             gattClientScanFilterParamAddNative(
818                     clientIf, filterIndex, featureSelection, LIST_LOGIC_TYPE,
819                     FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
820                     timeout, timeout, ONFOUND_SIGHTINGS);
821         }
822 
823         // Get delivery mode based on scan settings.
getDeliveryMode(ScanClient client)824         private int getDeliveryMode(ScanClient client) {
825             if (client == null) {
826                 return DELIVERY_MODE_IMMEDIATE;
827             }
828             ScanSettings settings = client.settings;
829             if (settings == null) {
830                 return DELIVERY_MODE_IMMEDIATE;
831             }
832             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
833                     || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
834                 return DELIVERY_MODE_ON_FOUND_LOST;
835             }
836             return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
837                     : DELIVERY_MODE_BATCH;
838         }
839 
840         // Get onfound and onlost timeouts in ms
getOnfoundLostTimeout(ScanClient client)841         private int getOnfoundLostTimeout(ScanClient client) {
842             if (client == null) {
843                 return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
844             }
845             ScanSettings settings = client.settings;
846             if (settings == null) {
847                 return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
848             }
849             return (int) settings.getReportDelayMillis();
850         }
851 
852         /************************** Regular scan related native methods **************************/
gattClientScanNative(boolean start)853         private native void gattClientScanNative(boolean start);
854 
gattSetScanParametersNative(int scan_interval, int scan_window)855         private native void gattSetScanParametersNative(int scan_interval,
856                 int scan_window);
857 
858         /************************** Filter related native methods ********************************/
gattClientScanFilterAddNative(int client_if, int filter_type, int filter_index, int company_id, int company_id_mask, long uuid_lsb, long uuid_msb, long uuid_mask_lsb, long uuid_mask_msb, String name, String address, byte addr_type, byte[] data, byte[] mask)859         private native void gattClientScanFilterAddNative(int client_if,
860                 int filter_type, int filter_index, int company_id,
861                 int company_id_mask, long uuid_lsb, long uuid_msb,
862                 long uuid_mask_lsb, long uuid_mask_msb, String name,
863                 String address, byte addr_type, byte[] data, byte[] mask);
864 
gattClientScanFilterDeleteNative(int client_if, int filter_type, int filter_index, int company_id, int company_id_mask, long uuid_lsb, long uuid_msb, long uuid_mask_lsb, long uuid_mask_msb, String name, String address, byte addr_type, byte[] data, byte[] mask)865         private native void gattClientScanFilterDeleteNative(int client_if,
866                 int filter_type, int filter_index, int company_id,
867                 int company_id_mask, long uuid_lsb, long uuid_msb,
868                 long uuid_mask_lsb, long uuid_mask_msb, String name,
869                 String address, byte addr_type, byte[] data, byte[] mask);
870 
gattClientScanFilterParamAddNative( int client_if, int filt_index, int feat_seln, int list_logic_type, int filt_logic_type, int rssi_high_thres, int rssi_low_thres, int dely_mode, int found_timeout, int lost_timeout, int found_timeout_cnt)871         private native void gattClientScanFilterParamAddNative(
872                 int client_if, int filt_index, int feat_seln,
873                 int list_logic_type, int filt_logic_type, int rssi_high_thres,
874                 int rssi_low_thres, int dely_mode, int found_timeout,
875                 int lost_timeout, int found_timeout_cnt);
876 
877         // Note this effectively remove scan filters for ALL clients.
gattClientScanFilterParamClearAllNative( int client_if)878         private native void gattClientScanFilterParamClearAllNative(
879                 int client_if);
880 
gattClientScanFilterParamDeleteNative( int client_if, int filt_index)881         private native void gattClientScanFilterParamDeleteNative(
882                 int client_if, int filt_index);
883 
gattClientScanFilterClearNative(int client_if, int filter_index)884         private native void gattClientScanFilterClearNative(int client_if,
885                 int filter_index);
886 
gattClientScanFilterEnableNative(int client_if, boolean enable)887         private native void gattClientScanFilterEnableNative(int client_if,
888                 boolean enable);
889 
890         /************************** Batch related native methods *********************************/
gattClientConfigBatchScanStorageNative(int client_if, int max_full_reports_percent, int max_truncated_reports_percent, int notify_threshold_percent)891         private native void gattClientConfigBatchScanStorageNative(int client_if,
892                 int max_full_reports_percent, int max_truncated_reports_percent,
893                 int notify_threshold_percent);
894 
gattClientStartBatchScanNative(int client_if, int scan_mode, int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule)895         private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
896                 int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
897 
gattClientStopBatchScanNative(int client_if)898         private native void gattClientStopBatchScanNative(int client_if);
899 
gattClientReadScanReportsNative(int client_if, int scan_type)900         private native void gattClientReadScanReportsNative(int client_if, int scan_type);
901     }
902 
logd(String s)903     private void logd(String s) {
904         if (DBG) Log.d(TAG, s);
905     }
906 }
907