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 (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
    445                 initFilterIndexStack();
    446             }
    447             if (isFilteringSupported()) {
    448                 configureScanFilters(client);
    449             }
    450             // Start scan native only for the first client.
    451             if (mRegularScanClients.size() == 1) {
    452                 gattClientScanNative(true);
    453             }
    454         }
    455 
    456         void startBatchScan(ScanClient client) {
    457             if (mFilterIndexStack.isEmpty() && isFilteringSupported()) {
    458                 initFilterIndexStack();
    459             }
    460             configureScanFilters(client);
    461             // Reset batch scan. May need to stop the existing batch scan and update scan params.
    462             resetBatchScan(client);
    463         }
    464 
    465         private void resetBatchScan(ScanClient client) {
    466             int clientIf = client.clientIf;
    467             BatchScanParams batchScanParams = getBatchScanParams();
    468             // Stop batch if batch scan params changed and previous params is not null.
    469             if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) {
    470                 logd("stopping BLe Batch");
    471                 resetCountDownLatch();
    472                 gattClientStopBatchScanNative(clientIf);
    473                 waitForCallback();
    474                 // Clear pending results as it's illegal to config storage if there are still
    475                 // pending results.
    476                 flushBatchResults(clientIf);
    477             }
    478             // Start batch if batchScanParams changed and current params is not null.
    479             if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) {
    480                 int notifyThreshold = 95;
    481                 logd("Starting BLE batch scan");
    482                 int resultType = getResultType(batchScanParams);
    483                 int fullScanPercent = getFullScanStoragePercent(resultType);
    484                 resetCountDownLatch();
    485                 logd("configuring batch scan storage, appIf " + client.clientIf);
    486                 gattClientConfigBatchScanStorageNative(client.clientIf, fullScanPercent,
    487                         100 - fullScanPercent, notifyThreshold);
    488                 waitForCallback();
    489                 resetCountDownLatch();
    490                 int scanInterval =
    491                         Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode));
    492                 int scanWindow =
    493                         Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode));
    494                 gattClientStartBatchScanNative(clientIf, resultType, scanInterval,
    495                         scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL);
    496                 waitForCallback();
    497             }
    498             mBatchScanParms = batchScanParams;
    499             setBatchAlarm();
    500         }
    501 
    502         private int getFullScanStoragePercent(int resultType) {
    503             switch (resultType) {
    504                 case SCAN_RESULT_TYPE_FULL:
    505                     return 100;
    506                 case SCAN_RESULT_TYPE_TRUNCATED:
    507                     return 0;
    508                 case SCAN_RESULT_TYPE_BOTH:
    509                     return 50;
    510                 default:
    511                     return 50;
    512             }
    513         }
    514 
    515         private BatchScanParams getBatchScanParams() {
    516             if (mBatchClients.isEmpty()) {
    517                 return null;
    518             }
    519             BatchScanParams params = new BatchScanParams();
    520             // TODO: split full batch scan results and truncated batch scan results to different
    521             // collections.
    522             for (ScanClient client : mBatchClients) {
    523                 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode());
    524                 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) {
    525                     params.fullScanClientIf = client.clientIf;
    526                 } else {
    527                     params.truncatedScanClientIf = client.clientIf;
    528                 }
    529             }
    530             return params;
    531         }
    532 
    533         private int getBatchScanWindowMillis(int scanMode) {
    534             switch (scanMode) {
    535                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
    536                     return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS;
    537                 case ScanSettings.SCAN_MODE_BALANCED:
    538                     return SCAN_MODE_BATCH_BALANCED_WINDOW_MS;
    539                 case ScanSettings.SCAN_MODE_LOW_POWER:
    540                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
    541                 default:
    542                     return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS;
    543             }
    544         }
    545 
    546         private int getBatchScanIntervalMillis(int scanMode) {
    547             switch (scanMode) {
    548                 case ScanSettings.SCAN_MODE_LOW_LATENCY:
    549                     return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS;
    550                 case ScanSettings.SCAN_MODE_BALANCED:
    551                     return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS;
    552                 case ScanSettings.SCAN_MODE_LOW_POWER:
    553                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
    554                 default:
    555                     return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS;
    556             }
    557         }
    558 
    559         // Set the batch alarm to be triggered within a short window after batch interval. This
    560         // allows system to optimize wake up time while still allows a degree of precise control.
    561         private void setBatchAlarm() {
    562             // Cancel any pending alarm just in case.
    563             mAlarmManager.cancel(mBatchScanIntervalIntent);
    564             if (mBatchClients.isEmpty()) {
    565                 return;
    566             }
    567             long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis();
    568             // Allows the alarm to be triggered within
    569             // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis]
    570             long windowLengthMillis = batchTriggerIntervalMillis / 10;
    571             long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis;
    572             mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP,
    573                     windowStartMillis, windowLengthMillis,
    574                     mBatchScanIntervalIntent);
    575         }
    576 
    577         void stopRegularScan(ScanClient client) {
    578             // Remove scan filters and recycle filter indices.
    579             removeScanFilters(client.clientIf);
    580             mRegularScanClients.remove(client);
    581             if (mRegularScanClients.isEmpty()) {
    582                 logd("stop scan");
    583                 gattClientScanNative(false);
    584             }
    585         }
    586 
    587         void stopBatchScan(ScanClient client) {
    588             mBatchClients.remove(client);
    589             removeScanFilters(client.clientIf);
    590             resetBatchScan(client);
    591         }
    592 
    593         void flushBatchResults(int clientIf) {
    594             logd("flushPendingBatchResults - clientIf = " + clientIf);
    595             if (mBatchScanParms.fullScanClientIf != -1) {
    596                 resetCountDownLatch();
    597                 gattClientReadScanReportsNative(mBatchScanParms.fullScanClientIf,
    598                         SCAN_RESULT_TYPE_FULL);
    599                 waitForCallback();
    600             }
    601             if (mBatchScanParms.truncatedScanClientIf != -1) {
    602                 resetCountDownLatch();
    603                 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanClientIf,
    604                         SCAN_RESULT_TYPE_TRUNCATED);
    605                 waitForCallback();
    606             }
    607             setBatchAlarm();
    608         }
    609 
    610         void cleanup() {
    611             mAlarmManager.cancel(mBatchScanIntervalIntent);
    612             // Protect against multiple calls of cleanup.
    613             if (mBatchAlarmReceiverRegistered) {
    614                 mService.unregisterReceiver(mBatchAlarmReceiver);
    615             }
    616             mBatchAlarmReceiverRegistered = false;
    617         }
    618 
    619         private long getBatchTriggerIntervalMillis() {
    620             long intervalMillis = Long.MAX_VALUE;
    621             for (ScanClient client : mBatchClients) {
    622                 if (client.settings != null && client.settings.getReportDelayMillis() > 0) {
    623                     intervalMillis = Math.min(intervalMillis,
    624                             client.settings.getReportDelayMillis());
    625                 }
    626             }
    627             return intervalMillis;
    628         }
    629 
    630         // Add scan filters. The logic is:
    631         // If no offload filter can/needs to be set, set ALL_PASS filter.
    632         // Otherwise offload all filters to hardware and enable all filters.
    633         private void configureScanFilters(ScanClient client) {
    634             int clientIf = client.clientIf;
    635             int deliveryMode = getDeliveryMode(client);
    636             if (!shouldAddAllPassFilterToController(client, deliveryMode)) {
    637                 return;
    638             }
    639 
    640             resetCountDownLatch();
    641             gattClientScanFilterEnableNative(clientIf, true);
    642             waitForCallback();
    643 
    644             if (shouldUseAllPassFilter(client)) {
    645                 int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ?
    646                         ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN;
    647                 resetCountDownLatch();
    648                 configureFilterParamter(clientIf, client, ALL_PASS_FILTER_SELECTION, filterIndex);
    649                 waitForCallback();
    650             } else {
    651                 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>();
    652                 for (ScanFilter filter : client.filters) {
    653                     ScanFilterQueue queue = new ScanFilterQueue();
    654                     queue.addScanFilter(filter);
    655                     int featureSelection = queue.getFeatureSelection();
    656                     int filterIndex = mFilterIndexStack.pop();
    657                     while (!queue.isEmpty()) {
    658                         resetCountDownLatch();
    659                         addFilterToController(clientIf, queue.pop(), filterIndex);
    660                         waitForCallback();
    661                     }
    662                     resetCountDownLatch();
    663                     configureFilterParamter(clientIf, client, featureSelection, filterIndex);
    664                     waitForCallback();
    665                     clientFilterIndices.add(filterIndex);
    666                 }
    667                 mClientFilterIndexMap.put(clientIf, clientFilterIndices);
    668             }
    669         }
    670 
    671         // Check whether the filter should be added to controller.
    672         // Note only on ALL_PASS filter should be added.
    673         private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) {
    674             // Not an ALL_PASS client, need to add filter.
    675             if (!shouldUseAllPassFilter(client)) {
    676                 return true;
    677             }
    678 
    679             if (deliveryMode == DELIVERY_MODE_BATCH) {
    680                 mAllPassBatchClients.add(client.clientIf);
    681                 return mAllPassBatchClients.size() == 1;
    682             } else {
    683                 mAllPassRegularClients.add(client.clientIf);
    684                 return mAllPassRegularClients.size() == 1;
    685             }
    686         }
    687 
    688         private void removeScanFilters(int clientIf) {
    689             Deque<Integer> filterIndices = mClientFilterIndexMap.remove(clientIf);
    690             if (filterIndices != null) {
    691                 mFilterIndexStack.addAll(filterIndices);
    692                 for (Integer filterIndex : filterIndices) {
    693                     resetCountDownLatch();
    694                     gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
    695                     waitForCallback();
    696                 }
    697             }
    698             // Remove if ALL_PASS filters are used.
    699             removeFilterIfExisits(mAllPassRegularClients, clientIf,
    700                     ALL_PASS_FILTER_INDEX_REGULAR_SCAN);
    701             removeFilterIfExisits(mAllPassBatchClients, clientIf,
    702                     ALL_PASS_FILTER_INDEX_BATCH_SCAN);
    703         }
    704 
    705         private void removeFilterIfExisits(Set<Integer> clients, int clientIf, int filterIndex) {
    706             if (!clients.contains(clientIf)) {
    707                 return;
    708             }
    709             clients.remove(clientIf);
    710             // Remove ALL_PASS filter iff no app is using it.
    711             if (clients.isEmpty()) {
    712                 resetCountDownLatch();
    713                 gattClientScanFilterParamDeleteNative(clientIf, filterIndex);
    714                 waitForCallback();
    715             }
    716         }
    717 
    718         private ScanClient getBatchScanClient(int clientIf) {
    719             for (ScanClient client : mBatchClients) {
    720                 if (client.clientIf == clientIf) {
    721                     return client;
    722                 }
    723             }
    724             return null;
    725         }
    726 
    727         /**
    728          * Return batch scan result type value defined in bt stack.
    729          */
    730         private int getResultType(BatchScanParams params) {
    731             if (params.fullScanClientIf != -1 && params.truncatedScanClientIf != -1) {
    732                 return SCAN_RESULT_TYPE_BOTH;
    733             }
    734             if (params.truncatedScanClientIf != -1) {
    735                 return SCAN_RESULT_TYPE_TRUNCATED;
    736             }
    737             if (params.fullScanClientIf != -1) {
    738                 return SCAN_RESULT_TYPE_FULL;
    739             }
    740             return -1;
    741         }
    742 
    743         // Check if ALL_PASS filter should be used for the client.
    744         private boolean shouldUseAllPassFilter(ScanClient client) {
    745             if (client == null) {
    746                 return true;
    747             }
    748             if (client.filters == null || client.filters.isEmpty()) {
    749                 return true;
    750             }
    751             return client.filters.size() < mClientFilterIndexMap.size();
    752         }
    753 
    754         private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry,
    755                 int filterIndex) {
    756             logd("addFilterToController: " + entry.type);
    757             switch (entry.type) {
    758                 case ScanFilterQueue.TYPE_DEVICE_ADDRESS:
    759                     logd("add address " + entry.address);
    760                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
    761                             0,
    762                             "", entry.address, (byte) 0, new byte[0], new byte[0]);
    763                     break;
    764 
    765                 case ScanFilterQueue.TYPE_SERVICE_DATA:
    766                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
    767                             0,
    768                             "", "", (byte) 0, entry.data, entry.data_mask);
    769                     break;
    770 
    771                 case ScanFilterQueue.TYPE_SERVICE_UUID:
    772                 case ScanFilterQueue.TYPE_SOLICIT_UUID:
    773                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0,
    774                             entry.uuid.getLeastSignificantBits(),
    775                             entry.uuid.getMostSignificantBits(),
    776                             entry.uuid_mask.getLeastSignificantBits(),
    777                             entry.uuid_mask.getMostSignificantBits(),
    778                             "", "", (byte) 0, new byte[0], new byte[0]);
    779                     break;
    780 
    781                 case ScanFilterQueue.TYPE_LOCAL_NAME:
    782                     logd("adding filters: " + entry.name);
    783                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, 0, 0, 0, 0, 0,
    784                             0,
    785                             entry.name, "", (byte) 0, new byte[0], new byte[0]);
    786                     break;
    787 
    788                 case ScanFilterQueue.TYPE_MANUFACTURER_DATA:
    789                     int len = entry.data.length;
    790                     if (entry.data_mask.length != len)
    791                         return;
    792                     gattClientScanFilterAddNative(clientIf, entry.type, filterIndex, entry.company,
    793                             entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0,
    794                             entry.data, entry.data_mask);
    795                     break;
    796             }
    797         }
    798 
    799         private void initFilterIndexStack() {
    800             int maxFiltersSupported =
    801                     AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported();
    802             // Start from index 3 as:
    803             // index 0 is reserved for ALL_PASS filter in Settings app.
    804             // index 1 is reserved for ALL_PASS filter for regular scan apps.
    805             // index 2 is reserved for ALL_PASS filter for batch scan apps.
    806             for (int i = 3; i < maxFiltersSupported; ++i) {
    807                 mFilterIndexStack.add(i);
    808             }
    809         }
    810 
    811         // Configure filter parameters.
    812         private void configureFilterParamter(int clientIf, ScanClient client, int featureSelection,
    813                 int filterIndex) {
    814             int deliveryMode = getDeliveryMode(client);
    815             int rssiThreshold = Byte.MIN_VALUE;
    816             int timeout = getOnfoundLostTimeout(client);
    817             gattClientScanFilterParamAddNative(
    818                     clientIf, filterIndex, featureSelection, LIST_LOGIC_TYPE,
    819                     FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode,
    820                     timeout, timeout, ONFOUND_SIGHTINGS);
    821         }
    822 
    823         // Get delivery mode based on scan settings.
    824         private int getDeliveryMode(ScanClient client) {
    825             if (client == null) {
    826                 return DELIVERY_MODE_IMMEDIATE;
    827             }
    828             ScanSettings settings = client.settings;
    829             if (settings == null) {
    830                 return DELIVERY_MODE_IMMEDIATE;
    831             }
    832             if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
    833                     || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
    834                 return DELIVERY_MODE_ON_FOUND_LOST;
    835             }
    836             return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE
    837                     : DELIVERY_MODE_BATCH;
    838         }
    839 
    840         // Get onfound and onlost timeouts in ms
    841         private int getOnfoundLostTimeout(ScanClient client) {
    842             if (client == null) {
    843                 return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
    844             }
    845             ScanSettings settings = client.settings;
    846             if (settings == null) {
    847                 return DEFAULT_ONLOST_ONFOUND_TIMEOUT_MILLIS;
    848             }
    849             return (int) settings.getReportDelayMillis();
    850         }
    851 
    852         /************************** Regular scan related native methods **************************/
    853         private native void gattClientScanNative(boolean start);
    854 
    855         private native void gattSetScanParametersNative(int scan_interval,
    856                 int scan_window);
    857 
    858         /************************** Filter related native methods ********************************/
    859         private native void gattClientScanFilterAddNative(int client_if,
    860                 int filter_type, int filter_index, int company_id,
    861                 int company_id_mask, long uuid_lsb, long uuid_msb,
    862                 long uuid_mask_lsb, long uuid_mask_msb, String name,
    863                 String address, byte addr_type, byte[] data, byte[] mask);
    864 
    865         private native void gattClientScanFilterDeleteNative(int client_if,
    866                 int filter_type, int filter_index, int company_id,
    867                 int company_id_mask, long uuid_lsb, long uuid_msb,
    868                 long uuid_mask_lsb, long uuid_mask_msb, String name,
    869                 String address, byte addr_type, byte[] data, byte[] mask);
    870 
    871         private native void gattClientScanFilterParamAddNative(
    872                 int client_if, int filt_index, int feat_seln,
    873                 int list_logic_type, int filt_logic_type, int rssi_high_thres,
    874                 int rssi_low_thres, int dely_mode, int found_timeout,
    875                 int lost_timeout, int found_timeout_cnt);
    876 
    877         // Note this effectively remove scan filters for ALL clients.
    878         private native void gattClientScanFilterParamClearAllNative(
    879                 int client_if);
    880 
    881         private native void gattClientScanFilterParamDeleteNative(
    882                 int client_if, int filt_index);
    883 
    884         private native void gattClientScanFilterClearNative(int client_if,
    885                 int filter_index);
    886 
    887         private native void gattClientScanFilterEnableNative(int client_if,
    888                 boolean enable);
    889 
    890         /************************** Batch related native methods *********************************/
    891         private native void gattClientConfigBatchScanStorageNative(int client_if,
    892                 int max_full_reports_percent, int max_truncated_reports_percent,
    893                 int notify_threshold_percent);
    894 
    895         private native void gattClientStartBatchScanNative(int client_if, int scan_mode,
    896                 int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule);
    897 
    898         private native void gattClientStopBatchScanNative(int client_if);
    899 
    900         private native void gattClientReadScanReportsNative(int client_if, int scan_type);
    901     }
    902 
    903     private void logd(String s) {
    904         if (DBG) Log.d(TAG, s);
    905     }
    906 }
    907