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