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