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