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