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.annotation.RequiresPermission; 20 import android.app.ActivityManager; 21 import android.app.AlarmManager; 22 import android.app.PendingIntent; 23 import android.bluetooth.BluetoothA2dp; 24 import android.bluetooth.BluetoothA2dpSink; 25 import android.bluetooth.BluetoothHeadset; 26 import android.bluetooth.BluetoothHeadsetClient; 27 import android.bluetooth.BluetoothHearingAid; 28 import android.bluetooth.BluetoothLeAudio; 29 import android.bluetooth.BluetoothProfile; 30 import android.bluetooth.le.ScanCallback; 31 import android.bluetooth.le.ScanFilter; 32 import android.bluetooth.le.ScanSettings; 33 import android.content.BroadcastReceiver; 34 import android.content.ContentResolver; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.content.IntentFilter; 38 import android.hardware.display.DisplayManager; 39 import android.location.LocationManager; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.RemoteException; 45 import android.os.SystemClock; 46 import android.provider.Settings; 47 import android.util.Log; 48 import android.util.SparseBooleanArray; 49 import android.util.SparseIntArray; 50 import android.view.Display; 51 52 import com.android.bluetooth.Utils; 53 import com.android.bluetooth.btservice.AdapterService; 54 import com.android.bluetooth.btservice.BluetoothAdapterProxy; 55 import com.android.internal.annotations.GuardedBy; 56 import com.android.internal.annotations.VisibleForTesting; 57 58 import java.util.ArrayDeque; 59 import java.util.Collections; 60 import java.util.Deque; 61 import java.util.HashMap; 62 import java.util.HashSet; 63 import java.util.Iterator; 64 import java.util.Map; 65 import java.util.Set; 66 import java.util.UUID; 67 import java.util.concurrent.ConcurrentHashMap; 68 69 /** 70 * Class that handles Bluetooth LE scan related operations. 71 * 72 * @hide 73 */ 74 public class ScanManager { 75 private static final boolean DBG = GattServiceConfig.DBG; 76 private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager"; 77 78 /** 79 * Scan params corresponding to regular scan setting 80 */ 81 private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 140; 82 private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 1400; 83 private static final int SCAN_MODE_BALANCED_WINDOW_MS = 183; 84 private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 730; 85 private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 100; 86 private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 100; 87 public static final int SCAN_MODE_SCREEN_OFF_LOW_POWER_WINDOW_MS = 512; 88 public static final int SCAN_MODE_SCREEN_OFF_LOW_POWER_INTERVAL_MS = 10240; 89 public static final int SCAN_MODE_SCREEN_OFF_BALANCED_WINDOW_MS = 183; 90 public static final int SCAN_MODE_SCREEN_OFF_BALANCED_INTERVAL_MS = 730; 91 92 // Result type defined in bt stack. Need to be accessed by GattService. 93 static final int SCAN_RESULT_TYPE_TRUNCATED = 1; 94 static final int SCAN_RESULT_TYPE_FULL = 2; 95 static final int SCAN_RESULT_TYPE_BOTH = 3; 96 97 // Messages for handling BLE scan operations. 98 @VisibleForTesting 99 static final int MSG_START_BLE_SCAN = 0; 100 static final int MSG_STOP_BLE_SCAN = 1; 101 static final int MSG_FLUSH_BATCH_RESULTS = 2; 102 static final int MSG_SCAN_TIMEOUT = 3; 103 static final int MSG_SUSPEND_SCANS = 4; 104 static final int MSG_RESUME_SCANS = 5; 105 static final int MSG_IMPORTANCE_CHANGE = 6; 106 static final int MSG_SCREEN_ON = 7; 107 static final int MSG_SCREEN_OFF = 8; 108 static final int MSG_REVERT_SCAN_MODE_UPGRADE = 9; 109 static final int MSG_START_CONNECTING = 10; 110 static final int MSG_STOP_CONNECTING = 11; 111 private static final String ACTION_REFRESH_BATCHED_SCAN = 112 "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; 113 114 // Timeout for each controller operation. 115 private static final int OPERATION_TIME_OUT_MILLIS = 500; 116 private static final int MAX_IS_UID_FOREGROUND_MAP_SIZE = 500; 117 118 private int mLastConfiguredScanSetting = Integer.MIN_VALUE; 119 // Scan parameters for batch scan. 120 private BatchScanParams mBatchScanParms; 121 122 private final Object mCurUsedTrackableAdvertisementsLock = new Object(); 123 @GuardedBy("mCurUsedTrackableAdvertisementsLock") 124 private int mCurUsedTrackableAdvertisements = 0; 125 private final GattService mService; 126 private final AdapterService mAdapterService; 127 private BroadcastReceiver mBatchAlarmReceiver; 128 private boolean mBatchAlarmReceiverRegistered; 129 private ScanNative mScanNative; 130 private volatile ClientHandler mHandler; 131 private BluetoothAdapterProxy mBluetoothAdapterProxy; 132 133 private Set<ScanClient> mRegularScanClients; 134 private Set<ScanClient> mBatchClients; 135 private Set<ScanClient> mSuspendedScanClients; 136 private SparseIntArray mPriorityMap = new SparseIntArray(); 137 138 private DisplayManager mDm; 139 140 private ActivityManager mActivityManager; 141 private LocationManager mLocationManager; 142 private static final int FOREGROUND_IMPORTANCE_CUTOFF = 143 ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; 144 private static final boolean DEFAULT_UID_IS_FOREGROUND = true; 145 private static final int SCAN_MODE_APP_IN_BACKGROUND = ScanSettings.SCAN_MODE_LOW_POWER; 146 private final SparseBooleanArray mIsUidForegroundMap = new SparseBooleanArray(); 147 private boolean mScreenOn = false; 148 private boolean mIsConnecting; 149 private int mProfilesConnecting, mProfilesConnected, mProfilesDisconnecting; 150 151 @VisibleForTesting 152 static class UidImportance { 153 public int uid; 154 public int importance; 155 UidImportance(int uid, int importance)156 UidImportance(int uid, int importance) { 157 this.uid = uid; 158 this.importance = importance; 159 } 160 } 161 ScanManager(GattService service, AdapterService adapterService, BluetoothAdapterProxy bluetoothAdapterProxy)162 ScanManager(GattService service, AdapterService adapterService, 163 BluetoothAdapterProxy bluetoothAdapterProxy) { 164 mRegularScanClients = 165 Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); 166 mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); 167 mSuspendedScanClients = 168 Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); 169 mService = service; 170 mAdapterService = adapterService; 171 mScanNative = new ScanNative(); 172 mDm = mService.getSystemService(DisplayManager.class); 173 mActivityManager = mService.getSystemService(ActivityManager.class); 174 mLocationManager = mAdapterService.getSystemService(LocationManager.class); 175 mBluetoothAdapterProxy = bluetoothAdapterProxy; 176 mIsConnecting = false; 177 178 mPriorityMap.put(ScanSettings.SCAN_MODE_OPPORTUNISTIC, 0); 179 mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF, 1); 180 mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_POWER, 2); 181 mPriorityMap.put(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED, 3); 182 // BALANCED and AMBIENT_DISCOVERY now have the same settings and priority. 183 mPriorityMap.put(ScanSettings.SCAN_MODE_BALANCED, 4); 184 mPriorityMap.put(ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY, 4); 185 mPriorityMap.put(ScanSettings.SCAN_MODE_LOW_LATENCY, 5); 186 } 187 start()188 void start() { 189 HandlerThread thread = new HandlerThread("BluetoothScanManager"); 190 thread.start(); 191 mHandler = new ClientHandler(thread.getLooper()); 192 if (mDm != null) { 193 mDm.registerDisplayListener(mDisplayListener, null); 194 } 195 mScreenOn = isScreenOn(); 196 AppScanStats.initScanRadioState(); 197 AppScanStats.setScreenState(mScreenOn); 198 if (mActivityManager != null) { 199 mActivityManager.addOnUidImportanceListener(mUidImportanceListener, 200 FOREGROUND_IMPORTANCE_CUTOFF); 201 } 202 IntentFilter locationIntentFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION); 203 locationIntentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 204 mService.registerReceiver(mLocationReceiver, locationIntentFilter); 205 mService.registerReceiver(mBluetoothConnectionReceiver, 206 getBluetoothConnectionIntentFilter()); 207 } 208 cleanup()209 void cleanup() { 210 mRegularScanClients.clear(); 211 mBatchClients.clear(); 212 mSuspendedScanClients.clear(); 213 mScanNative.cleanup(); 214 215 if (mActivityManager != null) { 216 try { 217 mActivityManager.removeOnUidImportanceListener(mUidImportanceListener); 218 } catch (IllegalArgumentException e) { 219 Log.w(TAG, "exception when invoking removeOnUidImportanceListener", e); 220 } 221 } 222 223 if (mDm != null) { 224 mDm.unregisterDisplayListener(mDisplayListener); 225 } 226 227 if (mHandler != null) { 228 // Shut down the thread 229 mHandler.removeCallbacksAndMessages(null); 230 Looper looper = mHandler.getLooper(); 231 if (looper != null) { 232 looper.quitSafely(); 233 } 234 mHandler = null; 235 } 236 237 try { 238 mService.unregisterReceiver(mLocationReceiver); 239 } catch (IllegalArgumentException e) { 240 Log.w(TAG, "exception when invoking unregisterReceiver(mLocationReceiver)", e); 241 } 242 243 try { 244 mService.unregisterReceiver(mBluetoothConnectionReceiver); 245 } catch (IllegalArgumentException e) { 246 Log.w(TAG, "exception when invoking unregisterReceiver(mBluetoothConnectionReceiver)", 247 e); 248 } 249 } 250 registerScanner(UUID uuid)251 void registerScanner(UUID uuid) { 252 mScanNative.registerScanner(uuid.getLeastSignificantBits(), 253 uuid.getMostSignificantBits()); 254 } 255 unregisterScanner(int scannerId)256 void unregisterScanner(int scannerId) { 257 mScanNative.unregisterScanner(scannerId); 258 } 259 260 /** 261 * Returns the regular scan queue. 262 */ getRegularScanQueue()263 Set<ScanClient> getRegularScanQueue() { 264 return mRegularScanClients; 265 } 266 267 /** 268 * Returns the suspended scan queue. 269 */ getSuspendedScanQueue()270 Set<ScanClient> getSuspendedScanQueue() { 271 return mSuspendedScanClients; 272 } 273 274 /** 275 * Returns batch scan queue. 276 */ getBatchScanQueue()277 Set<ScanClient> getBatchScanQueue() { 278 return mBatchClients; 279 } 280 281 /** 282 * Returns a set of full batch scan clients. 283 */ getFullBatchScanQueue()284 Set<ScanClient> getFullBatchScanQueue() { 285 // TODO: split full batch scan clients and truncated batch clients so we don't need to 286 // construct this every time. 287 Set<ScanClient> fullBatchClients = new HashSet<ScanClient>(); 288 for (ScanClient client : mBatchClients) { 289 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { 290 fullBatchClients.add(client); 291 } 292 } 293 return fullBatchClients; 294 } 295 startScan(ScanClient client)296 void startScan(ScanClient client) { 297 if (DBG) { 298 Log.d(TAG, "startScan() " + client); 299 } 300 sendMessage(MSG_START_BLE_SCAN, client); 301 } 302 stopScan(int scannerId)303 void stopScan(int scannerId) { 304 ScanClient client = mScanNative.getBatchScanClient(scannerId); 305 if (client == null) { 306 client = mScanNative.getRegularScanClient(scannerId); 307 } 308 if (client == null) { 309 client = mScanNative.getSuspendedScanClient(scannerId); 310 } 311 sendMessage(MSG_STOP_BLE_SCAN, client); 312 } 313 flushBatchScanResults(ScanClient client)314 void flushBatchScanResults(ScanClient client) { 315 sendMessage(MSG_FLUSH_BATCH_RESULTS, client); 316 } 317 callbackDone(int scannerId, int status)318 void callbackDone(int scannerId, int status) { 319 mScanNative.callbackDone(scannerId, status); 320 } 321 onConnectingState(boolean isConnecting)322 void onConnectingState(boolean isConnecting) { 323 if (isConnecting) { 324 sendMessage(MSG_START_CONNECTING, null); 325 } else { 326 sendMessage(MSG_STOP_CONNECTING, null); 327 } 328 } 329 getBluetoothConnectionIntentFilter()330 private IntentFilter getBluetoothConnectionIntentFilter() { 331 IntentFilter filter = new IntentFilter(); 332 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 333 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 334 filter.addAction(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED); 335 filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); 336 filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 337 filter.addAction(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); 338 filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); 339 return filter; 340 } 341 sendMessage(int what, ScanClient client)342 private void sendMessage(int what, ScanClient client) { 343 final ClientHandler handler = mHandler; 344 if (handler == null) { 345 Log.d(TAG, "sendMessage: mHandler is null."); 346 return; 347 } 348 Message message = new Message(); 349 message.what = what; 350 message.obj = client; 351 handler.sendMessage(message); 352 } 353 isFilteringSupported()354 private boolean isFilteringSupported() { 355 if (mBluetoothAdapterProxy == null) { 356 Log.e(TAG, "mBluetoothAdapterProxy is null"); 357 return false; 358 } 359 return mBluetoothAdapterProxy.isOffloadedScanFilteringSupported(); 360 } 361 isAutoBatchScanClientEnabled(ScanClient client)362 boolean isAutoBatchScanClientEnabled(ScanClient client) { 363 return mScanNative.isAutoBatchScanClientEnabled(client); 364 } 365 366 // Handler class that handles BLE scan operations. 367 private class ClientHandler extends Handler { 368 ClientHandler(Looper looper)369 ClientHandler(Looper looper) { 370 super(looper); 371 } 372 373 @Override handleMessage(Message msg)374 public void handleMessage(Message msg) { 375 switch (msg.what) { 376 case MSG_START_BLE_SCAN: 377 handleStartScan((ScanClient) msg.obj); 378 break; 379 case MSG_STOP_BLE_SCAN: 380 handleStopScan((ScanClient) msg.obj); 381 break; 382 case MSG_FLUSH_BATCH_RESULTS: 383 handleFlushBatchResults((ScanClient) msg.obj); 384 break; 385 case MSG_SCAN_TIMEOUT: 386 mScanNative.regularScanTimeout((ScanClient) msg.obj); 387 break; 388 case MSG_SUSPEND_SCANS: 389 handleSuspendScans(); 390 break; 391 case MSG_RESUME_SCANS: 392 handleResumeScans(); 393 break; 394 case MSG_SCREEN_OFF: 395 handleScreenOff(); 396 break; 397 case MSG_SCREEN_ON: 398 handleScreenOn(); 399 break; 400 case MSG_REVERT_SCAN_MODE_UPGRADE: 401 revertScanModeUpgrade((ScanClient) msg.obj); 402 break; 403 case MSG_IMPORTANCE_CHANGE: 404 handleImportanceChange((UidImportance) msg.obj); 405 break; 406 case MSG_START_CONNECTING: 407 handleConnectingState(); 408 break; 409 case MSG_STOP_CONNECTING: 410 handleClearConnectingState(); 411 break; 412 default: 413 // Shouldn't happen. 414 Log.e(TAG, "received an unkown message : " + msg.what); 415 } 416 } 417 handleStartScan(ScanClient client)418 void handleStartScan(ScanClient client) { 419 if (DBG) { 420 Log.d(TAG, "handling starting scan"); 421 } 422 423 if (!isScanSupported(client)) { 424 Log.e(TAG, "Scan settings not supported"); 425 return; 426 } 427 428 if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) { 429 Log.e(TAG, "Scan already started"); 430 return; 431 } 432 433 if (requiresScreenOn(client) && !mScreenOn) { 434 Log.w(TAG, "Cannot start unfiltered scan in screen-off. This scan will be resumed " 435 + "later: " + client.scannerId); 436 mSuspendedScanClients.add(client); 437 if (client.stats != null) { 438 client.stats.recordScanSuspend(client.scannerId); 439 } 440 return; 441 } 442 443 final boolean locationEnabled = mLocationManager.isLocationEnabled(); 444 if (requiresLocationOn(client) && !locationEnabled) { 445 Log.i(TAG, "Cannot start unfiltered scan in location-off. This scan will be" 446 + " resumed when location is on: " + client.scannerId); 447 mSuspendedScanClients.add(client); 448 if (client.stats != null) { 449 client.stats.recordScanSuspend(client.scannerId); 450 } 451 return; 452 } 453 454 if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) { 455 if (mScreenOn) { 456 clearAutoBatchScanClient(client); 457 } else { 458 setAutoBatchScanClient(client); 459 } 460 } 461 462 // Begin scan operations. 463 if (isBatchClient(client) || isAutoBatchScanClientEnabled(client)) { 464 mBatchClients.add(client); 465 mScanNative.startBatchScan(client); 466 } else { 467 updateScanModeBeforeStart(client); 468 updateScanModeConcurrency(client); 469 mRegularScanClients.add(client); 470 mScanNative.startRegularScan(client); 471 if (!mScanNative.isOpportunisticScanClient(client)) { 472 mScanNative.configureRegularScanParams(); 473 474 if (!mScanNative.isExemptFromScanTimeout(client)) { 475 Message msg = obtainMessage(MSG_SCAN_TIMEOUT); 476 msg.obj = client; 477 // Only one timeout message should exist at any time 478 sendMessageDelayed(msg, mAdapterService.getScanTimeoutMillis()); 479 if (DBG) { 480 Log.d(TAG, 481 "apply scan timeout (" + mAdapterService.getScanTimeoutMillis() 482 + ")" + "to scannerId " + client.scannerId); 483 } 484 } 485 } 486 } 487 client.started = true; 488 } 489 requiresScreenOn(ScanClient client)490 private boolean requiresScreenOn(ScanClient client) { 491 boolean isFiltered = (client.filters != null) && !client.filters.isEmpty(); 492 return !mScanNative.isOpportunisticScanClient(client) && !isFiltered; 493 } 494 requiresLocationOn(ScanClient client)495 private boolean requiresLocationOn(ScanClient client) { 496 boolean isFiltered = (client.filters != null) && !client.filters.isEmpty(); 497 return !client.hasDisavowedLocation && !isFiltered; 498 } 499 500 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) handleStopScan(ScanClient client)501 void handleStopScan(ScanClient client) { 502 if (client == null) { 503 return; 504 } 505 if (DBG) { 506 Log.d(TAG, "handling stopping scan " + client); 507 } 508 509 if (mSuspendedScanClients.contains(client)) { 510 mSuspendedScanClients.remove(client); 511 } 512 removeMessages(MSG_REVERT_SCAN_MODE_UPGRADE, client); 513 removeMessages(MSG_SCAN_TIMEOUT, client); 514 if (mRegularScanClients.contains(client)) { 515 mScanNative.stopRegularScan(client); 516 517 if (!mScanNative.isOpportunisticScanClient(client)) { 518 mScanNative.configureRegularScanParams(); 519 } 520 } else { 521 if (isAutoBatchScanClientEnabled(client)) { 522 handleFlushBatchResults(client); 523 } 524 mScanNative.stopBatchScan(client); 525 } 526 if (client.appDied) { 527 if (DBG) { 528 Log.d(TAG, "app died, unregister scanner - " + client.scannerId); 529 } 530 mService.unregisterScanner(client.scannerId, mService.getAttributionSource()); 531 } 532 } 533 handleFlushBatchResults(ScanClient client)534 void handleFlushBatchResults(ScanClient client) { 535 if (DBG) { 536 Log.d(TAG, "handleFlushBatchResults() " + client); 537 } 538 if (!mBatchClients.contains(client)) { 539 if (DBG) { 540 Log.d(TAG, "There is no batch scan client to flush " + client); 541 } 542 return; 543 } 544 mScanNative.flushBatchResults(client.scannerId); 545 } 546 isBatchClient(ScanClient client)547 private boolean isBatchClient(ScanClient client) { 548 if (client == null || client.settings == null) { 549 return false; 550 } 551 ScanSettings settings = client.settings; 552 return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES 553 && settings.getReportDelayMillis() != 0; 554 } 555 isScanSupported(ScanClient client)556 private boolean isScanSupported(ScanClient client) { 557 if (client == null || client.settings == null) { 558 return true; 559 } 560 ScanSettings settings = client.settings; 561 if (isFilteringSupported()) { 562 return true; 563 } 564 return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES 565 && settings.getReportDelayMillis() == 0; 566 } 567 handleScreenOff()568 void handleScreenOff() { 569 AppScanStats.setScreenState(false); 570 if (!mScreenOn) { 571 return; 572 } 573 mScreenOn = false; 574 if (DBG) { 575 Log.d(TAG, "handleScreenOff()"); 576 } 577 handleSuspendScans(); 578 updateRegularScanClientsScreenOff(); 579 updateRegularScanToBatchScanClients(); 580 } 581 handleConnectingState()582 void handleConnectingState() { 583 if (mAdapterService.getScanDowngradeDurationMillis() == 0) { 584 return; 585 } 586 boolean updatedScanParams = false; 587 mIsConnecting = true; 588 if (DBG) { 589 Log.d(TAG, "handleConnectingState()"); 590 } 591 for (ScanClient client : mRegularScanClients) { 592 if (downgradeScanModeFromMaxDuty(client)) { 593 updatedScanParams = true; 594 if (DBG) { 595 Log.d(TAG, "scanMode is downgraded by connecting for " + client); 596 } 597 } 598 } 599 if (updatedScanParams) { 600 mScanNative.configureRegularScanParams(); 601 } 602 removeMessages(MSG_STOP_CONNECTING); 603 Message msg = obtainMessage(MSG_STOP_CONNECTING); 604 sendMessageDelayed(msg, mAdapterService.getScanDowngradeDurationMillis()); 605 } 606 handleClearConnectingState()607 void handleClearConnectingState() { 608 if (!mIsConnecting) { 609 Log.e(TAG, "handleClearConnectingState() - not connecting state"); 610 return; 611 } 612 if (DBG) { 613 Log.d(TAG, "handleClearConnectingState()"); 614 } 615 boolean updatedScanParams = false; 616 for (ScanClient client : mRegularScanClients) { 617 if (revertDowngradeScanModeFromMaxDuty(client)) { 618 updatedScanParams = true; 619 if (DBG) { 620 Log.d(TAG, "downgraded scanMode is reverted for " + client); 621 } 622 } 623 } 624 if (updatedScanParams) { 625 mScanNative.configureRegularScanParams(); 626 } 627 removeMessages(MSG_STOP_CONNECTING); 628 mIsConnecting = false; 629 } 630 631 @RequiresPermission(android.Manifest.permission.BLUETOOTH_SCAN) handleSuspendScans()632 void handleSuspendScans() { 633 for (ScanClient client : mRegularScanClients) { 634 if ((requiresScreenOn(client) && !mScreenOn) 635 || (requiresLocationOn(client) && !mLocationManager.isLocationEnabled())) { 636 /*Suspend unfiltered scans*/ 637 if (client.stats != null) { 638 client.stats.recordScanSuspend(client.scannerId); 639 } 640 if (DBG) { 641 Log.d(TAG, "suspend scan " + client); 642 } 643 handleStopScan(client); 644 mSuspendedScanClients.add(client); 645 } 646 } 647 } 648 updateRegularScanToBatchScanClients()649 private void updateRegularScanToBatchScanClients() { 650 boolean updatedScanParams = false; 651 for (ScanClient client : mRegularScanClients) { 652 if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) { 653 if (DBG) { 654 Log.d(TAG, "Updating regular scan to batch scan" + client); 655 } 656 handleStopScan(client); 657 setAutoBatchScanClient(client); 658 handleStartScan(client); 659 updatedScanParams = true; 660 } 661 } 662 if (updatedScanParams) { 663 mScanNative.configureRegularScanParams(); 664 } 665 } 666 updateBatchScanToRegularScanClients()667 private void updateBatchScanToRegularScanClients() { 668 boolean updatedScanParams = false; 669 for (ScanClient client : mBatchClients) { 670 if (!mScanNative.isExemptFromAutoBatchScanUpdate(client)) { 671 if (DBG) { 672 Log.d(TAG, "Updating batch scan to regular scan" + client); 673 } 674 handleStopScan(client); 675 clearAutoBatchScanClient(client); 676 handleStartScan(client); 677 updatedScanParams = true; 678 } 679 } 680 if (updatedScanParams) { 681 mScanNative.configureRegularScanParams(); 682 } 683 } 684 setAutoBatchScanClient(ScanClient client)685 private void setAutoBatchScanClient(ScanClient client) { 686 if (isAutoBatchScanClientEnabled(client)) { 687 return; 688 } 689 client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); 690 if (client.stats != null) { 691 client.stats.setAutoBatchScan(client.scannerId, true); 692 } 693 } 694 clearAutoBatchScanClient(ScanClient client)695 private void clearAutoBatchScanClient(ScanClient client) { 696 if (!isAutoBatchScanClientEnabled(client)) { 697 return; 698 } 699 client.updateScanMode(client.scanModeApp); 700 if (client.stats != null) { 701 client.stats.setAutoBatchScan(client.scannerId, false); 702 } 703 } 704 updateRegularScanClientsScreenOff()705 private void updateRegularScanClientsScreenOff() { 706 boolean updatedScanParams = false; 707 for (ScanClient client : mRegularScanClients) { 708 if (updateScanModeScreenOff(client)) { 709 updatedScanParams = true; 710 if (DBG) { 711 Log.d(TAG, "Scan mode update during screen off" + client); 712 } 713 } 714 } 715 if (updatedScanParams) { 716 mScanNative.configureRegularScanParams(); 717 } 718 } 719 updateScanModeScreenOff(ScanClient client)720 private boolean updateScanModeScreenOff(ScanClient client) { 721 if (mScanNative.isForceDowngradedScanClient(client)) { 722 return false; 723 } 724 if (!isAppForeground(client) && !mScanNative.isOpportunisticScanClient(client)) { 725 return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); 726 } 727 728 // The following codes are effectively only for services 729 // Apps are either already or will be soon handled by handleImportanceChange(). 730 switch (client.scanModeApp) { 731 case ScanSettings.SCAN_MODE_LOW_POWER: 732 return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF); 733 case ScanSettings.SCAN_MODE_BALANCED: 734 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 735 return client.updateScanMode(ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED); 736 case ScanSettings.SCAN_MODE_LOW_LATENCY: 737 return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); 738 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 739 default: 740 return false; 741 } 742 } 743 744 /** 745 * Services and Apps are assumed to be in the foreground by default unless it changes to the 746 * background triggering onUidImportance(). 747 */ isAppForeground(ScanClient client)748 private boolean isAppForeground(ScanClient client) { 749 return mIsUidForegroundMap.get(client.appUid, DEFAULT_UID_IS_FOREGROUND); 750 } 751 updateScanModeBeforeStart(ScanClient client)752 private boolean updateScanModeBeforeStart(ScanClient client) { 753 if (upgradeScanModeBeforeStart(client)) { 754 return true; 755 } 756 if (mScreenOn) { 757 return updateScanModeScreenOn(client); 758 } else { 759 return updateScanModeScreenOff(client); 760 } 761 } 762 updateScanModeConcurrency(ScanClient client)763 private boolean updateScanModeConcurrency(ScanClient client) { 764 if (mIsConnecting) { 765 return downgradeScanModeFromMaxDuty(client); 766 } 767 return false; 768 } 769 upgradeScanModeBeforeStart(ScanClient client)770 private boolean upgradeScanModeBeforeStart(ScanClient client) { 771 if (client.started || mAdapterService.getScanUpgradeDurationMillis() == 0) { 772 return false; 773 } 774 if (client.stats == null || client.stats.hasRecentScan()) { 775 return false; 776 } 777 if (!isAppForeground(client) || isBatchClient(client)) { 778 return false; 779 } 780 781 if (upgradeScanModeByOneLevel(client)) { 782 Message msg = obtainMessage(MSG_REVERT_SCAN_MODE_UPGRADE); 783 msg.obj = client; 784 if (DBG) { 785 Log.d(TAG, "scanMode is upgraded for " + client); 786 } 787 sendMessageDelayed(msg, mAdapterService.getScanUpgradeDurationMillis()); 788 return true; 789 } 790 return false; 791 } 792 upgradeScanModeByOneLevel(ScanClient client)793 private boolean upgradeScanModeByOneLevel(ScanClient client) { 794 switch (client.scanModeApp) { 795 case ScanSettings.SCAN_MODE_LOW_POWER: 796 return client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED); 797 case ScanSettings.SCAN_MODE_BALANCED: 798 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 799 return client.updateScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); 800 case ScanSettings.SCAN_MODE_OPPORTUNISTIC: 801 case ScanSettings.SCAN_MODE_LOW_LATENCY: 802 default: 803 return false; 804 } 805 } 806 revertScanModeUpgrade(ScanClient client)807 void revertScanModeUpgrade(ScanClient client) { 808 if (mPriorityMap.get(client.settings.getScanMode()) 809 <= mPriorityMap.get(client.scanModeApp)) { 810 return; 811 } 812 if (client.updateScanMode(client.scanModeApp)) { 813 if (DBG) { 814 Log.d(TAG, "scanMode upgrade is reverted for " + client); 815 } 816 mScanNative.configureRegularScanParams(); 817 } 818 } 819 updateScanModeScreenOn(ScanClient client)820 private boolean updateScanModeScreenOn(ScanClient client) { 821 if (mScanNative.isForceDowngradedScanClient(client)) { 822 return false; 823 } 824 825 int newScanMode = (isAppForeground(client) 826 || mScanNative.isOpportunisticScanClient(client)) 827 ? client.scanModeApp : SCAN_MODE_APP_IN_BACKGROUND; 828 return client.updateScanMode(newScanMode); 829 } 830 downgradeScanModeFromMaxDuty(ScanClient client)831 private boolean downgradeScanModeFromMaxDuty(ScanClient client) { 832 if (mAdapterService.getScanDowngradeDurationMillis() == 0) { 833 return false; 834 } 835 if (ScanSettings.SCAN_MODE_LOW_LATENCY == client.settings.getScanMode()) { 836 client.updateScanMode(ScanSettings.SCAN_MODE_BALANCED); 837 client.stats.setScanDowngrade(client.scannerId, true); 838 if (DBG) { 839 Log.d(TAG, "downgradeScanModeFromMaxDuty() for " + client); 840 } 841 return true; 842 } 843 return false; 844 } 845 revertDowngradeScanModeFromMaxDuty(ScanClient client)846 private boolean revertDowngradeScanModeFromMaxDuty(ScanClient client) { 847 if (!mScanNative.isDowngradedScanClient(client)) { 848 return false; 849 } 850 client.stats.setScanDowngrade(client.scannerId, false); 851 if (DBG) { 852 Log.d(TAG, "revertDowngradeScanModeFromMaxDuty() for " + client); 853 } 854 if (mScreenOn) { 855 return updateScanModeScreenOn(client); 856 } else { 857 return updateScanModeScreenOff(client); 858 } 859 } 860 handleScreenOn()861 void handleScreenOn() { 862 AppScanStats.setScreenState(true); 863 if (mScreenOn) { 864 return; 865 } 866 mScreenOn = true; 867 if (DBG) { 868 Log.d(TAG, "handleScreenOn()"); 869 } 870 updateBatchScanToRegularScanClients(); 871 handleResumeScans(); 872 updateRegularScanClientsScreenOn(); 873 } 874 handleResumeScans()875 void handleResumeScans() { 876 Iterator<ScanClient> iterator = mSuspendedScanClients.iterator(); 877 while (iterator.hasNext()) { 878 ScanClient client = iterator.next(); 879 if ((!requiresScreenOn(client) || mScreenOn) 880 && (!requiresLocationOn(client) || mLocationManager.isLocationEnabled())) { 881 if (client.stats != null) { 882 client.stats.recordScanResume(client.scannerId); 883 } 884 if (DBG) { 885 Log.d(TAG, "resume scan " + client); 886 } 887 handleStartScan(client); 888 iterator.remove(); 889 } 890 } 891 } 892 updateRegularScanClientsScreenOn()893 private void updateRegularScanClientsScreenOn() { 894 boolean updatedScanParams = false; 895 for (ScanClient client : mRegularScanClients) { 896 if (updateScanModeScreenOn(client)) { 897 updatedScanParams = true; 898 } 899 } 900 if (updatedScanParams) { 901 mScanNative.configureRegularScanParams(); 902 } 903 } 904 } 905 906 /** 907 * Parameters for batch scans. 908 */ 909 class BatchScanParams { 910 public int scanMode; 911 public int fullScanscannerId; 912 public int truncatedScanscannerId; 913 BatchScanParams()914 BatchScanParams() { 915 scanMode = -1; 916 fullScanscannerId = -1; 917 truncatedScanscannerId = -1; 918 } 919 920 @Override equals(Object obj)921 public boolean equals(Object obj) { 922 if (this == obj) { 923 return true; 924 } 925 if (obj == null || getClass() != obj.getClass()) { 926 return false; 927 } 928 BatchScanParams other = (BatchScanParams) obj; 929 return scanMode == other.scanMode && fullScanscannerId == other.fullScanscannerId 930 && truncatedScanscannerId == other.truncatedScanscannerId; 931 932 } 933 } 934 getCurrentUsedTrackingAdvertisement()935 public int getCurrentUsedTrackingAdvertisement() { 936 synchronized (mCurUsedTrackableAdvertisementsLock) { 937 return mCurUsedTrackableAdvertisements; 938 } 939 } 940 941 private class ScanNative { 942 943 // Delivery mode defined in bt stack. 944 private static final int DELIVERY_MODE_IMMEDIATE = 0; 945 private static final int DELIVERY_MODE_ON_FOUND_LOST = 1; 946 private static final int DELIVERY_MODE_BATCH = 2; 947 948 private static final int ONFOUND_SIGHTINGS_AGGRESSIVE = 1; 949 private static final int ONFOUND_SIGHTINGS_STICKY = 4; 950 951 private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1; 952 private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2; 953 private static final int ALL_PASS_FILTER_SELECTION = 0; 954 955 private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0; 956 957 958 /** 959 * Onfound/onlost for scan settings 960 */ 961 private static final int MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR = (1); 962 private static final int MATCH_MODE_STICKY_TIMEOUT_FACTOR = (3); 963 private static final int ONLOST_FACTOR = 2; 964 private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500; 965 966 // The logic is AND for each filter field. 967 private static final int LIST_LOGIC_TYPE = 0x1111111; 968 private static final int FILTER_LOGIC_TYPE = 1; 969 // Filter indices that are available to user. It's sad we need to maintain filter index. 970 private final Deque<Integer> mFilterIndexStack; 971 // Map of scannerId and Filter indices used by client. 972 private final Map<Integer, Deque<Integer>> mClientFilterIndexMap; 973 // Keep track of the clients that uses ALL_PASS filters. 974 private final Set<Integer> mAllPassRegularClients = new HashSet<>(); 975 private final Set<Integer> mAllPassBatchClients = new HashSet<>(); 976 977 private AlarmManager mAlarmManager; 978 private PendingIntent mBatchScanIntervalIntent; 979 private ScanNativeInterface mNativeInterface; 980 ScanNative()981 ScanNative() { 982 mNativeInterface = GattObjectsFactory.getInstance().getScanNativeInterface(); 983 mFilterIndexStack = new ArrayDeque<Integer>(); 984 mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>(); 985 986 mAlarmManager = mService.getSystemService(AlarmManager.class); 987 Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null); 988 mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent, 989 PendingIntent.FLAG_IMMUTABLE); 990 IntentFilter filter = new IntentFilter(); 991 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 992 filter.addAction(ACTION_REFRESH_BATCHED_SCAN); 993 mBatchAlarmReceiver = new BroadcastReceiver() { 994 @Override 995 public void onReceive(Context context, Intent intent) { 996 Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime()); 997 String action = intent.getAction(); 998 999 if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { 1000 if (mBatchClients.isEmpty()) { 1001 return; 1002 } 1003 // Note this actually flushes all pending batch data. 1004 if (mBatchClients.iterator().hasNext()) { 1005 flushBatchScanResults(mBatchClients.iterator().next()); 1006 } 1007 } 1008 } 1009 }; 1010 mService.registerReceiver(mBatchAlarmReceiver, filter); 1011 mBatchAlarmReceiverRegistered = true; 1012 } 1013 callbackDone(int scannerId, int status)1014 private void callbackDone(int scannerId, int status) { 1015 if (DBG) { 1016 Log.d(TAG, "callback done for scannerId - " + scannerId + " status - " + status); 1017 } 1018 if (status == 0) { 1019 mNativeInterface.callbackDone(); 1020 } 1021 // TODO: add a callback for scan failure. 1022 } 1023 resetCountDownLatch()1024 private void resetCountDownLatch() { 1025 mNativeInterface.resetCountDownLatch(); 1026 } 1027 waitForCallback()1028 private boolean waitForCallback() { 1029 return mNativeInterface.waitForCallback(OPERATION_TIME_OUT_MILLIS); 1030 } 1031 configureRegularScanParams()1032 void configureRegularScanParams() { 1033 if (DBG) { 1034 Log.d(TAG, "configureRegularScanParams() - queue=" + mRegularScanClients.size()); 1035 } 1036 int curScanSetting = Integer.MIN_VALUE; 1037 ScanClient client = getAggressiveClient(mRegularScanClients); 1038 if (client != null) { 1039 curScanSetting = client.settings.getScanMode(); 1040 } 1041 1042 if (curScanSetting != Integer.MIN_VALUE 1043 && curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { 1044 if (curScanSetting != mLastConfiguredScanSetting) { 1045 int scanWindowMs = getScanWindowMillis(client.settings); 1046 int scanIntervalMs = getScanIntervalMillis(client.settings); 1047 1048 // convert scanWindow and scanInterval from ms to LE scan units(0.625ms) 1049 int scanWindow = Utils.millsToUnit(scanWindowMs); 1050 int scanInterval = Utils.millsToUnit(scanIntervalMs); 1051 mNativeInterface.gattClientScan(false); 1052 if (!AppScanStats.recordScanRadioStop()) { 1053 Log.w(TAG, "There is no scan radio to stop"); 1054 } 1055 if (DBG) { 1056 Log.d(TAG, "Start gattClientScanNative with" 1057 + " old scanMode " + mLastConfiguredScanSetting 1058 + " new scanMode " + curScanSetting 1059 + " ( in MS: " + scanIntervalMs + " / " + scanWindowMs 1060 + ", in scan unit: " + scanInterval + " / " + scanWindow + " )" 1061 + client); 1062 } 1063 mNativeInterface.gattSetScanParameters(client.scannerId, scanInterval, 1064 scanWindow); 1065 mNativeInterface.gattClientScan(true); 1066 if (!AppScanStats.recordScanRadioStart(curScanSetting)) { 1067 Log.w(TAG, "Scan radio already started"); 1068 } 1069 mLastConfiguredScanSetting = curScanSetting; 1070 } 1071 } else { 1072 mLastConfiguredScanSetting = curScanSetting; 1073 if (DBG) { 1074 Log.d(TAG, "configureRegularScanParams() - queue empty, scan stopped"); 1075 } 1076 } 1077 } 1078 getAggressiveClient(Set<ScanClient> cList)1079 ScanClient getAggressiveClient(Set<ScanClient> cList) { 1080 ScanClient result = null; 1081 int currentScanModePriority = Integer.MIN_VALUE; 1082 for (ScanClient client : cList) { 1083 int priority = mPriorityMap.get(client.settings.getScanMode()); 1084 if (priority > currentScanModePriority) { 1085 result = client; 1086 currentScanModePriority = priority; 1087 } 1088 } 1089 return result; 1090 } 1091 startRegularScan(ScanClient client)1092 void startRegularScan(ScanClient client) { 1093 if (isFilteringSupported() && mFilterIndexStack.isEmpty() 1094 && mClientFilterIndexMap.isEmpty()) { 1095 initFilterIndexStack(); 1096 } 1097 if (isFilteringSupported()) { 1098 configureScanFilters(client); 1099 } 1100 // Start scan native only for the first client. 1101 if (numRegularScanClients() == 1 1102 && client.settings != null 1103 && client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { 1104 if (DBG) { 1105 Log.d(TAG, "start gattClientScanNative from startRegularScan()"); 1106 } 1107 mNativeInterface.gattClientScan(true); 1108 if (!AppScanStats.recordScanRadioStart(client.settings.getScanMode())) { 1109 Log.w(TAG, "Scan radio already started"); 1110 } 1111 } 1112 } 1113 numRegularScanClients()1114 private int numRegularScanClients() { 1115 int num = 0; 1116 for (ScanClient client : mRegularScanClients) { 1117 if (client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { 1118 num++; 1119 } 1120 } 1121 return num; 1122 } 1123 startBatchScan(ScanClient client)1124 void startBatchScan(ScanClient client) { 1125 if (mFilterIndexStack.isEmpty() && isFilteringSupported()) { 1126 initFilterIndexStack(); 1127 } 1128 configureScanFilters(client); 1129 if (!isOpportunisticScanClient(client)) { 1130 // Reset batch scan. May need to stop the existing batch scan and update scan 1131 // params. 1132 resetBatchScan(client); 1133 } 1134 } 1135 isExemptFromScanTimeout(ScanClient client)1136 private boolean isExemptFromScanTimeout(ScanClient client) { 1137 return isOpportunisticScanClient(client) || isFirstMatchScanClient(client); 1138 } 1139 isExemptFromAutoBatchScanUpdate(ScanClient client)1140 private boolean isExemptFromAutoBatchScanUpdate(ScanClient client) { 1141 return isOpportunisticScanClient(client) || !isAllMatchesAutoBatchScanClient(client); 1142 } 1143 isAutoBatchScanClientEnabled(ScanClient client)1144 private boolean isAutoBatchScanClientEnabled(ScanClient client) { 1145 return client.stats != null && client.stats.isAutoBatchScan(client.scannerId); 1146 } 1147 isAllMatchesAutoBatchScanClient(ScanClient client)1148 private boolean isAllMatchesAutoBatchScanClient(ScanClient client) { 1149 return client.settings.getCallbackType() 1150 == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH; 1151 } 1152 isOpportunisticScanClient(ScanClient client)1153 private boolean isOpportunisticScanClient(ScanClient client) { 1154 return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; 1155 } 1156 isTimeoutScanClient(ScanClient client)1157 private boolean isTimeoutScanClient(ScanClient client) { 1158 return (client.stats != null) && client.stats.isScanTimeout(client.scannerId); 1159 } 1160 isDowngradedScanClient(ScanClient client)1161 private boolean isDowngradedScanClient(ScanClient client) { 1162 return (client.stats != null) && client.stats.isScanDowngraded(client.scannerId); 1163 } 1164 isForceDowngradedScanClient(ScanClient client)1165 private boolean isForceDowngradedScanClient(ScanClient client) { 1166 return isTimeoutScanClient(client) || isDowngradedScanClient(client); 1167 } 1168 isFirstMatchScanClient(ScanClient client)1169 private boolean isFirstMatchScanClient(ScanClient client) { 1170 return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) 1171 != 0; 1172 } 1173 resetBatchScan(ScanClient client)1174 private void resetBatchScan(ScanClient client) { 1175 int scannerId = client.scannerId; 1176 BatchScanParams batchScanParams = getBatchScanParams(); 1177 // Stop batch if batch scan params changed and previous params is not null. 1178 if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) { 1179 if (DBG) { 1180 Log.d(TAG, "stopping BLe Batch"); 1181 } 1182 resetCountDownLatch(); 1183 mNativeInterface.gattClientStopBatchScan(scannerId); 1184 waitForCallback(); 1185 // Clear pending results as it's illegal to config storage if there are still 1186 // pending results. 1187 flushBatchResults(scannerId); 1188 } 1189 // Start batch if batchScanParams changed and current params is not null. 1190 if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) { 1191 int notifyThreshold = 95; 1192 if (DBG) { 1193 Log.d(TAG, "Starting BLE batch scan"); 1194 } 1195 int resultType = getResultType(batchScanParams); 1196 int fullScanPercent = getFullScanStoragePercent(resultType); 1197 resetCountDownLatch(); 1198 if (DBG) { 1199 Log.d(TAG, "configuring batch scan storage, appIf " + client.scannerId); 1200 } 1201 mNativeInterface.gattClientConfigBatchScanStorage(client.scannerId, fullScanPercent, 1202 100 - fullScanPercent, notifyThreshold); 1203 waitForCallback(); 1204 resetCountDownLatch(); 1205 int scanInterval = 1206 Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode)); 1207 int scanWindow = 1208 Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode)); 1209 mNativeInterface.gattClientStartBatchScan(scannerId, resultType, scanInterval, 1210 scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL); 1211 waitForCallback(); 1212 } 1213 mBatchScanParms = batchScanParams; 1214 setBatchAlarm(); 1215 } 1216 getFullScanStoragePercent(int resultType)1217 private int getFullScanStoragePercent(int resultType) { 1218 switch (resultType) { 1219 case SCAN_RESULT_TYPE_FULL: 1220 return 100; 1221 case SCAN_RESULT_TYPE_TRUNCATED: 1222 return 0; 1223 case SCAN_RESULT_TYPE_BOTH: 1224 return 50; 1225 default: 1226 return 50; 1227 } 1228 } 1229 getBatchScanParams()1230 private BatchScanParams getBatchScanParams() { 1231 if (mBatchClients.isEmpty()) { 1232 return null; 1233 } 1234 BatchScanParams params = new BatchScanParams(); 1235 ScanClient winner = getAggressiveClient(mBatchClients); 1236 if (winner != null) { 1237 params.scanMode = winner.settings.getScanMode(); 1238 } 1239 // TODO: split full batch scan results and truncated batch scan results to different 1240 // collections. 1241 for (ScanClient client : mBatchClients) { 1242 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { 1243 params.fullScanscannerId = client.scannerId; 1244 } else { 1245 params.truncatedScanscannerId = client.scannerId; 1246 } 1247 } 1248 return params; 1249 } 1250 1251 // Batched scan doesn't require high duty cycle scan because scan result is reported 1252 // infrequently anyway. To avoid redefining paramete sets, map to the low duty cycle 1253 // parameter set as follows. getBatchScanWindowMillis(int scanMode)1254 private int getBatchScanWindowMillis(int scanMode) { 1255 ContentResolver resolver = mService.getContentResolver(); 1256 switch (scanMode) { 1257 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1258 return Settings.Global.getInt( 1259 resolver, 1260 Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS, 1261 SCAN_MODE_BALANCED_WINDOW_MS); 1262 case ScanSettings.SCAN_MODE_SCREEN_OFF: 1263 return mAdapterService.getScreenOffLowPowerWindowMillis(); 1264 default: 1265 return Settings.Global.getInt( 1266 resolver, 1267 Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, 1268 SCAN_MODE_LOW_POWER_WINDOW_MS); 1269 } 1270 } 1271 getBatchScanIntervalMillis(int scanMode)1272 private int getBatchScanIntervalMillis(int scanMode) { 1273 ContentResolver resolver = mService.getContentResolver(); 1274 switch (scanMode) { 1275 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1276 return Settings.Global.getInt( 1277 resolver, 1278 Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS, 1279 SCAN_MODE_BALANCED_INTERVAL_MS); 1280 case ScanSettings.SCAN_MODE_SCREEN_OFF: 1281 return mAdapterService.getScreenOffLowPowerIntervalMillis(); 1282 default: 1283 return Settings.Global.getInt( 1284 resolver, 1285 Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, 1286 SCAN_MODE_LOW_POWER_INTERVAL_MS); 1287 } 1288 } 1289 1290 // Set the batch alarm to be triggered within a short window after batch interval. This 1291 // allows system to optimize wake up time while still allows a degree of precise control. setBatchAlarm()1292 private void setBatchAlarm() { 1293 // Cancel any pending alarm just in case. 1294 mAlarmManager.cancel(mBatchScanIntervalIntent); 1295 if (mBatchClients.isEmpty()) { 1296 return; 1297 } 1298 long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis(); 1299 // Allows the alarm to be triggered within 1300 // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis] 1301 long windowLengthMillis = batchTriggerIntervalMillis / 10; 1302 long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis; 1303 mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, windowStartMillis, 1304 windowLengthMillis, mBatchScanIntervalIntent); 1305 } 1306 stopRegularScan(ScanClient client)1307 void stopRegularScan(ScanClient client) { 1308 // Remove scan filters and recycle filter indices. 1309 if (client == null) { 1310 return; 1311 } 1312 int deliveryMode = getDeliveryMode(client); 1313 if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) { 1314 for (ScanFilter filter : client.filters) { 1315 int entriesToFree = getNumOfTrackingAdvertisements(client.settings); 1316 if (!manageAllocationOfTrackingAdvertisement(entriesToFree, false)) { 1317 Log.e(TAG, "Error freeing for onfound/onlost filter resources " 1318 + entriesToFree); 1319 try { 1320 mService.onScanManagerErrorCallback(client.scannerId, 1321 ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 1322 } catch (RemoteException e) { 1323 Log.e(TAG, "failed on onScanManagerCallback at freeing", e); 1324 } 1325 } 1326 } 1327 } 1328 mRegularScanClients.remove(client); 1329 if (numRegularScanClients() == 0) { 1330 if (DBG) { 1331 Log.d(TAG, "stop gattClientScanNative"); 1332 } 1333 mNativeInterface.gattClientScan(false); 1334 if (!AppScanStats.recordScanRadioStop()) { 1335 Log.w(TAG, "There is no scan radio to stop"); 1336 } 1337 } 1338 removeScanFilters(client.scannerId); 1339 } 1340 regularScanTimeout(ScanClient client)1341 void regularScanTimeout(ScanClient client) { 1342 if (!isExemptFromScanTimeout(client) && client.stats.isScanningTooLong()) { 1343 if (DBG) { 1344 Log.d(TAG, "regularScanTimeout - client scan time was too long"); 1345 } 1346 if (client.filters == null || client.filters.isEmpty()) { 1347 Log.w(TAG, 1348 "Moving unfiltered scan client to opportunistic scan (scannerId " 1349 + client.scannerId + ")"); 1350 setOpportunisticScanClient(client); 1351 removeScanFilters(client.scannerId); 1352 client.stats.setScanTimeout(client.scannerId); 1353 } else { 1354 Log.w(TAG, 1355 "Moving filtered scan client to downgraded scan (scannerId " 1356 + client.scannerId + ")"); 1357 client.updateScanMode(ScanSettings.SCAN_MODE_LOW_POWER); 1358 client.stats.setScanTimeout(client.scannerId); 1359 } 1360 } 1361 1362 // The scan should continue for background scans 1363 configureRegularScanParams(); 1364 if (numRegularScanClients() == 0) { 1365 if (DBG) { 1366 Log.d(TAG, "stop gattClientScanNative"); 1367 } 1368 mNativeInterface.gattClientScan(false); 1369 if (!AppScanStats.recordScanRadioStop()) { 1370 Log.w(TAG, "There is no scan radio to stop"); 1371 } 1372 } 1373 } 1374 setOpportunisticScanClient(ScanClient client)1375 void setOpportunisticScanClient(ScanClient client) { 1376 // TODO: Add constructor to ScanSettings.Builder 1377 // that can copy values from an existing ScanSettings object 1378 ScanSettings.Builder builder = new ScanSettings.Builder(); 1379 ScanSettings settings = client.settings; 1380 builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC); 1381 builder.setCallbackType(settings.getCallbackType()); 1382 builder.setScanResultType(settings.getScanResultType()); 1383 builder.setReportDelay(settings.getReportDelayMillis()); 1384 builder.setNumOfMatches(settings.getNumOfMatches()); 1385 client.settings = builder.build(); 1386 } 1387 1388 // Find the regular scan client information. getRegularScanClient(int scannerId)1389 ScanClient getRegularScanClient(int scannerId) { 1390 for (ScanClient client : mRegularScanClients) { 1391 if (client.scannerId == scannerId) { 1392 return client; 1393 } 1394 } 1395 return null; 1396 } 1397 getSuspendedScanClient(int scannerId)1398 ScanClient getSuspendedScanClient(int scannerId) { 1399 for (ScanClient client : mSuspendedScanClients) { 1400 if (client.scannerId == scannerId) { 1401 return client; 1402 } 1403 } 1404 return null; 1405 } 1406 stopBatchScan(ScanClient client)1407 void stopBatchScan(ScanClient client) { 1408 mBatchClients.remove(client); 1409 removeScanFilters(client.scannerId); 1410 if (!isOpportunisticScanClient(client)) { 1411 resetBatchScan(client); 1412 } 1413 } 1414 flushBatchResults(int scannerId)1415 void flushBatchResults(int scannerId) { 1416 if (DBG) { 1417 Log.d(TAG, "flushPendingBatchResults - scannerId = " + scannerId); 1418 } 1419 if (mBatchScanParms.fullScanscannerId != -1) { 1420 resetCountDownLatch(); 1421 mNativeInterface.gattClientReadScanReports(mBatchScanParms.fullScanscannerId, 1422 SCAN_RESULT_TYPE_FULL); 1423 waitForCallback(); 1424 } 1425 if (mBatchScanParms.truncatedScanscannerId != -1) { 1426 resetCountDownLatch(); 1427 mNativeInterface.gattClientReadScanReports(mBatchScanParms.truncatedScanscannerId, 1428 SCAN_RESULT_TYPE_TRUNCATED); 1429 waitForCallback(); 1430 } 1431 setBatchAlarm(); 1432 } 1433 cleanup()1434 void cleanup() { 1435 mAlarmManager.cancel(mBatchScanIntervalIntent); 1436 // Protect against multiple calls of cleanup. 1437 if (mBatchAlarmReceiverRegistered) { 1438 mService.unregisterReceiver(mBatchAlarmReceiver); 1439 } 1440 mBatchAlarmReceiverRegistered = false; 1441 } 1442 getBatchTriggerIntervalMillis()1443 private long getBatchTriggerIntervalMillis() { 1444 long intervalMillis = Long.MAX_VALUE; 1445 for (ScanClient client : mBatchClients) { 1446 if (client.settings != null && client.settings.getReportDelayMillis() > 0) { 1447 intervalMillis = 1448 Math.min(intervalMillis, client.settings.getReportDelayMillis()); 1449 } 1450 } 1451 return intervalMillis; 1452 } 1453 1454 // Add scan filters. The logic is: 1455 // If no offload filter can/needs to be set, set ALL_PASS filter. 1456 // Otherwise offload all filters to hardware and enable all filters. configureScanFilters(ScanClient client)1457 private void configureScanFilters(ScanClient client) { 1458 int scannerId = client.scannerId; 1459 int deliveryMode = getDeliveryMode(client); 1460 int trackEntries = 0; 1461 1462 // Do not add any filters set by opportunistic scan clients 1463 if (isOpportunisticScanClient(client)) { 1464 return; 1465 } 1466 1467 if (!shouldAddAllPassFilterToController(client, deliveryMode)) { 1468 return; 1469 } 1470 1471 resetCountDownLatch(); 1472 mNativeInterface.gattClientScanFilterEnable(scannerId, true); 1473 waitForCallback(); 1474 1475 if (shouldUseAllPassFilter(client)) { 1476 int filterIndex = 1477 (deliveryMode == DELIVERY_MODE_BATCH) ? ALL_PASS_FILTER_INDEX_BATCH_SCAN 1478 : ALL_PASS_FILTER_INDEX_REGULAR_SCAN; 1479 resetCountDownLatch(); 1480 // Don't allow Onfound/onlost with all pass 1481 configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION, filterIndex, 1482 0); 1483 waitForCallback(); 1484 } else { 1485 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>(); 1486 for (ScanFilter filter : client.filters) { 1487 ScanFilterQueue queue = new ScanFilterQueue(); 1488 queue.addScanFilter(filter); 1489 int featureSelection = queue.getFeatureSelection(); 1490 int filterIndex = mFilterIndexStack.pop(); 1491 1492 resetCountDownLatch(); 1493 mNativeInterface.gattClientScanFilterAdd(scannerId, queue.toArray(), 1494 filterIndex); 1495 waitForCallback(); 1496 1497 resetCountDownLatch(); 1498 if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) { 1499 trackEntries = getNumOfTrackingAdvertisements(client.settings); 1500 if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) { 1501 Log.e(TAG, "No hardware resources for onfound/onlost filter " 1502 + trackEntries); 1503 try { 1504 mService.onScanManagerErrorCallback(scannerId, 1505 ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 1506 } catch (RemoteException e) { 1507 Log.e(TAG, "failed on onScanManagerCallback", e); 1508 } 1509 } 1510 } 1511 configureFilterParamter(scannerId, client, featureSelection, filterIndex, 1512 trackEntries); 1513 waitForCallback(); 1514 clientFilterIndices.add(filterIndex); 1515 } 1516 mClientFilterIndexMap.put(scannerId, clientFilterIndices); 1517 } 1518 } 1519 1520 // Check whether the filter should be added to controller. 1521 // Note only on ALL_PASS filter should be added. shouldAddAllPassFilterToController(ScanClient client, int deliveryMode)1522 private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) { 1523 // Not an ALL_PASS client, need to add filter. 1524 if (!shouldUseAllPassFilter(client)) { 1525 return true; 1526 } 1527 1528 if (deliveryMode == DELIVERY_MODE_BATCH) { 1529 mAllPassBatchClients.add(client.scannerId); 1530 return mAllPassBatchClients.size() == 1; 1531 } else { 1532 mAllPassRegularClients.add(client.scannerId); 1533 return mAllPassRegularClients.size() == 1; 1534 } 1535 } 1536 removeScanFilters(int scannerId)1537 private void removeScanFilters(int scannerId) { 1538 Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId); 1539 if (filterIndices != null) { 1540 mFilterIndexStack.addAll(filterIndices); 1541 for (Integer filterIndex : filterIndices) { 1542 resetCountDownLatch(); 1543 mNativeInterface.gattClientScanFilterParamDelete(scannerId, filterIndex); 1544 waitForCallback(); 1545 } 1546 } 1547 // Remove if ALL_PASS filters are used. 1548 removeFilterIfExisits(mAllPassRegularClients, scannerId, 1549 ALL_PASS_FILTER_INDEX_REGULAR_SCAN); 1550 removeFilterIfExisits(mAllPassBatchClients, scannerId, 1551 ALL_PASS_FILTER_INDEX_BATCH_SCAN); 1552 } 1553 removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex)1554 private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) { 1555 if (!clients.contains(scannerId)) { 1556 return; 1557 } 1558 clients.remove(scannerId); 1559 // Remove ALL_PASS filter iff no app is using it. 1560 if (clients.isEmpty()) { 1561 resetCountDownLatch(); 1562 mNativeInterface.gattClientScanFilterParamDelete(scannerId, filterIndex); 1563 waitForCallback(); 1564 } 1565 } 1566 getBatchScanClient(int scannerId)1567 private ScanClient getBatchScanClient(int scannerId) { 1568 for (ScanClient client : mBatchClients) { 1569 if (client.scannerId == scannerId) { 1570 return client; 1571 } 1572 } 1573 return null; 1574 } 1575 1576 /** 1577 * Return batch scan result type value defined in bt stack. 1578 */ getResultType(BatchScanParams params)1579 private int getResultType(BatchScanParams params) { 1580 if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) { 1581 return SCAN_RESULT_TYPE_BOTH; 1582 } 1583 if (params.truncatedScanscannerId != -1) { 1584 return SCAN_RESULT_TYPE_TRUNCATED; 1585 } 1586 if (params.fullScanscannerId != -1) { 1587 return SCAN_RESULT_TYPE_FULL; 1588 } 1589 return -1; 1590 } 1591 1592 // Check if ALL_PASS filter should be used for the client. shouldUseAllPassFilter(ScanClient client)1593 private boolean shouldUseAllPassFilter(ScanClient client) { 1594 if (client == null) { 1595 return true; 1596 } 1597 if (client.filters == null || client.filters.isEmpty()) { 1598 return true; 1599 } 1600 return client.filters.size() > mFilterIndexStack.size(); 1601 } 1602 initFilterIndexStack()1603 private void initFilterIndexStack() { 1604 int maxFiltersSupported = 1605 AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported(); 1606 // Start from index 4 as: 1607 // index 0 is reserved for ALL_PASS filter in Settings app. 1608 // index 1 is reserved for ALL_PASS filter for regular scan apps. 1609 // index 2 is reserved for ALL_PASS filter for batch scan apps. 1610 // index 3 is reserved for BAP/CAP Announcements 1611 for (int i = 4; i < maxFiltersSupported; ++i) { 1612 mFilterIndexStack.add(i); 1613 } 1614 } 1615 1616 // Configure filter parameters. configureFilterParamter(int scannerId, ScanClient client, int featureSelection, int filterIndex, int numOfTrackingEntries)1617 private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection, 1618 int filterIndex, int numOfTrackingEntries) { 1619 int deliveryMode = getDeliveryMode(client); 1620 int rssiThreshold = Byte.MIN_VALUE; 1621 ScanSettings settings = client.settings; 1622 int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true); 1623 int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false); 1624 int onFoundCount = getOnFoundOnLostSightings(settings); 1625 onLostTimeout = 10000; 1626 if (DBG) { 1627 Log.d(TAG, "configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " " 1628 + onFoundCount + " " + numOfTrackingEntries); 1629 } 1630 FilterParams filtValue = 1631 new FilterParams(scannerId, filterIndex, featureSelection, LIST_LOGIC_TYPE, 1632 FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode, 1633 onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries); 1634 mNativeInterface.gattClientScanFilterParamAdd(filtValue); 1635 } 1636 1637 // Get delivery mode based on scan settings. getDeliveryMode(ScanClient client)1638 private int getDeliveryMode(ScanClient client) { 1639 if (client == null) { 1640 return DELIVERY_MODE_IMMEDIATE; 1641 } 1642 ScanSettings settings = client.settings; 1643 if (settings == null) { 1644 return DELIVERY_MODE_IMMEDIATE; 1645 } 1646 if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 1647 || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { 1648 return DELIVERY_MODE_ON_FOUND_LOST; 1649 } 1650 if (isAllMatchesAutoBatchScanClient(client)) { 1651 return isAutoBatchScanClientEnabled(client) ? DELIVERY_MODE_BATCH 1652 : DELIVERY_MODE_IMMEDIATE; 1653 } 1654 return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE 1655 : DELIVERY_MODE_BATCH; 1656 } 1657 getScanWindowMillis(ScanSettings settings)1658 private int getScanWindowMillis(ScanSettings settings) { 1659 ContentResolver resolver = mService.getContentResolver(); 1660 if (settings == null) { 1661 return Settings.Global.getInt( 1662 resolver, 1663 Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, 1664 SCAN_MODE_LOW_POWER_WINDOW_MS); 1665 } 1666 1667 switch (settings.getScanMode()) { 1668 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1669 return Settings.Global.getInt( 1670 resolver, 1671 Settings.Global.BLE_SCAN_LOW_LATENCY_WINDOW_MS, 1672 SCAN_MODE_LOW_LATENCY_WINDOW_MS); 1673 case ScanSettings.SCAN_MODE_BALANCED: 1674 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 1675 return Settings.Global.getInt( 1676 resolver, 1677 Settings.Global.BLE_SCAN_BALANCED_WINDOW_MS, 1678 SCAN_MODE_BALANCED_WINDOW_MS); 1679 case ScanSettings.SCAN_MODE_LOW_POWER: 1680 return Settings.Global.getInt( 1681 resolver, 1682 Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, 1683 SCAN_MODE_LOW_POWER_WINDOW_MS); 1684 case ScanSettings.SCAN_MODE_SCREEN_OFF: 1685 return mAdapterService.getScreenOffLowPowerWindowMillis(); 1686 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: 1687 return mAdapterService.getScreenOffBalancedWindowMillis(); 1688 default: 1689 return Settings.Global.getInt( 1690 resolver, 1691 Settings.Global.BLE_SCAN_LOW_POWER_WINDOW_MS, 1692 SCAN_MODE_LOW_POWER_WINDOW_MS); 1693 } 1694 } 1695 getScanIntervalMillis(ScanSettings settings)1696 private int getScanIntervalMillis(ScanSettings settings) { 1697 ContentResolver resolver = mService.getContentResolver(); 1698 if (settings == null) { 1699 return Settings.Global.getInt( 1700 resolver, 1701 Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, 1702 SCAN_MODE_LOW_POWER_INTERVAL_MS); 1703 } 1704 switch (settings.getScanMode()) { 1705 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1706 return Settings.Global.getInt( 1707 resolver, 1708 Settings.Global.BLE_SCAN_LOW_LATENCY_INTERVAL_MS, 1709 SCAN_MODE_LOW_LATENCY_INTERVAL_MS); 1710 case ScanSettings.SCAN_MODE_BALANCED: 1711 case ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY: 1712 return Settings.Global.getInt( 1713 resolver, 1714 Settings.Global.BLE_SCAN_BALANCED_INTERVAL_MS, 1715 SCAN_MODE_BALANCED_INTERVAL_MS); 1716 case ScanSettings.SCAN_MODE_LOW_POWER: 1717 return Settings.Global.getInt( 1718 resolver, 1719 Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, 1720 SCAN_MODE_LOW_POWER_INTERVAL_MS); 1721 case ScanSettings.SCAN_MODE_SCREEN_OFF: 1722 return mAdapterService.getScreenOffLowPowerIntervalMillis(); 1723 case ScanSettings.SCAN_MODE_SCREEN_OFF_BALANCED: 1724 return mAdapterService.getScreenOffBalancedIntervalMillis(); 1725 default: 1726 return Settings.Global.getInt( 1727 resolver, 1728 Settings.Global.BLE_SCAN_LOW_POWER_INTERVAL_MS, 1729 SCAN_MODE_LOW_POWER_INTERVAL_MS); 1730 } 1731 } 1732 getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound)1733 private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) { 1734 int factor; 1735 int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS; 1736 1737 if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) { 1738 factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR; 1739 } else { 1740 factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR; 1741 } 1742 if (!onFound) { 1743 factor = factor * ONLOST_FACTOR; 1744 } 1745 return (timeout * factor); 1746 } 1747 getOnFoundOnLostSightings(ScanSettings settings)1748 private int getOnFoundOnLostSightings(ScanSettings settings) { 1749 if (settings == null) { 1750 return ONFOUND_SIGHTINGS_AGGRESSIVE; 1751 } 1752 if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) { 1753 return ONFOUND_SIGHTINGS_AGGRESSIVE; 1754 } else { 1755 return ONFOUND_SIGHTINGS_STICKY; 1756 } 1757 } 1758 getNumOfTrackingAdvertisements(ScanSettings settings)1759 private int getNumOfTrackingAdvertisements(ScanSettings settings) { 1760 if (settings == null) { 1761 return 0; 1762 } 1763 int val = 0; 1764 int maxTotalTrackableAdvertisements = 1765 AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements(); 1766 // controller based onfound onlost resources are scarce commodity; the 1767 // assignment of filters to num of beacons to track is configurable based 1768 // on hw capabilities. Apps give an intent and allocation of onfound 1769 // resources or failure there of is done based on availability - FCFS model 1770 switch (settings.getNumOfMatches()) { 1771 case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT: 1772 val = 1; 1773 break; 1774 case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT: 1775 val = 2; 1776 break; 1777 case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT: 1778 val = maxTotalTrackableAdvertisements / 2; 1779 break; 1780 default: 1781 val = 1; 1782 if (DBG) { 1783 Log.d(TAG, "Invalid setting for getNumOfMatches() " 1784 + settings.getNumOfMatches()); 1785 } 1786 } 1787 return val; 1788 } 1789 manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement, boolean allocate)1790 private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement, 1791 boolean allocate) { 1792 int maxTotalTrackableAdvertisements = 1793 AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements(); 1794 synchronized (mCurUsedTrackableAdvertisementsLock) { 1795 int availableEntries = 1796 maxTotalTrackableAdvertisements - mCurUsedTrackableAdvertisements; 1797 if (allocate) { 1798 if (availableEntries >= numOfTrackableAdvertisement) { 1799 mCurUsedTrackableAdvertisements += numOfTrackableAdvertisement; 1800 return true; 1801 } else { 1802 return false; 1803 } 1804 } else { 1805 if (numOfTrackableAdvertisement > mCurUsedTrackableAdvertisements) { 1806 return false; 1807 } else { 1808 mCurUsedTrackableAdvertisements -= numOfTrackableAdvertisement; 1809 return true; 1810 } 1811 } 1812 } 1813 } 1814 registerScanner(long appUuidLsb, long appUuidMsb)1815 private void registerScanner(long appUuidLsb, long appUuidMsb) { 1816 mNativeInterface.registerScanner(appUuidLsb, appUuidMsb); 1817 } 1818 unregisterScanner(int scannerId)1819 private void unregisterScanner(int scannerId) { 1820 mNativeInterface.unregisterScanner(scannerId); 1821 } 1822 } 1823 1824 @VisibleForTesting getClientHandler()1825 ClientHandler getClientHandler() { 1826 return mHandler; 1827 } 1828 1829 @VisibleForTesting getBatchScanParams()1830 BatchScanParams getBatchScanParams() { 1831 return mBatchScanParms; 1832 } 1833 isScreenOn()1834 private boolean isScreenOn() { 1835 Display[] displays = mDm.getDisplays(); 1836 1837 if (displays == null) { 1838 return false; 1839 } 1840 1841 for (Display display : displays) { 1842 if (display.getState() == Display.STATE_ON) { 1843 return true; 1844 } 1845 } 1846 1847 return false; 1848 } 1849 1850 private final DisplayManager.DisplayListener mDisplayListener = 1851 new DisplayManager.DisplayListener() { 1852 @Override 1853 public void onDisplayAdded(int displayId) {} 1854 1855 @Override 1856 public void onDisplayRemoved(int displayId) {} 1857 1858 @Override 1859 public void onDisplayChanged(int displayId) { 1860 if (isScreenOn()) { 1861 sendMessage(MSG_SCREEN_ON, null); 1862 } else { 1863 sendMessage(MSG_SCREEN_OFF, null); 1864 } 1865 } 1866 }; 1867 1868 private ActivityManager.OnUidImportanceListener mUidImportanceListener = 1869 new ActivityManager.OnUidImportanceListener() { 1870 @Override 1871 public void onUidImportance(final int uid, final int importance) { 1872 if (mService.mScannerMap.getAppScanStatsByUid(uid) != null) { 1873 Message message = new Message(); 1874 message.what = MSG_IMPORTANCE_CHANGE; 1875 message.obj = new UidImportance(uid, importance); 1876 mHandler.sendMessage(message); 1877 } 1878 } 1879 }; 1880 1881 private BroadcastReceiver mLocationReceiver = 1882 new BroadcastReceiver() { 1883 @Override 1884 public void onReceive(Context context, Intent intent) { 1885 String action = intent.getAction(); 1886 if (LocationManager.MODE_CHANGED_ACTION.equals(action)) { 1887 final boolean locationEnabled = mLocationManager.isLocationEnabled(); 1888 if (locationEnabled) { 1889 sendMessage(MSG_RESUME_SCANS, null); 1890 } else { 1891 sendMessage(MSG_SUSPEND_SCANS, null); 1892 } 1893 } 1894 } 1895 }; 1896 1897 private BroadcastReceiver mBluetoothConnectionReceiver = 1898 new BroadcastReceiver() { 1899 @Override 1900 public void onReceive(Context context, Intent intent) { 1901 String action = intent.getAction(); 1902 if (action == null) { 1903 Log.w(TAG, "Received intent with null action"); 1904 return; 1905 } 1906 switch (action) { 1907 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: 1908 case BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED: 1909 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: 1910 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: 1911 case BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED: 1912 case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: 1913 int prevState = intent.getIntExtra( 1914 BluetoothProfile.EXTRA_PREVIOUS_STATE, -1); 1915 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 1916 if (DBG) { 1917 Log.d(TAG, "PROFILE_CONNECTION_STATE_CHANGE: action=" 1918 + action + ", prevState=" + prevState + ", state=" + state); 1919 } 1920 boolean updatedConnectingState = 1921 updateCountersAndCheckForConnectingState(state, prevState); 1922 if (DBG) { 1923 Log.d(TAG, "updatedConnectingState = " + updatedConnectingState); 1924 } 1925 if (updatedConnectingState) { 1926 if (!mIsConnecting) { 1927 onConnectingState(true); 1928 } 1929 } else { 1930 if (mIsConnecting) { 1931 onConnectingState(false); 1932 } 1933 } 1934 break; 1935 default: 1936 Log.w(TAG, "Received unknown intent " + intent); 1937 break; 1938 } 1939 } 1940 }; 1941 updateCountersAndCheckForConnectingState(int state, int prevState)1942 private boolean updateCountersAndCheckForConnectingState(int state, int prevState) { 1943 switch (prevState) { 1944 case BluetoothProfile.STATE_CONNECTING: 1945 if (mProfilesConnecting > 0) { 1946 mProfilesConnecting--; 1947 } else { 1948 Log.e(TAG, "mProfilesConnecting " + mProfilesConnecting); 1949 throw new IllegalStateException( 1950 "Invalid state transition, " + prevState + " -> " + state); 1951 } 1952 break; 1953 case BluetoothProfile.STATE_CONNECTED: 1954 if (mProfilesConnected > 0) { 1955 mProfilesConnected--; 1956 } else { 1957 Log.e(TAG, "mProfilesConnected " + mProfilesConnected); 1958 throw new IllegalStateException( 1959 "Invalid state transition, " + prevState + " -> " + state); 1960 } 1961 break; 1962 case BluetoothProfile.STATE_DISCONNECTING: 1963 if (mProfilesDisconnecting > 0) { 1964 mProfilesDisconnecting--; 1965 } else { 1966 Log.e(TAG, "mProfilesDisconnecting " + mProfilesDisconnecting); 1967 throw new IllegalStateException( 1968 "Invalid state transition, " + prevState + " -> " + state); 1969 } 1970 break; 1971 } 1972 switch (state) { 1973 case BluetoothProfile.STATE_CONNECTING: 1974 mProfilesConnecting++; 1975 break; 1976 case BluetoothProfile.STATE_CONNECTED: 1977 mProfilesConnected++; 1978 break; 1979 case BluetoothProfile.STATE_DISCONNECTING: 1980 mProfilesDisconnecting++; 1981 break; 1982 case BluetoothProfile.STATE_DISCONNECTED: 1983 break; 1984 default: 1985 } 1986 if (DBG) { 1987 Log.d(TAG, "mProfilesConnecting " + mProfilesConnecting + ", mProfilesConnected " 1988 + mProfilesConnected + ", mProfilesDisconnecting " + mProfilesDisconnecting); 1989 } 1990 return (mProfilesConnecting > 0); 1991 } 1992 handleImportanceChange(UidImportance imp)1993 private void handleImportanceChange(UidImportance imp) { 1994 if (imp == null) { 1995 return; 1996 } 1997 int uid = imp.uid; 1998 int importance = imp.importance; 1999 boolean updatedScanParams = false; 2000 boolean isForeground = importance <= ActivityManager.RunningAppProcessInfo 2001 .IMPORTANCE_FOREGROUND_SERVICE; 2002 2003 if (mIsUidForegroundMap.size() < MAX_IS_UID_FOREGROUND_MAP_SIZE) { 2004 mIsUidForegroundMap.put(uid, isForeground); 2005 } 2006 2007 for (ScanClient client : mRegularScanClients) { 2008 if (client.appUid != uid || mScanNative.isForceDowngradedScanClient(client)) { 2009 continue; 2010 } 2011 if (isForeground) { 2012 if (client.updateScanMode(client.scanModeApp)) { 2013 updatedScanParams = true; 2014 } 2015 } else { 2016 // Skip scan mode update in any of following cases 2017 // 1. screen is already off which triggers handleScreenOff() 2018 // 2. opportunistics scan 2019 if (mScreenOn && !mScanNative.isOpportunisticScanClient(client) 2020 && client.updateScanMode(SCAN_MODE_APP_IN_BACKGROUND)) { 2021 updatedScanParams = true; 2022 } 2023 } 2024 if (DBG) { 2025 Log.d(TAG, "uid " + uid + " isForeground " + isForeground 2026 + " scanMode " + client.settings.getScanMode()); 2027 } 2028 } 2029 2030 if (updatedScanParams) { 2031 mScanNative.configureRegularScanParams(); 2032 } 2033 } 2034 } 2035