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