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