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