ScanManager.java revision 2b9ac142e56cf6a6cc0572fb75f8a5ce40894f8b
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 (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
445                initFilterIndexStack();
446            }
447            if (isFilteringSupported()) {
448                configureScanFilters(client);
449            }
450            // Start scan native only for the first client.
451            if (mRegularScanClients.size() == 1) {
452                gattClientScanNative(true);
453            }
454        }
455
456        void startBatchScan(ScanClient client) {
457            if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
458                initFilterIndexStack();
459            }
460            configureScanFilters(client);
461            // Reset batch scan. May need to stop the existing batch scan and update scan params.
462            resetBatchScan(client);
463        }
464
465        private void resetBatchScan(ScanClient client) {
466            int clientIf = client.clientIf;
467            BatchScanParams batchScanParams = getBatchScanParams();
468            // Stop batch if batch scan params changed and previous params is not null.
469            if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
470                logd("stopping BLe Batch");
471                resetCountDownLatch();
472                gattClientStopBatchScanNative(clientIf);
473                waitForCallback();
474                // Clear pending results as it's illegal to config storage if there are still
475                // pending results.
476                flushBatchResults(clientIf);
477            }
478            // Start batch if batchScanParams changed and current params is not null.
479            if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
480                int notifyThreshold = 95;
481                logd("Starting BLE batch scan");
482                int resultType = getResultType(batchScanParams);
483                int fullScanPercent = getFullScanStoragePercent(resultType);
484                resetCountDownLatch();
485                logd("configuring batch scan storage, appIf " + client.clientIf);
486                gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
487                        100 - fullScanPercent, notifyThreshold);
488                waitForCallback();
489                resetCountDownLatch();
490                int scanInterval =
491                        Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
492                int scanWindow =
493                        Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
494                gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
495                        scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
496                waitForCallback();
497            }
498            mBatchScanParms = batchScanParams;
499            setBatchAlarm();
500        }
501
502        private int getFullScanStoragePercent(int resultType) {
503            switch (resultType) {
504                case SCAN_RESULT_TYPE_FULL:
505                    return 100;
506                case SCAN_RESULT_TYPE_TRUNCATED:
507                    return 0;
508                case SCAN_RESULT_TYPE_BOTH:
509                    return 50;
510                default:
511                    return 50;
512            }
513        }
514
515        private BatchScanParams getBatchScanParams() {
516            if (mBatchClients.isEmpty()) {
517                return null;
518            }
519            BatchScanParams params = new BatchScanParams();
520            // TODO: split full batch scan results and truncated batch scan results to different
521            // collections.
522            for (ScanClient client : mBatchClients) {
523                params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
524                if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
525                    params.fullScanClientIf = client.clientIf;
526                } else {
527                    params.truncatedScanClientIf = client.clientIf;
528                }
529            }
530            return params;
531        }
532
533        private int getBatchScanWindowMillis(int scanMode) {
534            switch (scanMode) {
535                case ScanSettings.SCAN_MODE_LOW_LATENCY:
536                    return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
537                case ScanSettings.SCAN_MODE_BALANCED:
538                    return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
539                case ScanSettings.SCAN_MODE_LOW_POWER:
540                    return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
541                default:
542                    return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
543            }
544        }
545
546        private int getBatchScanIntervalMillis(int scanMode) {
547            switch (scanMode) {
548                case ScanSettings.SCAN_MODE_LOW_LATENCY:
549                    return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
550                case ScanSettings.SCAN_MODE_BALANCED:
551                    return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
552                case ScanSettings.SCAN_MODE_LOW_POWER:
553                    return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
554                default:
555                    return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
556            }
557        }
558
559        // Set the batch alarm to be triggered within a short window after batch interval. This
560        // allows system to optimize wake up time while still allows a degree of precise control.
561        private void setBatchAlarm() {
562            // Cancel any pending alarm just in case.
563            mAlarmManager.cancel(mBatchScanIntervalIntent);
564            if (mBatchClients.isEmpty()) {
565                return;
566            }
567            long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
568            // Allows the alarm to be triggered within
569            // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
570            long windowLengthMillis = batchTriggerIntervalMillis / 10;
571            long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
572            mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
573                    windowStartMillis, windowLengthMillis,
574                    mBatchScanIntervalIntent);
575        }
576
577        void stopRegularScan(ScanClient client) {
578            // Remove scan filters and recycle filter indices.
579            removeScanFilters(client.clientIf);
580            mRegularScanClients.remove(client);
581            if (mRegularScanClients.isEmpty()) {
582                logd("stop scan");
583                gattClientScanNative(false);
584            }
585        }
586
587        void stopBatchScan(ScanClient client) {
588            mBatchClients.remove(client);
589            removeScanFilters(client.clientIf);
590            resetBatchScan(client);
591        }
592
593        void flushBatchResults(int clientIf) {
594            logd("flushPendingBatchResults - clientIf = " + clientIf);
595            if (mBatchScanParms.fullScanClientIf != -1) {
596                resetCountDownLatch();
597                gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
598                        SCAN_RESULT_TYPE_FULL);
599                waitForCallback();
600            }
601            if (mBatchScanParms.truncatedScanClientIf != -1) {
602                resetCountDownLatch();
603                gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
604                        SCAN_RESULT_TYPE_TRUNCATED);
605                waitForCallback();
606            }
607            setBatchAlarm();
608        }
609
610        void cleanup() {
611            mAlarmManager.cancel(mBatchScanIntervalIntent);
612            // Protect against multiple calls of cleanup.
613            if (mBatchAlarmReceiverRegistered) {
614                mService.unregisterReceiver(mBatchAlarmReceiver);
615            }
616            mBatchAlarmReceiverRegistered = false;
617        }
618
619        private long getBatchTriggerIntervalMillis() {
620            long intervalMillis = Long.MAX_VALUE;
621            for (ScanClient client : mBatchClients) {
622                if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
623                    intervalMillis = Math.min(intervalMillis,
624                            client.settings.getReportDelayMillis());
625                }
626            }
627            return intervalMillis;
628        }
629
630        // Add scan filters. The logic is:
631        // If no offload filter can/needs to be set, set ALL_PASS filter.
632        // Otherwise offload all filters to hardware and enable all filters.
633        private void configureScanFilters(ScanClient client) {
634            int clientIf = client.clientIf;
635            int deliveryMode = getDeliveryMode(client);
636            if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
637                return;
638            }
639
640            resetCountDownLatch();
641            gattClientScanFilterEnableNative(clientIf, true);
642            waitForCallback();
643
644            if (shouldUseAllPassFilter(client)) {
645                int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
646                        ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
647                resetCountDownLatch();
648                configureFilterParamter(clientIf, client, ALL_PASS_FILTER_SELECTION, filterIndex);
649                waitForCallback();
650            } else {
651                Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
652                for (ScanFilter filter : client.filters) {
653                    ScanFilterQueue queue = new ScanFilterQueue();
654                    queue.addScanFilter(filter);
655                    int featureSelection = queue.getFeatureSelection();
656                    int filterIndex = mFilterIndexStack.pop();
657                    while (!queue.isEmpty()) {
658                        resetCountDownLatch();
659                        addFilterToController(clientIf, queue.pop(), filterIndex);
660                        waitForCallback();
661                    }
662                    resetCountDownLatch();
663                    configureFilterParamter(clientIf, client, featureSelection, filterIndex);
664                    waitForCallback();
665                    clientFilterIndices.add(filterIndex);
666                }
667                mClientFilterIndexMap.put(clientIf, clientFilterIndices);
668            }
669        }
670
671        // Check whether the filter should be added to controller.
672        // Note only on ALL_PASS filter should be added.
673        private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
674            // Not an ALL_PASS client, need to add filter.
675            if (!shouldUseAllPassFilter(client)) {
676                return true;
677            }
678
679            if (deliveryMode == DELIVERY_MODE_BATCH) {
680                mAllPassBatchClients.add(client.clientIf);
681                return mAllPassBatchClients.size() == 1;
682            } else {
683                mAllPassRegularClients.add(client.clientIf);
684                return mAllPassRegularClients.size() == 1;
685            }
686        }
687
688        private void removeScanFilters(int clientIf) {
689            Deque<Integer> filterIndices = mClientFilterIndexMap.remove(clientIf);
690            if (filterIndices != null) {
691                mFilterIndexStack.addAll(filterIndices);
692                for (Integer filterIndex : filterIndices) {
693                    resetCountDownLatch();
694                    gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
695                    waitForCallback();
696                }
697            }
698            // Remove if ALL_PASS filters are used.
699            removeFilterIfExisits(mAllPassRegularClients, clientIf,
700                    ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
701            removeFilterIfExisits(mAllPassBatchClients, clientIf,
702                    ALL_PASS_FILTER_INDEX_BATCH_SCAN);
703        }
704
705        private void removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex) {
706            if (!clients.contains(clientIf)) {
707                return;
708            }
709            clients.remove(clientIf);
710            // Remove ALL_PASS filter iff no app is using it.
711            if (clients.isEmpty()) {
712                resetCountDownLatch();
713                gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
714                waitForCallback();
715            }
716        }
717
718        private ScanClient getBatchScanClient(int clientIf) {
719            for (ScanClient client : mBatchClients) {
720                if (client.clientIf == clientIf) {
721                    return client;
722                }
723            }
724            return null;
725        }
726
727        /**
728         * Return batch scan result type value defined in bt stack.
729         */
730        private int getResultType(BatchScanParams params) {
731            if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
732                return SCAN_RESULT_TYPE_BOTH;
733            }
734            if (params.truncatedScanClientIf != -1) {
735                return SCAN_RESULT_TYPE_TRUNCATED;
736            }
737            if (params.fullScanClientIf != -1) {
738                return SCAN_RESULT_TYPE_FULL;
739            }
740            return -1;
741        }
742
743        // Check if ALL_PASS filter should be used for the client.
744        private boolean shouldUseAllPassFilter(ScanClient client) {
745            if (client == null) {
746                return true;
747            }
748            if (client.filters == null || client.filters.isEmpty()) {
749                return true;
750            }
751            return client.filters.size() < mClientFilterIndexMap.size();
752        }
753
754        private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry,
755                int filterIndex) {
756            logd("addFilterToController: " + entry.type);
757            switch (entry.type) {
758                case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
759                    logd("add address " + entry.address);
760                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
761                            0,
762                            "", entry.address, (byte) 0, new byte[0], new byte[0]);
763                    break;
764
765                case ScanFilterQueue.TYPE_SERVICE_DATA:
766                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
767                            0,
768                            "", "", (byte) 0, entry.data, entry.data_mask);
769                    break;
770
771                case ScanFilterQueue.TYPE_SERVICE_UUID:
772                case ScanFilterQueue.TYPE_SOLICIT_UUID:
773                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
774                            entry.uuid.getLeastSignificantBits(),
775                            entry.uuid.getMostSignificantBits(),
776                            entry.uuid_mask.getLeastSignificantBits(),
777                            entry.uuid_mask.getMostSignificantBits(),
778                            "", "", (byte) 0, new byte[0], new byte[0]);
779                    break;
780
781                case ScanFilterQueue.TYPE_LOCAL_NAME:
782                    logd("adding filters: " + entry.name);
783                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
784                            0,
785                            entry.name, "", (byte) 0, new byte[0], new byte[0]);
786                    break;
787
788                case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
789                    int len = entry.data.length;
790                    if (entry.data_mask.length != len)
791                        return;
792                    gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
793                            entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
794                            entry.data, entry.data_mask);
795                    break;
796            }
797        }
798
799        private void initFilterIndexStack() {
800            int maxFiltersSupported =
801                    AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
802            // Start from index 3 as:
803            // index 0 is reserved for ALL_PASS filter in Settings app.
804            // index 1 is reserved for ALL_PASS filter for regular scan apps.
805            // index 2 is reserved for ALL_PASS filter for batch scan apps.
806            for (int i = 3; i < maxFiltersSupported; ++i) {
807                mFilterIndexStack.add(i);
808            }
809        }
810
811        // Configure filter parameters.
812        private void configureFilterParamter(int clientIf, ScanClient client, int featureSelection,
813                int filterIndex) {
814            int deliveryMode = getDeliveryMode(client);
815            int rssiThreshold = Byte.MIN_VALUE;
816            int timeout = getOnfoundLostTimeout(client);
817            gattClientScanFilterParamAddNative(
818                    clientIf, filterIndex, featureSelection, LIST_LOGIC_TYPE,
819                    FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
820                    timeout, timeout, ONFOUND_SIGHTINGS);
821        }
822
823        // Get delivery mode based on scan settings.
824        private int getDeliveryMode(ScanClient client) {
825            if (client == null) {
826                return DELIVERY_MODE_IMMEDIATE;
827            }
828            ScanSettings settings = client.settings;
829            if (settings == null) {
830                return DELIVERY_MODE_IMMEDIATE;
831            }
832            if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
833                    || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
834                return DELIVERY_MODE_ON_FOUND_LOST;
835            }
836            return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
837                    : DELIVERY_MODE_BATCH;
838        }
839
840        // Get onfound and onlost timeouts in ms
841        private int getOnfoundLostTimeout(ScanClient client) {
842            if (client == null) {
843                return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
844            }
845            ScanSettings settings = client.settings;
846            if (settings == null) {
847                return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
848            }
849            return (int) settings.getReportDelayMillis();
850        }
851
852        /************************** Regular scan related native methods **************************/
853        private native void gattClientScanNative(boolean start);
854
855        private native void gattSetScanParametersNative(int scan_interval,
856                int scan_window);
857
858        /************************** Filter related native methods ********************************/
859        private native void gattClientScanFilterAddNative(int client_if,
860                int filter_type, int filter_index, int company_id,
861                int company_id_mask, long uuid_lsb, long uuid_msb,
862                long uuid_mask_lsb, long uuid_mask_msb, String name,
863                String address, byte addr_type, byte[] data, byte[] mask);
864
865        private native void gattClientScanFilterDeleteNative(int client_if,
866                int filter_type, int filter_index, int company_id,
867                int company_id_mask, long uuid_lsb, long uuid_msb,
868                long uuid_mask_lsb, long uuid_mask_msb, String name,
869                String address, byte addr_type, byte[] data, byte[] mask);
870
871        private native void gattClientScanFilterParamAddNative(
872                int client_if, int filt_index, int feat_seln,
873                int list_logic_type, int filt_logic_type, int rssi_high_thres,
874                int rssi_low_thres, int dely_mode, int found_timeout,
875                int lost_timeout, int found_timeout_cnt);
876
877        // Note this effectively remove scan filters for ALL clients.
878        private native void gattClientScanFilterParamClearAllNative(
879                int client_if);
880
881        private native void gattClientScanFilterParamDeleteNative(
882                int client_if, int filt_index);
883
884        private native void gattClientScanFilterClearNative(int client_if,
885                int filter_index);
886
887        private native void gattClientScanFilterEnableNative(int client_if,
888                boolean enable);
889
890        /************************** Batch related native methods *********************************/
891        private native void gattClientConfigBatchScanStorageNative(int client_if,
892                int max_full_reports_percent, int max_truncated_reports_percent,
893                int notify_threshold_percent);
894
895        private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
896                int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
897
898        private native void gattClientStopBatchScanNative(int client_if);
899
900        private native void gattClientReadScanReportsNative(int client_if, int scan_type);
901    }
902
903    private void logd(String s) {
904        if (DBG) Log.d(TAG, s);
905    }
906}
907