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