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.ScanCallback;
23 import android.bluetooth.le.ScanFilter;
24 import android.bluetooth.le.ScanSettings;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.Looper;
32 import android.os.Message;
33 import android.os.RemoteException;
34 import android.os.ServiceManager;
35 import android.os.SystemClock;
36 import android.util.Log;
37 
38 import com.android.bluetooth.Utils;
39 import com.android.bluetooth.btservice.AdapterService;
40 import com.android.internal.app.IBatteryStats;
41 
42 import java.util.ArrayDeque;
43 import java.util.Deque;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Map;
47 import java.util.Set;
48 import java.util.concurrent.CountDownLatch;
49 import java.util.concurrent.TimeUnit;
50 
51 /**
52  * Class that handles Bluetooth LE scan related operations.
53  *
54  * @hide
55  */
56 public class ScanManager {
57     private static final boolean DBG = GattServiceConfig.DBG;
58     private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager";
59 
60     // Result type defined in bt stack. Need to be accessed by GattService.
61     static final int SCAN_RESULT_TYPE_TRUNCATED = 1;
62     static final int SCAN_RESULT_TYPE_FULL = 2;
63     static final int SCAN_RESULT_TYPE_BOTH = 3;
64 
65     // Internal messages for handling BLE scan operations.
66     private static final int MSG_START_BLE_SCAN = 0;
67     private static final int MSG_STOP_BLE_SCAN = 1;
68     private static final int MSG_FLUSH_BATCH_RESULTS = 2;
69     private static final int MSG_SCAN_TIMEOUT = 3;
70 
71     // Maximum msec before scan gets downgraded to opportunistic
72     private static final int SCAN_TIMEOUT_MS = 30 * 60 * 1000;
73 
74     private static final String ACTION_REFRESH_BATCHED_SCAN =
75             "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN";
76 
77     // Timeout for each controller operation.
78     private static final int OPERATION_TIME_OUT_MILLIS = 500;
79 
80     private int mLastConfiguredScanSetting = Integer.MIN_VALUE;
81     // Scan parameters for batch scan.
82     private BatchScanParams mBatchScanParms;
83 
84     private Integer curUsedTrackableAdvertisements;
85     private GattService mService;
86     private IBatteryStats mBatteryStats;
87     private BroadcastReceiver mBatchAlarmReceiver;
88     private boolean mBatchAlarmReceiverRegistered;
89     private ScanNative mScanNative;
90     private ClientHandler mHandler;
91 
92     private Set<ScanClient> mRegularScanClients;
93     private Set<ScanClient> mBatchClients;
94 
95     private CountDownLatch mLatch;
96 
ScanManager(GattService service)97     ScanManager(GattService service) {
98         mRegularScanClients = new HashSet<ScanClient>();
99         mBatchClients = new HashSet<ScanClient>();
100         mService = service;
101         mScanNative = new ScanNative();
102         curUsedTrackableAdvertisements = 0;
103     }
104 
start()105     void start() {
106         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batterystats"));
107         HandlerThread thread = new HandlerThread("BluetoothScanManager");
108         thread.start();
109         mHandler = new ClientHandler(thread.getLooper());
110     }
111 
cleanup()112     void cleanup() {
113         mRegularScanClients.clear();
114         mBatchClients.clear();
115         mScanNative.cleanup();
116     }
117 
118     /**
119      * Returns the regular scan queue.
120      */
getRegularScanQueue()121     Set<ScanClient> getRegularScanQueue() {
122         return mRegularScanClients;
123     }
124 
125     /**
126      * Returns batch scan queue.
127      */
getBatchScanQueue()128     Set<ScanClient> getBatchScanQueue() {
129         return mBatchClients;
130     }
131 
132     /**
133      * Returns a set of full batch scan clients.
134      */
getFullBatchScanQueue()135     Set<ScanClient> getFullBatchScanQueue() {
136         // TODO: split full batch scan clients and truncated batch clients so we don't need to
137         // construct this every time.
138         Set<ScanClient> fullBatchClients = new HashSet<ScanClient>();
139         for (ScanClient client : mBatchClients) {
140             if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
141                 fullBatchClients.add(client);
142             }
143         }
144         return fullBatchClients;
145     }
146 
startScan(ScanClient client)147     void startScan(ScanClient client) {
148         sendMessage(MSG_START_BLE_SCAN, client);
149     }
150 
stopScan(ScanClient client)151     void stopScan(ScanClient client) {
152         sendMessage(MSG_STOP_BLE_SCAN, client);
153     }
154 
flushBatchScanResults(ScanClient client)155     void flushBatchScanResults(ScanClient client) {
156         sendMessage(MSG_FLUSH_BATCH_RESULTS, client);
157     }
158 
callbackDone(int clientIf, int status)159     void callbackDone(int clientIf, int status) {
160         logd("callback done for clientIf - " + clientIf + " status - " + status);
161         if (status == 0) {
162             mLatch.countDown();
163         }
164         // TODO: add a callback for scan failure.
165     }
166 
sendMessage(int what, ScanClient client)167     private void sendMessage(int what, ScanClient client) {
168         Message message = new Message();
169         message.what = what;
170         message.obj = client;
171         mHandler.sendMessage(message);
172     }
173 
isFilteringSupported()174     private boolean isFilteringSupported() {
175         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
176         return adapter.isOffloadedFilteringSupported();
177     }
178 
179     // Handler class that handles BLE scan operations.
180     private class ClientHandler extends Handler {
181 
ClientHandler(Looper looper)182         ClientHandler(Looper looper) {
183             super(looper);
184         }
185 
186         @Override
handleMessage(Message msg)187         public void handleMessage(Message msg) {
188             ScanClient client = (ScanClient) msg.obj;
189             switch (msg.what) {
190                 case MSG_START_BLE_SCAN:
191                     handleStartScan(client);
192                     break;
193                 case MSG_STOP_BLE_SCAN:
194                     handleStopScan(client);
195                     break;
196                 case MSG_FLUSH_BATCH_RESULTS:
197                     handleFlushBatchResults(client);
198                     break;
199                 case MSG_SCAN_TIMEOUT:
200                     mScanNative.regularScanTimeout();
201                     break;
202                 default:
203                     // Shouldn't happen.
204                     Log.e(TAG, "received an unkown message : " + msg.what);
205             }
206         }
207 
handleStartScan(ScanClient client)208         void handleStartScan(ScanClient client) {
209             Utils.enforceAdminPermission(mService);
210             logd("handling starting scan");
211 
212             if (!isScanSupported(client)) {
213                 Log.e(TAG, "Scan settings not supported");
214                 return;
215             }
216 
217             if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) {
218                 Log.e(TAG, "Scan already started");
219                 return;
220             }
221             // Begin scan operations.
222             if (isBatchClient(client)) {
223                 mBatchClients.add(client);
224                 mScanNative.startBatchScan(client);
225             } else {
226                 mRegularScanClients.add(client);
227                 mScanNative.startRegularScan(client);
228                 if (!mScanNative.isOpportunisticScanClient(client)) {
229                     mScanNative.configureRegularScanParams();
230 
231                     if (!mScanNative.isFirstMatchScanClient(client)) {
232                         Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT);
233                         msg.obj = client;
234                         // Only one timeout message should exist at any time
235                         mHandler.removeMessages(SCAN_TIMEOUT_MS);
236                         mHandler.sendMessageDelayed(msg, SCAN_TIMEOUT_MS);
237                     }
238                 }
239 
240                 // Update BatteryStats with this workload.
241                 try {
242                     mBatteryStats.noteBleScanStarted(client.workSource);
243                 } catch (RemoteException e) {
244                     /* ignore */
245                 }
246             }
247         }
248 
handleStopScan(ScanClient client)249         void handleStopScan(ScanClient client) {
250             Utils.enforceAdminPermission(mService);
251             if (client == null) return;
252 
253             if (mRegularScanClients.contains(client)) {
254                 // The ScanClient passed in just holds the clientIf. We retrieve the real client,
255                 // which may have workSource set.
256                 client = mScanNative.getRegularScanClient(client.clientIf);
257                 if (client == null) return;
258 
259                 mScanNative.stopRegularScan(client);
260 
261                 if (mScanNative.numRegularScanClients() == 0) {
262                     mHandler.removeMessages(MSG_SCAN_TIMEOUT);
263                 }
264 
265                 if (!mScanNative.isOpportunisticScanClient(client)) {
266                     mScanNative.configureRegularScanParams();
267                 }
268 
269                 // Update BatteryStats with this workload.
270                 try {
271                     mBatteryStats.noteBleScanStopped(client.workSource);
272                 } catch (RemoteException e) {
273                     /* ignore */
274                 }
275             } else {
276                 mScanNative.stopBatchScan(client);
277             }
278             if (client.appDied) {
279                 logd("app died, unregister client - " + client.clientIf);
280                 mService.unregisterClient(client.clientIf);
281             }
282         }
283 
handleFlushBatchResults(ScanClient client)284         void handleFlushBatchResults(ScanClient client) {
285             Utils.enforceAdminPermission(mService);
286             if (!mBatchClients.contains(client)) {
287                 return;
288             }
289             mScanNative.flushBatchResults(client.clientIf);
290         }
291 
isBatchClient(ScanClient client)292         private boolean isBatchClient(ScanClient client) {
293             if (client == null || client.settings == null) {
294                 return false;
295             }
296             ScanSettings settings = client.settings;
297             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
298                     settings.getReportDelayMillis() != 0;
299         }
300 
isScanSupported(ScanClient client)301         private boolean isScanSupported(ScanClient client) {
302             if (client == null || client.settings == null) {
303                 return true;
304             }
305             ScanSettings settings = client.settings;
306             if (isFilteringSupported()) {
307                 return true;
308             }
309             return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES &&
310                     settings.getReportDelayMillis() == 0;
311         }
312     }
313 
314     /**
315      * Parameters for batch scans.
316      */
317     class BatchScanParams {
318         int scanMode;
319         int fullScanClientIf;
320         int truncatedScanClientIf;
321 
BatchScanParams()322         BatchScanParams() {
323             scanMode = -1;
324             fullScanClientIf = -1;
325             truncatedScanClientIf = -1;
326         }
327 
328         @Override
equals(Object obj)329         public boolean equals(Object obj) {
330             if (this == obj) {
331                 return true;
332             }
333             if (obj == null || getClass() != obj.getClass()) {
334                 return false;
335             }
336             BatchScanParams other = (BatchScanParams) obj;
337             return scanMode == other.scanMode && fullScanClientIf == other.fullScanClientIf
338                     && truncatedScanClientIf == other.truncatedScanClientIf;
339 
340         }
341     }
342 
getCurrentUsedTrackingAdvertisement()343     public int getCurrentUsedTrackingAdvertisement() {
344         return curUsedTrackableAdvertisements;
345     }
346 
347     private class ScanNative {
348 
349         // Delivery mode defined in bt stack.
350         private static final int DELIVERY_MODE_IMMEDIATE = 0;
351         private static final int DELIVERY_MODE_ON_FOUND_LOST = 1;
352         private static final int DELIVERY_MODE_BATCH = 2;
353 
354         private static final int ONFOUND_SIGHTINGS_AGGRESSIVE = 1;
355         private static final int ONFOUND_SIGHTINGS_STICKY = 4;
356 
357         private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1;
358         private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2;
359         private static final int ALL_PASS_FILTER_SELECTION = 0;
360 
361         private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0;
362 
363         /**
364          * Scan params corresponding to regular scan setting
365          */
366         private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 500;
367         private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 5000;
368         private static final int SCAN_MODE_BALANCED_WINDOW_MS = 2000;
369         private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 5000;
370         private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 5000;
371         private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 5000;
372 
373         /**
374          * Onfound/onlost for scan settings
375          */
376         private static final int MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR = (1);
377         private static final int MATCH_MODE_STICKY_TIMEOUT_FACTOR = (3);
378         private static final int ONLOST_FACTOR = 2;
379         private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500;
380 
381         /**
382          * Scan params corresponding to batch scan setting
383          */
384         private static final int SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS = 1500;
385         private static final int SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS = 150000;
386         private static final int SCAN_MODE_BATCH_BALANCED_WINDOW_MS = 1500;
387         private static final int SCAN_MODE_BATCH_BALANCED_INTERVAL_MS = 15000;
388         private static final int SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS = 1500;
389         private static final int SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS = 5000;
390 
391         // The logic is AND for each filter field.
392         private static final int LIST_LOGIC_TYPE = 0x1111111;
393         private static final int FILTER_LOGIC_TYPE = 1;
394         // Filter indices that are available to user. It's sad we need to maintain filter index.
395         private final Deque<Integer> mFilterIndexStack;
396         // Map of clientIf and Filter indices used by client.
397         private final Map<Integer, Deque<Integer>> mClientFilterIndexMap;
398         // Keep track of the clients that uses ALL_PASS filters.
399         private final Set<Integer> mAllPassRegularClients = new HashSet<>();
400         private final Set<Integer> mAllPassBatchClients = new HashSet<>();
401 
402         private AlarmManager mAlarmManager;
403         private PendingIntent mBatchScanIntervalIntent;
404 
ScanNative()405         ScanNative() {
406             mFilterIndexStack = new ArrayDeque<Integer>();
407             mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>();
408 
409             mAlarmManager = (AlarmManager) mService.getSystemService(Context.ALARM_SERVICE);
410             Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null);
411             mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent, 0);
412             IntentFilter filter = new IntentFilter();
413             filter.addAction(ACTION_REFRESH_BATCHED_SCAN);
414             mBatchAlarmReceiver = new BroadcastReceiver() {
415                     @Override
416                 public void onReceive(Context context, Intent intent) {
417                     Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime());
418                     String action = intent.getAction();
419 
420                     if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) {
421                         if (mBatchClients.isEmpty()) {
422                             return;
423                         }
424                         // Note this actually flushes all pending batch data.
425                         flushBatchScanResults(mBatchClients.iterator().next());
426                     }
427                 }
428             };
429             mService.registerReceiver(mBatchAlarmReceiver, filter);
430             mBatchAlarmReceiverRegistered = true;
431         }
432 
resetCountDownLatch()433         private void resetCountDownLatch() {
434             mLatch = new CountDownLatch(1);
435         }
436 
437         // Returns true if mLatch reaches 0, false if timeout or interrupted.
waitForCallback()438         private boolean waitForCallback() {
439             try {
440                 return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS);
441             } catch (InterruptedException e) {
442                 return false;
443             }
444         }
445 
configureRegularScanParams()446         void configureRegularScanParams() {
447             logd("configureRegularScanParams() - queue=" + mRegularScanClients.size());
448             int curScanSetting = Integer.MIN_VALUE;
449             ScanClient client = getAggressiveClient(mRegularScanClients);
450             if (client != null) {
451                 curScanSetting = client.settings.getScanMode();
452             }
453 
454             logd("configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting +
455                     " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting);
456 
457             if (curScanSetting != Integer.MIN_VALUE &&
458                     curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
459                 if (curScanSetting != mLastConfiguredScanSetting) {
460                     int scanWindow = getScanWindowMillis(client.settings);
461                     int scanInterval = getScanIntervalMillis(client.settings);
462                     // convert scanWindow and scanInterval from ms to LE scan units(0.625ms)
463                     scanWindow = Utils.millsToUnit(scanWindow);
464                     scanInterval = Utils.millsToUnit(scanInterval);
465                     gattClientScanNative(false);
466                     logd("configureRegularScanParams - scanInterval = " + scanInterval +
467                         "configureRegularScanParams - scanWindow = " + scanWindow);
468                     gattSetScanParametersNative(client.clientIf, scanInterval, scanWindow);
469                     gattClientScanNative(true);
470                     mLastConfiguredScanSetting = curScanSetting;
471                 }
472             } else {
473                 mLastConfiguredScanSetting = curScanSetting;
474                 logd("configureRegularScanParams() - queue emtpy, scan stopped");
475             }
476         }
477 
getAggressiveClient(Set<ScanClient> cList)478         ScanClient getAggressiveClient(Set<ScanClient> cList) {
479             ScanClient result = null;
480             int curScanSetting = Integer.MIN_VALUE;
481             for (ScanClient client : cList) {
482                 // ScanClient scan settings are assumed to be monotonically increasing in value for
483                 // more power hungry(higher duty cycle) operation.
484                 if (client.settings.getScanMode() > curScanSetting) {
485                     result = client;
486                     curScanSetting = client.settings.getScanMode();
487                 }
488             }
489             return result;
490         }
491 
startRegularScan(ScanClient client)492         void startRegularScan(ScanClient client) {
493             if (isFilteringSupported() && mFilterIndexStack.isEmpty()
494                     && mClientFilterIndexMap.isEmpty()) {
495                 initFilterIndexStack();
496             }
497             if (isFilteringSupported()) {
498                 configureScanFilters(client);
499             }
500             // Start scan native only for the first client.
501             if (numRegularScanClients() == 1) {
502                 gattClientScanNative(true);
503             }
504         }
505 
numRegularScanClients()506         private int numRegularScanClients() {
507             int num = 0;
508             for (ScanClient client: mRegularScanClients) {
509                 if (client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) {
510                     num++;
511                 }
512             }
513             return num;
514         }
515 
startBatchScan(ScanClient client)516         void startBatchScan(ScanClient client) {
517             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
518                 initFilterIndexStack();
519             }
520             configureScanFilters(client);
521             if (!isOpportunisticScanClient(client)) {
522                 // Reset batch scan. May need to stop the existing batch scan and update scan params.
523                 resetBatchScan(client);
524             }
525         }
526 
isOpportunisticScanClient(ScanClient client)527         private boolean isOpportunisticScanClient(ScanClient client) {
528             return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC;
529         }
530 
isFirstMatchScanClient(ScanClient client)531         private boolean isFirstMatchScanClient(ScanClient client) {
532             return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0;
533         }
534 
resetBatchScan(ScanClient client)535         private void resetBatchScan(ScanClient client) {
536             int clientIf = client.clientIf;
537             BatchScanParams batchScanParams = getBatchScanParams();
538             // Stop batch if batch scan params changed and previous params is not null.
539             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
540                 logd("stopping BLe Batch");
541                 resetCountDownLatch();
542                 gattClientStopBatchScanNative(clientIf);
543                 waitForCallback();
544                 // Clear pending results as it's illegal to config storage if there are still
545                 // pending results.
546                 flushBatchResults(clientIf);
547             }
548             // Start batch if batchScanParams changed and current params is not null.
549             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
550                 int notifyThreshold = 95;
551                 logd("Starting BLE batch scan");
552                 int resultType = getResultType(batchScanParams);
553                 int fullScanPercent = getFullScanStoragePercent(resultType);
554                 resetCountDownLatch();
555                 logd("configuring batch scan storage, appIf " + client.clientIf);
556                 gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
557                         100 - fullScanPercent, notifyThreshold);
558                 waitForCallback();
559                 resetCountDownLatch();
560                 int scanInterval =
561                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
562                 int scanWindow =
563                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
564                 gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
565                         scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
566                 waitForCallback();
567             }
568             mBatchScanParms = batchScanParams;
569             setBatchAlarm();
570         }
571 
getFullScanStoragePercent(int resultType)572         private int getFullScanStoragePercent(int resultType) {
573             switch (resultType) {
574                 case SCAN_RESULT_TYPE_FULL:
575                     return 100;
576                 case SCAN_RESULT_TYPE_TRUNCATED:
577                     return 0;
578                 case SCAN_RESULT_TYPE_BOTH:
579                     return 50;
580                 default:
581                     return 50;
582             }
583         }
584 
getBatchScanParams()585         private BatchScanParams getBatchScanParams() {
586             if (mBatchClients.isEmpty()) {
587                 return null;
588             }
589             BatchScanParams params = new BatchScanParams();
590             // TODO: split full batch scan results and truncated batch scan results to different
591             // collections.
592             for (ScanClient client : mBatchClients) {
593                 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
594                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
595                     params.fullScanClientIf = client.clientIf;
596                 } else {
597                     params.truncatedScanClientIf = client.clientIf;
598                 }
599             }
600             return params;
601         }
602 
getBatchScanWindowMillis(int scanMode)603         private int getBatchScanWindowMillis(int scanMode) {
604             switch (scanMode) {
605                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
606                     return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
607                 case ScanSettings.SCAN_MODE_BALANCED:
608                     return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
609                 case ScanSettings.SCAN_MODE_LOW_POWER:
610                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
611                 default:
612                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
613             }
614         }
615 
getBatchScanIntervalMillis(int scanMode)616         private int getBatchScanIntervalMillis(int scanMode) {
617             switch (scanMode) {
618                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
619                     return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
620                 case ScanSettings.SCAN_MODE_BALANCED:
621                     return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
622                 case ScanSettings.SCAN_MODE_LOW_POWER:
623                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
624                 default:
625                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
626             }
627         }
628 
629         // Set the batch alarm to be triggered within a short window after batch interval. This
630         // allows system to optimize wake up time while still allows a degree of precise control.
setBatchAlarm()631         private void setBatchAlarm() {
632             // Cancel any pending alarm just in case.
633             mAlarmManager.cancel(mBatchScanIntervalIntent);
634             if (mBatchClients.isEmpty()) {
635                 return;
636             }
637             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
638             // Allows the alarm to be triggered within
639             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
640             long windowLengthMillis = batchTriggerIntervalMillis / 10;
641             long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
642             mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
643                     windowStartMillis, windowLengthMillis,
644                     mBatchScanIntervalIntent);
645         }
646 
stopRegularScan(ScanClient client)647         void stopRegularScan(ScanClient client) {
648             // Remove scan filters and recycle filter indices.
649             if (client == null) return;
650             int deliveryMode = getDeliveryMode(client);
651             if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
652                 for (ScanFilter filter : client.filters) {
653                     int entriesToFree = getNumOfTrackingAdvertisements(client.settings);
654                     if (!manageAllocationOfTrackingAdvertisement(entriesToFree, false)) {
655                         Log.e(TAG, "Error freeing for onfound/onlost filter resources "
656                                     + entriesToFree);
657                         try {
658                             mService.onScanManagerErrorCallback(client.clientIf,
659                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
660                         } catch (RemoteException e) {
661                             Log.e(TAG, "failed on onScanManagerCallback at freeing", e);
662                         }
663                     }
664                 }
665             }
666             mRegularScanClients.remove(client);
667             if (numRegularScanClients() == 0) {
668                 logd("stop scan");
669                 gattClientScanNative(false);
670             }
671             removeScanFilters(client.clientIf);
672         }
673 
regularScanTimeout()674         void regularScanTimeout() {
675             for (ScanClient client : mRegularScanClients) {
676                 if (!isOpportunisticScanClient(client) && !isFirstMatchScanClient(client)) {
677                     logd("clientIf set to scan opportunisticly: " + client.clientIf);
678                     setOpportunisticScanClient(client);
679                     client.stats.setScanTimeout();
680                 }
681             }
682 
683             // The scan should continue for background scans
684             configureRegularScanParams();
685             if (numRegularScanClients() == 0) {
686                 logd("stop scan");
687                 gattClientScanNative(false);
688             }
689         }
690 
setOpportunisticScanClient(ScanClient client)691         void setOpportunisticScanClient(ScanClient client) {
692             // TODO: Add constructor to ScanSettings.Builder
693             // that can copy values from an existing ScanSettings object
694             ScanSettings.Builder builder = new ScanSettings.Builder();
695             ScanSettings settings = client.settings;
696             builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC);
697             builder.setCallbackType(settings.getCallbackType());
698             builder.setScanResultType(settings.getScanResultType());
699             builder.setReportDelay(settings.getReportDelayMillis());
700             builder.setNumOfMatches(settings.getNumOfMatches());
701             client.settings = builder.build();
702         }
703 
704         // Find the regular scan client information.
getRegularScanClient(int clientIf)705         ScanClient getRegularScanClient(int clientIf) {
706             for (ScanClient client : mRegularScanClients) {
707               if (client.clientIf == clientIf) return client;
708             }
709             return null;
710         }
711 
stopBatchScan(ScanClient client)712         void stopBatchScan(ScanClient client) {
713             mBatchClients.remove(client);
714             removeScanFilters(client.clientIf);
715             if (!isOpportunisticScanClient(client)) {
716                 resetBatchScan(client);
717             }
718         }
719 
flushBatchResults(int clientIf)720         void flushBatchResults(int clientIf) {
721             logd("flushPendingBatchResults - clientIf = " + clientIf);
722             if (mBatchScanParms.fullScanClientIf != -1) {
723                 resetCountDownLatch();
724                 gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
725                         SCAN_RESULT_TYPE_FULL);
726                 waitForCallback();
727             }
728             if (mBatchScanParms.truncatedScanClientIf != -1) {
729                 resetCountDownLatch();
730                 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
731                         SCAN_RESULT_TYPE_TRUNCATED);
732                 waitForCallback();
733             }
734             setBatchAlarm();
735         }
736 
cleanup()737         void cleanup() {
738             mAlarmManager.cancel(mBatchScanIntervalIntent);
739             // Protect against multiple calls of cleanup.
740             if (mBatchAlarmReceiverRegistered) {
741                 mService.unregisterReceiver(mBatchAlarmReceiver);
742             }
743             mBatchAlarmReceiverRegistered = false;
744         }
745 
getBatchTriggerIntervalMillis()746         private long getBatchTriggerIntervalMillis() {
747             long intervalMillis = Long.MAX_VALUE;
748             for (ScanClient client : mBatchClients) {
749                 if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
750                     intervalMillis = Math.min(intervalMillis,
751                             client.settings.getReportDelayMillis());
752                 }
753             }
754             return intervalMillis;
755         }
756 
757         // Add scan filters. The logic is:
758         // If no offload filter can/needs to be set, set ALL_PASS filter.
759         // Otherwise offload all filters to hardware and enable all filters.
configureScanFilters(ScanClient client)760         private void configureScanFilters(ScanClient client) {
761             int clientIf = client.clientIf;
762             int deliveryMode = getDeliveryMode(client);
763             int trackEntries = 0;
764             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
765                 return;
766             }
767 
768             resetCountDownLatch();
769             gattClientScanFilterEnableNative(clientIf, true);
770             waitForCallback();
771 
772             if (shouldUseAllPassFilter(client)) {
773                 int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
774                         ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
775                 resetCountDownLatch();
776                 // Don't allow Onfound/onlost with all pass
777                 configureFilterParamter(clientIf, client, ALL_PASS_FILTER_SELECTION,
778                                 filterIndex, 0);
779                 waitForCallback();
780             } else {
781                 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
782                 for (ScanFilter filter : client.filters) {
783                     ScanFilterQueue queue = new ScanFilterQueue();
784                     queue.addScanFilter(filter);
785                     int featureSelection = queue.getFeatureSelection();
786                     int filterIndex = mFilterIndexStack.pop();
787                     while (!queue.isEmpty()) {
788                         resetCountDownLatch();
789                         addFilterToController(clientIf, queue.pop(), filterIndex);
790                         waitForCallback();
791                     }
792                     resetCountDownLatch();
793                     if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
794                         trackEntries = getNumOfTrackingAdvertisements(client.settings);
795                         if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) {
796                             Log.e(TAG, "No hardware resources for onfound/onlost filter " +
797                                     trackEntries);
798                             try {
799                                 mService.onScanManagerErrorCallback(clientIf,
800                                             ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
801                             } catch (RemoteException e) {
802                                 Log.e(TAG, "failed on onScanManagerCallback", e);
803                             }
804                         }
805                     }
806                     configureFilterParamter(clientIf, client, featureSelection, filterIndex,
807                                             trackEntries);
808                     waitForCallback();
809                     clientFilterIndices.add(filterIndex);
810                 }
811                 mClientFilterIndexMap.put(clientIf, clientFilterIndices);
812             }
813         }
814 
815         // Check whether the filter should be added to controller.
816         // Note only on ALL_PASS filter should be added.
shouldAddAllPassFilterToController(ScanClient client, int deliveryMode)817         private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
818             // Not an ALL_PASS client, need to add filter.
819             if (!shouldUseAllPassFilter(client)) {
820                 return true;
821             }
822 
823             if (deliveryMode == DELIVERY_MODE_BATCH) {
824                 mAllPassBatchClients.add(client.clientIf);
825                 return mAllPassBatchClients.size() == 1;
826             } else {
827                 mAllPassRegularClients.add(client.clientIf);
828                 return mAllPassRegularClients.size() == 1;
829             }
830         }
831 
removeScanFilters(int clientIf)832         private void removeScanFilters(int clientIf) {
833             Deque<Integer> filterIndices = mClientFilterIndexMap.remove(clientIf);
834             if (filterIndices != null) {
835                 mFilterIndexStack.addAll(filterIndices);
836                 for (Integer filterIndex : filterIndices) {
837                     resetCountDownLatch();
838                     gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
839                     waitForCallback();
840                 }
841             }
842             // Remove if ALL_PASS filters are used.
843             removeFilterIfExisits(mAllPassRegularClients, clientIf,
844                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
845             removeFilterIfExisits(mAllPassBatchClients, clientIf,
846                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
847         }
848 
removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex)849         private void removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex) {
850             if (!clients.contains(clientIf)) {
851                 return;
852             }
853             clients.remove(clientIf);
854             // Remove ALL_PASS filter iff no app is using it.
855             if (clients.isEmpty()) {
856                 resetCountDownLatch();
857                 gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
858                 waitForCallback();
859             }
860         }
861 
getBatchScanClient(int clientIf)862         private ScanClient getBatchScanClient(int clientIf) {
863             for (ScanClient client : mBatchClients) {
864                 if (client.clientIf == clientIf) {
865                     return client;
866                 }
867             }
868             return null;
869         }
870 
871         /**
872          * Return batch scan result type value defined in bt stack.
873          */
getResultType(BatchScanParams params)874         private int getResultType(BatchScanParams params) {
875             if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
876                 return SCAN_RESULT_TYPE_BOTH;
877             }
878             if (params.truncatedScanClientIf != -1) {
879                 return SCAN_RESULT_TYPE_TRUNCATED;
880             }
881             if (params.fullScanClientIf != -1) {
882                 return SCAN_RESULT_TYPE_FULL;
883             }
884             return -1;
885         }
886 
887         // Check if ALL_PASS filter should be used for the client.
shouldUseAllPassFilter(ScanClient client)888         private boolean shouldUseAllPassFilter(ScanClient client) {
889             if (client == null) {
890                 return true;
891             }
892             if (client.filters == null || client.filters.isEmpty()) {
893                 return true;
894             }
895             return client.filters.size() > mFilterIndexStack.size();
896         }
897 
addFilterToController(int clientIf, ScanFilterQueue.Entry entry, int filterIndex)898         private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry,
899                 int filterIndex) {
900             logd("addFilterToController: " + entry.type);
901             switch (entry.type) {
902                 case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
903                     logd("add address " + entry.address);
904                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
905                             0,
906                             "", entry.address, (byte) entry.addr_type, new byte[0], new byte[0]);
907                     break;
908 
909                 case ScanFilterQueue.TYPE_SERVICE_DATA:
910                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
911                             0,
912                             "", "", (byte) 0, entry.data, entry.data_mask);
913                     break;
914 
915                 case ScanFilterQueue.TYPE_SERVICE_UUID:
916                 case ScanFilterQueue.TYPE_SOLICIT_UUID:
917                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
918                             entry.uuid.getLeastSignificantBits(),
919                             entry.uuid.getMostSignificantBits(),
920                             entry.uuid_mask.getLeastSignificantBits(),
921                             entry.uuid_mask.getMostSignificantBits(),
922                             "", "", (byte) 0, new byte[0], new byte[0]);
923                     break;
924 
925                 case ScanFilterQueue.TYPE_LOCAL_NAME:
926                     logd("adding filters: " + entry.name);
927                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
928                             0,
929                             entry.name, "", (byte) 0, new byte[0], new byte[0]);
930                     break;
931 
932                 case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
933                     int len = entry.data.length;
934                     if (entry.data_mask.length != len)
935                         return;
936                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
937                             entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
938                             entry.data, entry.data_mask);
939                     break;
940             }
941         }
942 
initFilterIndexStack()943         private void initFilterIndexStack() {
944             int maxFiltersSupported =
945                     AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
946             // Start from index 3 as:
947             // index 0 is reserved for ALL_PASS filter in Settings app.
948             // index 1 is reserved for ALL_PASS filter for regular scan apps.
949             // index 2 is reserved for ALL_PASS filter for batch scan apps.
950             for (int i = 3; i < maxFiltersSupported; ++i) {
951                 mFilterIndexStack.add(i);
952             }
953         }
954 
955         // Configure filter parameters.
configureFilterParamter(int clientIf, ScanClient client, int featureSelection, int filterIndex, int numOfTrackingEntries)956         private void configureFilterParamter(int clientIf, ScanClient client, int featureSelection,
957                 int filterIndex, int numOfTrackingEntries) {
958             int deliveryMode = getDeliveryMode(client);
959             int rssiThreshold = Byte.MIN_VALUE;
960             ScanSettings settings = client.settings;
961             int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true);
962             int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false);
963             int onFoundCount = getOnFoundOnLostSightings(settings);
964             onLostTimeout = 10000;
965             logd("configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " "
966                     + onFoundCount + " " + numOfTrackingEntries);
967             FilterParams FiltValue = new FilterParams(clientIf, filterIndex, featureSelection,
968                     LIST_LOGIC_TYPE, FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
969                     onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries);
970             gattClientScanFilterParamAddNative(FiltValue);
971         }
972 
973         // Get delivery mode based on scan settings.
getDeliveryMode(ScanClient client)974         private int getDeliveryMode(ScanClient client) {
975             if (client == null) {
976                 return DELIVERY_MODE_IMMEDIATE;
977             }
978             ScanSettings settings = client.settings;
979             if (settings == null) {
980                 return DELIVERY_MODE_IMMEDIATE;
981             }
982             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
983                     || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
984                 return DELIVERY_MODE_ON_FOUND_LOST;
985             }
986             return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
987                     : DELIVERY_MODE_BATCH;
988         }
989 
getScanWindowMillis(ScanSettings settings)990         private int getScanWindowMillis(ScanSettings settings) {
991             if (settings == null) {
992                 return SCAN_MODE_LOW_POWER_WINDOW_MS;
993             }
994             switch (settings.getScanMode()) {
995                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
996                     return SCAN_MODE_LOW_LATENCY_WINDOW_MS;
997                 case ScanSettings.SCAN_MODE_BALANCED:
998                     return SCAN_MODE_BALANCED_WINDOW_MS;
999                 case ScanSettings.SCAN_MODE_LOW_POWER:
1000                     return SCAN_MODE_LOW_POWER_WINDOW_MS;
1001                 default:
1002                     return SCAN_MODE_LOW_POWER_WINDOW_MS;
1003             }
1004         }
1005 
getScanIntervalMillis(ScanSettings settings)1006         private int getScanIntervalMillis(ScanSettings settings) {
1007             if (settings == null)
1008                 return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1009             switch (settings.getScanMode()) {
1010                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
1011                     return SCAN_MODE_LOW_LATENCY_INTERVAL_MS;
1012                 case ScanSettings.SCAN_MODE_BALANCED:
1013                     return SCAN_MODE_BALANCED_INTERVAL_MS;
1014                 case ScanSettings.SCAN_MODE_LOW_POWER:
1015                     return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1016                 default:
1017                     return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1018             }
1019         }
1020 
getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound)1021         private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) {
1022             int factor;
1023             int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS;
1024 
1025             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1026                 factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR;
1027             } else {
1028                 factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR;
1029             }
1030             if (!onFound) factor = factor * ONLOST_FACTOR;
1031             return (timeout*factor);
1032         }
1033 
getOnFoundOnLostSightings(ScanSettings settings)1034         private int getOnFoundOnLostSightings(ScanSettings settings) {
1035             if (settings == null)
1036                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
1037             if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1038                 return ONFOUND_SIGHTINGS_AGGRESSIVE;
1039             } else {
1040                 return ONFOUND_SIGHTINGS_STICKY;
1041             }
1042         }
1043 
getNumOfTrackingAdvertisements(ScanSettings settings)1044         private int getNumOfTrackingAdvertisements(ScanSettings settings) {
1045             if (settings == null)
1046                 return 0;
1047             int val=0;
1048             int maxTotalTrackableAdvertisements =
1049                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1050             // controller based onfound onlost resources are scarce commodity; the
1051             // assignment of filters to num of beacons to track is configurable based
1052             // on hw capabilities. Apps give an intent and allocation of onfound
1053             // resources or failure there of is done based on availibility - FCFS model
1054             switch (settings.getNumOfMatches()) {
1055                 case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT:
1056                     val = 1;
1057                     break;
1058                 case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT:
1059                     val = 2;
1060                     break;
1061                 case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT:
1062                     val = maxTotalTrackableAdvertisements/2;
1063                     break;
1064                 default:
1065                     val = 1;
1066                     logd("Invalid setting for getNumOfMatches() " + settings.getNumOfMatches());
1067             }
1068             return val;
1069         }
1070 
manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement, boolean allocate)1071         private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement,
1072                             boolean allocate) {
1073             int maxTotalTrackableAdvertisements =
1074                     AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1075             synchronized(curUsedTrackableAdvertisements) {
1076                 int availableEntries = maxTotalTrackableAdvertisements
1077                                             - curUsedTrackableAdvertisements;
1078                 if (allocate) {
1079                     if (availableEntries >= numOfTrackableAdvertisement) {
1080                         curUsedTrackableAdvertisements += numOfTrackableAdvertisement;
1081                         return true;
1082                     } else {
1083                         return false;
1084                     }
1085                 } else {
1086                     if (numOfTrackableAdvertisement > curUsedTrackableAdvertisements) {
1087                         return false;
1088                     } else {
1089                          curUsedTrackableAdvertisements -= numOfTrackableAdvertisement;
1090                          return true;
1091                     }
1092                 }
1093             }
1094         }
1095 
1096 
1097         /************************** Regular scan related native methods **************************/
gattClientScanNative(boolean start)1098         private native void gattClientScanNative(boolean start);
1099 
gattSetScanParametersNative(int client_if, int scan_interval, int scan_window)1100         private native void gattSetScanParametersNative(int client_if, int scan_interval,
1101                 int scan_window);
1102 
1103         /************************** 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)1104         private native void gattClientScanFilterAddNative(int client_if,
1105                 int filter_type, int filter_index, int company_id,
1106                 int company_id_mask, long uuid_lsb, long uuid_msb,
1107                 long uuid_mask_lsb, long uuid_mask_msb, String name,
1108                 String address, byte addr_type, byte[] data, byte[] mask);
1109 
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)1110         private native void gattClientScanFilterDeleteNative(int client_if,
1111                 int filter_type, int filter_index, int company_id,
1112                 int company_id_mask, long uuid_lsb, long uuid_msb,
1113                 long uuid_mask_lsb, long uuid_mask_msb, String name,
1114                 String address, byte addr_type, byte[] data, byte[] mask);
1115 
gattClientScanFilterParamAddNative(FilterParams FiltValue)1116         private native void gattClientScanFilterParamAddNative(FilterParams FiltValue);
1117 
1118         // Note this effectively remove scan filters for ALL clients.
gattClientScanFilterParamClearAllNative( int client_if)1119         private native void gattClientScanFilterParamClearAllNative(
1120                 int client_if);
1121 
gattClientScanFilterParamDeleteNative( int client_if, int filt_index)1122         private native void gattClientScanFilterParamDeleteNative(
1123                 int client_if, int filt_index);
1124 
gattClientScanFilterClearNative(int client_if, int filter_index)1125         private native void gattClientScanFilterClearNative(int client_if,
1126                 int filter_index);
1127 
gattClientScanFilterEnableNative(int client_if, boolean enable)1128         private native void gattClientScanFilterEnableNative(int client_if,
1129                 boolean enable);
1130 
1131         /************************** Batch related native methods *********************************/
gattClientConfigBatchScanStorageNative(int client_if, int max_full_reports_percent, int max_truncated_reports_percent, int notify_threshold_percent)1132         private native void gattClientConfigBatchScanStorageNative(int client_if,
1133                 int max_full_reports_percent, int max_truncated_reports_percent,
1134                 int notify_threshold_percent);
1135 
gattClientStartBatchScanNative(int client_if, int scan_mode, int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule)1136         private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
1137                 int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
1138 
gattClientStopBatchScanNative(int client_if)1139         private native void gattClientStopBatchScanNative(int client_if);
1140 
gattClientReadScanReportsNative(int client_if, int scan_type)1141         private native void gattClientReadScanReportsNative(int client_if, int scan_type);
1142     }
1143 
logd(String s)1144     private void logd(String s) {
1145         if (DBG) Log.d(TAG, s);
1146     }
1147 }
1148