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                removeScanFilters(client.scannerId);
689                client.stats.setScanTimeout();
690            }
691
692            // The scan should continue for background scans
693            configureRegularScanParams();
694            if (numRegularScanClients() == 0) {
695                if (DBG) Log.d(TAG, "stop scan");
696                gattClientScanNative(false);
697            }
698        }
699
700        void setOpportunisticScanClient(ScanClient client) {
701            // TODO: Add constructor to ScanSettings.Builder
702            // that can copy values from an existing ScanSettings object
703            ScanSettings.Builder builder = new ScanSettings.Builder();
704            ScanSettings settings = client.settings;
705            builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC);
706            builder.setCallbackType(settings.getCallbackType());
707            builder.setScanResultType(settings.getScanResultType());
708            builder.setReportDelay(settings.getReportDelayMillis());
709            builder.setNumOfMatches(settings.getNumOfMatches());
710            client.settings = builder.build();
711        }
712
713        // Find the regular scan client information.
714        ScanClient getRegularScanClient(int scannerId) {
715            for (ScanClient client : mRegularScanClients) {
716              if (client.scannerId == scannerId) return client;
717            }
718            return null;
719        }
720
721        void stopBatchScan(ScanClient client) {
722            mBatchClients.remove(client);
723            removeScanFilters(client.scannerId);
724            if (!isOpportunisticScanClient(client)) {
725                resetBatchScan(client);
726            }
727        }
728
729        void flushBatchResults(int scannerId) {
730            if (DBG) Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId);
731            if (mBatchScanParms.fullScanscannerId != -1) {
732                resetCountDownLatch();
733                gattClientReadScanReportsNative(mBatchScanParms.fullScanscannerId,
734                        SCAN_RESULT_TYPE_FULL);
735                waitForCallback();
736            }
737            if (mBatchScanParms.truncatedScanscannerId != -1) {
738                resetCountDownLatch();
739                gattClientReadScanReportsNative(mBatchScanParms.truncatedScanscannerId,
740                        SCAN_RESULT_TYPE_TRUNCATED);
741                waitForCallback();
742            }
743            setBatchAlarm();
744        }
745
746        void cleanup() {
747            mAlarmManager.cancel(mBatchScanIntervalIntent);
748            // Protect against multiple calls of cleanup.
749            if (mBatchAlarmReceiverRegistered) {
750                mService.unregisterReceiver(mBatchAlarmReceiver);
751            }
752            mBatchAlarmReceiverRegistered = false;
753        }
754
755        private long getBatchTriggerIntervalMillis() {
756            long intervalMillis = Long.MAX_VALUE;
757            for (ScanClient client : mBatchClients) {
758                if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
759                    intervalMillis = Math.min(intervalMillis,
760                            client.settings.getReportDelayMillis());
761                }
762            }
763            return intervalMillis;
764        }
765
766        // Add scan filters. The logic is:
767        // If no offload filter can/needs to be set, set ALL_PASS filter.
768        // Otherwise offload all filters to hardware and enable all filters.
769        private void configureScanFilters(ScanClient client) {
770            int scannerId = client.scannerId;
771            int deliveryMode = getDeliveryMode(client);
772            int trackEntries = 0;
773
774            // Do not add any filters set by opportunistic scan clients
775            if (isOpportunisticScanClient(client)) {
776                return;
777            }
778
779            if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
780                return;
781            }
782
783            resetCountDownLatch();
784            gattClientScanFilterEnableNative(scannerId, true);
785            waitForCallback();
786
787            if (shouldUseAllPassFilter(client)) {
788                int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
789                        ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
790                resetCountDownLatch();
791                // Don't allow Onfound/onlost with all pass
792                configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION,
793                                filterIndex, 0);
794                waitForCallback();
795            } else {
796                Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
797                for (ScanFilter filter : client.filters) {
798                    ScanFilterQueue queue = new ScanFilterQueue();
799                    queue.addScanFilter(filter);
800                    int featureSelection = queue.getFeatureSelection();
801                    int filterIndex = mFilterIndexStack.pop();
802                    while (!queue.isEmpty()) {
803                        resetCountDownLatch();
804                        addFilterToController(scannerId, queue.pop(), filterIndex);
805                        waitForCallback();
806                    }
807                    resetCountDownLatch();
808                    if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) {
809                        trackEntries = getNumOfTrackingAdvertisements(client.settings);
810                        if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) {
811                            Log.e(TAG, "No hardware resources for onfound/onlost filter " +
812                                    trackEntries);
813                            try {
814                                mService.onScanManagerErrorCallback(scannerId,
815                                            ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
816                            } catch (RemoteException e) {
817                                Log.e(TAG, "failed on onScanManagerCallback", e);
818                            }
819                        }
820                    }
821                    configureFilterParamter(scannerId, client, featureSelection, filterIndex,
822                                            trackEntries);
823                    waitForCallback();
824                    clientFilterIndices.add(filterIndex);
825                }
826                mClientFilterIndexMap.put(scannerId, clientFilterIndices);
827            }
828        }
829
830        // Check whether the filter should be added to controller.
831        // Note only on ALL_PASS filter should be added.
832        private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
833            // Not an ALL_PASS client, need to add filter.
834            if (!shouldUseAllPassFilter(client)) {
835                return true;
836            }
837
838            if (deliveryMode == DELIVERY_MODE_BATCH) {
839                mAllPassBatchClients.add(client.scannerId);
840                return mAllPassBatchClients.size() == 1;
841            } else {
842                mAllPassRegularClients.add(client.scannerId);
843                return mAllPassRegularClients.size() == 1;
844            }
845        }
846
847        private void removeScanFilters(int scannerId) {
848            Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId);
849            if (filterIndices != null) {
850                mFilterIndexStack.addAll(filterIndices);
851                for (Integer filterIndex : filterIndices) {
852                    resetCountDownLatch();
853                    gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
854                    waitForCallback();
855                }
856            }
857            // Remove if ALL_PASS filters are used.
858            removeFilterIfExisits(mAllPassRegularClients, scannerId,
859                    ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
860            removeFilterIfExisits(mAllPassBatchClients, scannerId,
861                    ALL_PASS_FILTER_INDEX_BATCH_SCAN);
862        }
863
864        private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) {
865            if (!clients.contains(scannerId)) {
866                return;
867            }
868            clients.remove(scannerId);
869            // Remove ALL_PASS filter iff no app is using it.
870            if (clients.isEmpty()) {
871                resetCountDownLatch();
872                gattClientScanFilterParamDeleteNative(scannerId, filterIndex);
873                waitForCallback();
874            }
875        }
876
877        private ScanClient getBatchScanClient(int scannerId) {
878            for (ScanClient client : mBatchClients) {
879                if (client.scannerId == scannerId) {
880                    return client;
881                }
882            }
883            return null;
884        }
885
886        /**
887         * Return batch scan result type value defined in bt stack.
888         */
889        private int getResultType(BatchScanParams params) {
890            if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) {
891                return SCAN_RESULT_TYPE_BOTH;
892            }
893            if (params.truncatedScanscannerId != -1) {
894                return SCAN_RESULT_TYPE_TRUNCATED;
895            }
896            if (params.fullScanscannerId != -1) {
897                return SCAN_RESULT_TYPE_FULL;
898            }
899            return -1;
900        }
901
902        // Check if ALL_PASS filter should be used for the client.
903        private boolean shouldUseAllPassFilter(ScanClient client) {
904            if (client == null) {
905                return true;
906            }
907            if (client.filters == null || client.filters.isEmpty()) {
908                return true;
909            }
910            return client.filters.size() > mFilterIndexStack.size();
911        }
912
913        private void addFilterToController(int scannerId, ScanFilterQueue.Entry entry,
914                int filterIndex) {
915            if (DBG) Log.d(TAG, "addFilterToController: " + entry.type);
916            switch (entry.type) {
917                case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
918                    if (DBG) Log.d(TAG, "add address " + entry.address);
919                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
920                            0,
921                            "", entry.address, (byte) entry.addr_type, new byte[0], new byte[0]);
922                    break;
923
924                case ScanFilterQueue.TYPE_SERVICE_DATA:
925                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
926                            0,
927                            "", "", (byte) 0, entry.data, entry.data_mask);
928                    break;
929
930                case ScanFilterQueue.TYPE_SERVICE_UUID:
931                case ScanFilterQueue.TYPE_SOLICIT_UUID:
932                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0,
933                            entry.uuid.getLeastSignificantBits(),
934                            entry.uuid.getMostSignificantBits(),
935                            entry.uuid_mask.getLeastSignificantBits(),
936                            entry.uuid_mask.getMostSignificantBits(),
937                            "", "", (byte) 0, new byte[0], new byte[0]);
938                    break;
939
940                case ScanFilterQueue.TYPE_LOCAL_NAME:
941                    if (DBG) Log.d(TAG, "adding filters: " + entry.name);
942                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0,
943                            0,
944                            entry.name, "", (byte) 0, new byte[0], new byte[0]);
945                    break;
946
947                case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
948                    int len = entry.data.length;
949                    if (entry.data_mask.length != len)
950                        return;
951                    gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, entry.company,
952                            entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
953                            entry.data, entry.data_mask);
954                    break;
955            }
956        }
957
958        private void initFilterIndexStack() {
959            int maxFiltersSupported =
960                    AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
961            // Start from index 3 as:
962            // index 0 is reserved for ALL_PASS filter in Settings app.
963            // index 1 is reserved for ALL_PASS filter for regular scan apps.
964            // index 2 is reserved for ALL_PASS filter for batch scan apps.
965            for (int i = 3; i < maxFiltersSupported; ++i) {
966                mFilterIndexStack.add(i);
967            }
968        }
969
970        // Configure filter parameters.
971        private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection,
972                int filterIndex, int numOfTrackingEntries) {
973            int deliveryMode = getDeliveryMode(client);
974            int rssiThreshold = Byte.MIN_VALUE;
975            ScanSettings settings = client.settings;
976            int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true);
977            int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false);
978            int onFoundCount = getOnFoundOnLostSightings(settings);
979            onLostTimeout = 10000;
980            if (DBG) {
981                Log.d(TAG, "configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " "
982                                + onFoundCount + " " + numOfTrackingEntries);
983            }
984            FilterParams FiltValue = new FilterParams(scannerId, filterIndex, featureSelection,
985                    LIST_LOGIC_TYPE, FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
986                    onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries);
987            gattClientScanFilterParamAddNative(FiltValue);
988        }
989
990        // Get delivery mode based on scan settings.
991        private int getDeliveryMode(ScanClient client) {
992            if (client == null) {
993                return DELIVERY_MODE_IMMEDIATE;
994            }
995            ScanSettings settings = client.settings;
996            if (settings == null) {
997                return DELIVERY_MODE_IMMEDIATE;
998            }
999            if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
1000                    || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
1001                return DELIVERY_MODE_ON_FOUND_LOST;
1002            }
1003            return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
1004                    : DELIVERY_MODE_BATCH;
1005        }
1006
1007        private int getScanWindowMillis(ScanSettings settings) {
1008            if (settings == null) {
1009                return SCAN_MODE_LOW_POWER_WINDOW_MS;
1010            }
1011            switch (settings.getScanMode()) {
1012                case ScanSettings.SCAN_MODE_LOW_LATENCY:
1013                    return SCAN_MODE_LOW_LATENCY_WINDOW_MS;
1014                case ScanSettings.SCAN_MODE_BALANCED:
1015                    return SCAN_MODE_BALANCED_WINDOW_MS;
1016                case ScanSettings.SCAN_MODE_LOW_POWER:
1017                    return SCAN_MODE_LOW_POWER_WINDOW_MS;
1018                default:
1019                    return SCAN_MODE_LOW_POWER_WINDOW_MS;
1020            }
1021        }
1022
1023        private int getScanIntervalMillis(ScanSettings settings) {
1024            if (settings == null)
1025                return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1026            switch (settings.getScanMode()) {
1027                case ScanSettings.SCAN_MODE_LOW_LATENCY:
1028                    return SCAN_MODE_LOW_LATENCY_INTERVAL_MS;
1029                case ScanSettings.SCAN_MODE_BALANCED:
1030                    return SCAN_MODE_BALANCED_INTERVAL_MS;
1031                case ScanSettings.SCAN_MODE_LOW_POWER:
1032                    return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1033                default:
1034                    return SCAN_MODE_LOW_POWER_INTERVAL_MS;
1035            }
1036        }
1037
1038        private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) {
1039            int factor;
1040            int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS;
1041
1042            if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1043                factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR;
1044            } else {
1045                factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR;
1046            }
1047            if (!onFound) factor = factor * ONLOST_FACTOR;
1048            return (timeout*factor);
1049        }
1050
1051        private int getOnFoundOnLostSightings(ScanSettings settings) {
1052            if (settings == null)
1053                return ONFOUND_SIGHTINGS_AGGRESSIVE;
1054            if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) {
1055                return ONFOUND_SIGHTINGS_AGGRESSIVE;
1056            } else {
1057                return ONFOUND_SIGHTINGS_STICKY;
1058            }
1059        }
1060
1061        private int getNumOfTrackingAdvertisements(ScanSettings settings) {
1062            if (settings == null)
1063                return 0;
1064            int val=0;
1065            int maxTotalTrackableAdvertisements =
1066                    AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1067            // controller based onfound onlost resources are scarce commodity; the
1068            // assignment of filters to num of beacons to track is configurable based
1069            // on hw capabilities. Apps give an intent and allocation of onfound
1070            // resources or failure there of is done based on availibility - FCFS model
1071            switch (settings.getNumOfMatches()) {
1072                case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT:
1073                    val = 1;
1074                    break;
1075                case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT:
1076                    val = 2;
1077                    break;
1078                case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT:
1079                    val = maxTotalTrackableAdvertisements/2;
1080                    break;
1081                default:
1082                    val = 1;
1083                    if (DBG) {
1084                        Log.d(TAG, "Invalid setting for getNumOfMatches() "
1085                                        + settings.getNumOfMatches());
1086                    }
1087            }
1088            return val;
1089        }
1090
1091        private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement,
1092                            boolean allocate) {
1093            int maxTotalTrackableAdvertisements =
1094                    AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements();
1095            synchronized(curUsedTrackableAdvertisements) {
1096                int availableEntries = maxTotalTrackableAdvertisements
1097                                            - curUsedTrackableAdvertisements;
1098                if (allocate) {
1099                    if (availableEntries >= numOfTrackableAdvertisement) {
1100                        curUsedTrackableAdvertisements += numOfTrackableAdvertisement;
1101                        return true;
1102                    } else {
1103                        return false;
1104                    }
1105                } else {
1106                    if (numOfTrackableAdvertisement > curUsedTrackableAdvertisements) {
1107                        return false;
1108                    } else {
1109                         curUsedTrackableAdvertisements -= numOfTrackableAdvertisement;
1110                         return true;
1111                    }
1112                }
1113            }
1114        }
1115
1116
1117        /************************** Regular scan related native methods **************************/
1118        private native void registerScannerNative(long app_uuid_lsb, long app_uuid_msb);
1119        private native void unregisterScannerNative(int scannerId);
1120
1121        private native void gattClientScanNative(boolean start);
1122
1123        private native void gattSetScanParametersNative(int client_if, int scan_interval,
1124                int scan_window);
1125
1126        /************************** Filter related native methods ********************************/
1127        private native void gattClientScanFilterAddNative(int client_if,
1128                int filter_type, int filter_index, int company_id,
1129                int company_id_mask, long uuid_lsb, long uuid_msb,
1130                long uuid_mask_lsb, long uuid_mask_msb, String name,
1131                String address, byte addr_type, byte[] data, byte[] mask);
1132
1133        private native void gattClientScanFilterDeleteNative(int client_if,
1134                int filter_type, int filter_index, int company_id,
1135                int company_id_mask, long uuid_lsb, long uuid_msb,
1136                long uuid_mask_lsb, long uuid_mask_msb, String name,
1137                String address, byte addr_type, byte[] data, byte[] mask);
1138
1139        private native void gattClientScanFilterParamAddNative(FilterParams FiltValue);
1140
1141        // Note this effectively remove scan filters for ALL clients.
1142        private native void gattClientScanFilterParamClearAllNative(
1143                int client_if);
1144
1145        private native void gattClientScanFilterParamDeleteNative(
1146                int client_if, int filt_index);
1147
1148        private native void gattClientScanFilterClearNative(int client_if,
1149                int filter_index);
1150
1151        private native void gattClientScanFilterEnableNative(int client_if,
1152                boolean enable);
1153
1154        /************************** Batch related native methods *********************************/
1155        private native void gattClientConfigBatchScanStorageNative(int client_if,
1156                int max_full_reports_percent, int max_truncated_reports_percent,
1157                int notify_threshold_percent);
1158
1159        private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
1160                int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
1161
1162        private native void gattClientStopBatchScanNative(int client_if);
1163
1164        private native void gattClientReadScanReportsNative(int client_if, int scan_type);
1165    }
1166}
1167