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