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