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