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