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