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