ScanManager.java revision 3f33bbb6164a0b42eee2d9d469e9325c5395d680
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 17package com.android.bluetooth.gatt; 18 19import android.app.AlarmManager; 20import android.app.PendingIntent; 21import android.bluetooth.BluetoothAdapter; 22import android.bluetooth.le.ScanCallback; 23import android.bluetooth.le.ScanFilter; 24import android.bluetooth.le.ScanSettings; 25import android.content.BroadcastReceiver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.os.Handler; 30import android.os.HandlerThread; 31import android.os.Looper; 32import android.os.Message; 33import android.os.RemoteException; 34import android.os.ServiceManager; 35import android.os.SystemClock; 36import android.util.Log; 37 38import com.android.bluetooth.Utils; 39import com.android.bluetooth.btservice.AdapterService; 40import com.android.internal.app.IBatteryStats; 41 42import java.util.ArrayDeque; 43import java.util.Collections; 44import java.util.Deque; 45import java.util.HashMap; 46import java.util.HashSet; 47import java.util.Map; 48import java.util.Set; 49import java.util.UUID; 50import java.util.concurrent.ConcurrentHashMap; 51import java.util.concurrent.CountDownLatch; 52import java.util.concurrent.TimeUnit; 53 54/** 55 * Class that handles Bluetooth LE scan related operations. 56 * 57 * @hide 58 */ 59public class ScanManager { 60 private static final boolean DBG = GattServiceConfig.DBG; 61 private static final String TAG = GattServiceConfig.TAG_PREFIX + "ScanManager"; 62 63 // Result type defined in bt stack. Need to be accessed by GattService. 64 static final int SCAN_RESULT_TYPE_TRUNCATED = 1; 65 static final int SCAN_RESULT_TYPE_FULL = 2; 66 static final int SCAN_RESULT_TYPE_BOTH = 3; 67 68 // Internal messages for handling BLE scan operations. 69 private static final int MSG_START_BLE_SCAN = 0; 70 private static final int MSG_STOP_BLE_SCAN = 1; 71 private static final int MSG_FLUSH_BATCH_RESULTS = 2; 72 private static final int MSG_SCAN_TIMEOUT = 3; 73 74 private static final String ACTION_REFRESH_BATCHED_SCAN = 75 "com.android.bluetooth.gatt.REFRESH_BATCHED_SCAN"; 76 77 // Timeout for each controller operation. 78 private static final int OPERATION_TIME_OUT_MILLIS = 500; 79 80 private int mLastConfiguredScanSetting = Integer.MIN_VALUE; 81 // Scan parameters for batch scan. 82 private BatchScanParams mBatchScanParms; 83 84 private Integer curUsedTrackableAdvertisements; 85 private GattService mService; 86 private IBatteryStats mBatteryStats; 87 private BroadcastReceiver mBatchAlarmReceiver; 88 private boolean mBatchAlarmReceiverRegistered; 89 private ScanNative mScanNative; 90 private ClientHandler mHandler; 91 92 private Set<ScanClient> mRegularScanClients; 93 private Set<ScanClient> mBatchClients; 94 95 private CountDownLatch mLatch; 96 97 ScanManager(GattService service) { 98 mRegularScanClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); 99 mBatchClients = Collections.newSetFromMap(new ConcurrentHashMap<ScanClient, Boolean>()); 100 mService = service; 101 mScanNative = new ScanNative(); 102 curUsedTrackableAdvertisements = 0; 103 } 104 105 void start() { 106 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batterystats")); 107 HandlerThread thread = new HandlerThread("BluetoothScanManager"); 108 thread.start(); 109 mHandler = new ClientHandler(thread.getLooper()); 110 } 111 112 void cleanup() { 113 mRegularScanClients.clear(); 114 mBatchClients.clear(); 115 mScanNative.cleanup(); 116 117 if (mHandler != null) { 118 // Shut down the thread 119 mHandler.removeCallbacksAndMessages(null); 120 Looper looper = mHandler.getLooper(); 121 if (looper != null) { 122 looper.quit(); 123 } 124 mHandler = null; 125 } 126 } 127 128 void registerScanner(UUID uuid) { 129 mScanNative.registerScannerNative( 130 uuid.getLeastSignificantBits(), uuid.getMostSignificantBits()); 131 } 132 133 void unregisterScanner(int scannerId) { 134 mScanNative.unregisterScannerNative(scannerId); 135 } 136 137 /** 138 * Returns the regular scan queue. 139 */ 140 Set<ScanClient> getRegularScanQueue() { 141 return mRegularScanClients; 142 } 143 144 /** 145 * Returns batch scan queue. 146 */ 147 Set<ScanClient> getBatchScanQueue() { 148 return mBatchClients; 149 } 150 151 /** 152 * Returns a set of full batch scan clients. 153 */ 154 Set<ScanClient> getFullBatchScanQueue() { 155 // TODO: split full batch scan clients and truncated batch clients so we don't need to 156 // construct this every time. 157 Set<ScanClient> fullBatchClients = new HashSet<ScanClient>(); 158 for (ScanClient client : mBatchClients) { 159 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { 160 fullBatchClients.add(client); 161 } 162 } 163 return fullBatchClients; 164 } 165 166 void startScan(ScanClient client) { 167 sendMessage(MSG_START_BLE_SCAN, client); 168 } 169 170 void stopScan(ScanClient client) { 171 sendMessage(MSG_STOP_BLE_SCAN, client); 172 } 173 174 void flushBatchScanResults(ScanClient client) { 175 sendMessage(MSG_FLUSH_BATCH_RESULTS, client); 176 } 177 178 void callbackDone(int scannerId, int status) { 179 logd("callback done for scannerId - " + scannerId + " status - " + status); 180 if (status == 0) { 181 mLatch.countDown(); 182 } 183 // TODO: add a callback for scan failure. 184 } 185 186 private void sendMessage(int what, ScanClient client) { 187 Message message = new Message(); 188 message.what = what; 189 message.obj = client; 190 mHandler.sendMessage(message); 191 } 192 193 private boolean isFilteringSupported() { 194 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 195 return adapter.isOffloadedFilteringSupported(); 196 } 197 198 // Handler class that handles BLE scan operations. 199 private class ClientHandler extends Handler { 200 201 ClientHandler(Looper looper) { 202 super(looper); 203 } 204 205 @Override 206 public void handleMessage(Message msg) { 207 ScanClient client = (ScanClient) msg.obj; 208 switch (msg.what) { 209 case MSG_START_BLE_SCAN: 210 handleStartScan(client); 211 break; 212 case MSG_STOP_BLE_SCAN: 213 handleStopScan(client); 214 break; 215 case MSG_FLUSH_BATCH_RESULTS: 216 handleFlushBatchResults(client); 217 break; 218 case MSG_SCAN_TIMEOUT: 219 mScanNative.regularScanTimeout(client); 220 break; 221 default: 222 // Shouldn't happen. 223 Log.e(TAG, "received an unkown message : " + msg.what); 224 } 225 } 226 227 void handleStartScan(ScanClient client) { 228 Utils.enforceAdminPermission(mService); 229 logd("handling starting scan"); 230 231 if (!isScanSupported(client)) { 232 Log.e(TAG, "Scan settings not supported"); 233 return; 234 } 235 236 if (mRegularScanClients.contains(client) || mBatchClients.contains(client)) { 237 Log.e(TAG, "Scan already started"); 238 return; 239 } 240 // Begin scan operations. 241 if (isBatchClient(client)) { 242 mBatchClients.add(client); 243 mScanNative.startBatchScan(client); 244 } else { 245 mRegularScanClients.add(client); 246 mScanNative.startRegularScan(client); 247 if (!mScanNative.isOpportunisticScanClient(client)) { 248 mScanNative.configureRegularScanParams(); 249 250 if (!mScanNative.isExemptFromScanDowngrade(client)) { 251 Message msg = mHandler.obtainMessage(MSG_SCAN_TIMEOUT); 252 msg.obj = client; 253 // Only one timeout message should exist at any time 254 mHandler.sendMessageDelayed(msg, AppScanStats.SCAN_TIMEOUT_MS); 255 } 256 } 257 258 // Update BatteryStats with this workload. 259 try { 260 mBatteryStats.noteBleScanStarted(client.workSource); 261 } catch (RemoteException e) { 262 /* ignore */ 263 } 264 } 265 } 266 267 void handleStopScan(ScanClient client) { 268 Utils.enforceAdminPermission(mService); 269 if (client == null) return; 270 271 if (mRegularScanClients.contains(client)) { 272 // Update BatteryStats with this workload. 273 try { 274 // The ScanClient passed in just holds the scannerId. We retrieve the real 275 // client, which may have workSource set. 276 ScanClient workClient = mScanNative.getRegularScanClient(client.scannerId); 277 if (workClient != null) mBatteryStats.noteBleScanStopped(workClient.workSource); 278 } catch (RemoteException e) { 279 /* ignore */ 280 } 281 282 mScanNative.stopRegularScan(client); 283 284 if (mScanNative.numRegularScanClients() == 0) { 285 mHandler.removeMessages(MSG_SCAN_TIMEOUT); 286 } 287 288 if (!mScanNative.isOpportunisticScanClient(client)) { 289 mScanNative.configureRegularScanParams(); 290 } 291 } else { 292 mScanNative.stopBatchScan(client); 293 } 294 if (client.appDied) { 295 logd("app died, unregister scanner - " + client.scannerId); 296 mService.unregisterScanner(client.scannerId); 297 } 298 } 299 300 void handleFlushBatchResults(ScanClient client) { 301 Utils.enforceAdminPermission(mService); 302 if (!mBatchClients.contains(client)) { 303 return; 304 } 305 mScanNative.flushBatchResults(client.scannerId); 306 } 307 308 private boolean isBatchClient(ScanClient client) { 309 if (client == null || client.settings == null) { 310 return false; 311 } 312 ScanSettings settings = client.settings; 313 return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES && 314 settings.getReportDelayMillis() != 0; 315 } 316 317 private boolean isScanSupported(ScanClient client) { 318 if (client == null || client.settings == null) { 319 return true; 320 } 321 ScanSettings settings = client.settings; 322 if (isFilteringSupported()) { 323 return true; 324 } 325 return settings.getCallbackType() == ScanSettings.CALLBACK_TYPE_ALL_MATCHES && 326 settings.getReportDelayMillis() == 0; 327 } 328 } 329 330 /** 331 * Parameters for batch scans. 332 */ 333 class BatchScanParams { 334 int scanMode; 335 int fullScanscannerId; 336 int truncatedScanscannerId; 337 338 BatchScanParams() { 339 scanMode = -1; 340 fullScanscannerId = -1; 341 truncatedScanscannerId = -1; 342 } 343 344 @Override 345 public boolean equals(Object obj) { 346 if (this == obj) { 347 return true; 348 } 349 if (obj == null || getClass() != obj.getClass()) { 350 return false; 351 } 352 BatchScanParams other = (BatchScanParams) obj; 353 return scanMode == other.scanMode && fullScanscannerId == other.fullScanscannerId 354 && truncatedScanscannerId == other.truncatedScanscannerId; 355 356 } 357 } 358 359 public int getCurrentUsedTrackingAdvertisement() { 360 return curUsedTrackableAdvertisements; 361 } 362 363 private class ScanNative { 364 365 // Delivery mode defined in bt stack. 366 private static final int DELIVERY_MODE_IMMEDIATE = 0; 367 private static final int DELIVERY_MODE_ON_FOUND_LOST = 1; 368 private static final int DELIVERY_MODE_BATCH = 2; 369 370 private static final int ONFOUND_SIGHTINGS_AGGRESSIVE = 1; 371 private static final int ONFOUND_SIGHTINGS_STICKY = 4; 372 373 private static final int ALL_PASS_FILTER_INDEX_REGULAR_SCAN = 1; 374 private static final int ALL_PASS_FILTER_INDEX_BATCH_SCAN = 2; 375 private static final int ALL_PASS_FILTER_SELECTION = 0; 376 377 private static final int DISCARD_OLDEST_WHEN_BUFFER_FULL = 0; 378 379 /** 380 * Scan params corresponding to regular scan setting 381 */ 382 private static final int SCAN_MODE_LOW_POWER_WINDOW_MS = 500; 383 private static final int SCAN_MODE_LOW_POWER_INTERVAL_MS = 5000; 384 private static final int SCAN_MODE_BALANCED_WINDOW_MS = 2000; 385 private static final int SCAN_MODE_BALANCED_INTERVAL_MS = 5000; 386 private static final int SCAN_MODE_LOW_LATENCY_WINDOW_MS = 5000; 387 private static final int SCAN_MODE_LOW_LATENCY_INTERVAL_MS = 5000; 388 389 /** 390 * Onfound/onlost for scan settings 391 */ 392 private static final int MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR = (1); 393 private static final int MATCH_MODE_STICKY_TIMEOUT_FACTOR = (3); 394 private static final int ONLOST_FACTOR = 2; 395 private static final int ONLOST_ONFOUND_BASE_TIMEOUT_MS = 500; 396 397 /** 398 * Scan params corresponding to batch scan setting 399 */ 400 private static final int SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS = 1500; 401 private static final int SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS = 150000; 402 private static final int SCAN_MODE_BATCH_BALANCED_WINDOW_MS = 1500; 403 private static final int SCAN_MODE_BATCH_BALANCED_INTERVAL_MS = 15000; 404 private static final int SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS = 1500; 405 private static final int SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS = 5000; 406 407 // The logic is AND for each filter field. 408 private static final int LIST_LOGIC_TYPE = 0x1111111; 409 private static final int FILTER_LOGIC_TYPE = 1; 410 // Filter indices that are available to user. It's sad we need to maintain filter index. 411 private final Deque<Integer> mFilterIndexStack; 412 // Map of scannerId and Filter indices used by client. 413 private final Map<Integer, Deque<Integer>> mClientFilterIndexMap; 414 // Keep track of the clients that uses ALL_PASS filters. 415 private final Set<Integer> mAllPassRegularClients = new HashSet<>(); 416 private final Set<Integer> mAllPassBatchClients = new HashSet<>(); 417 418 private AlarmManager mAlarmManager; 419 private PendingIntent mBatchScanIntervalIntent; 420 421 ScanNative() { 422 mFilterIndexStack = new ArrayDeque<Integer>(); 423 mClientFilterIndexMap = new HashMap<Integer, Deque<Integer>>(); 424 425 mAlarmManager = (AlarmManager) mService.getSystemService(Context.ALARM_SERVICE); 426 Intent batchIntent = new Intent(ACTION_REFRESH_BATCHED_SCAN, null); 427 mBatchScanIntervalIntent = PendingIntent.getBroadcast(mService, 0, batchIntent, 0); 428 IntentFilter filter = new IntentFilter(); 429 filter.addAction(ACTION_REFRESH_BATCHED_SCAN); 430 mBatchAlarmReceiver = new BroadcastReceiver() { 431 @Override 432 public void onReceive(Context context, Intent intent) { 433 Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime()); 434 String action = intent.getAction(); 435 436 if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { 437 if (mBatchClients.isEmpty()) { 438 return; 439 } 440 // Note this actually flushes all pending batch data. 441 flushBatchScanResults(mBatchClients.iterator().next()); 442 } 443 } 444 }; 445 mService.registerReceiver(mBatchAlarmReceiver, filter); 446 mBatchAlarmReceiverRegistered = true; 447 } 448 449 private void resetCountDownLatch() { 450 mLatch = new CountDownLatch(1); 451 } 452 453 // Returns true if mLatch reaches 0, false if timeout or interrupted. 454 private boolean waitForCallback() { 455 try { 456 return mLatch.await(OPERATION_TIME_OUT_MILLIS, TimeUnit.MILLISECONDS); 457 } catch (InterruptedException e) { 458 return false; 459 } 460 } 461 462 void configureRegularScanParams() { 463 logd("configureRegularScanParams() - queue=" + mRegularScanClients.size()); 464 int curScanSetting = Integer.MIN_VALUE; 465 ScanClient client = getAggressiveClient(mRegularScanClients); 466 if (client != null) { 467 curScanSetting = client.settings.getScanMode(); 468 } 469 470 logd("configureRegularScanParams() - ScanSetting Scan mode=" + curScanSetting + 471 " mLastConfiguredScanSetting=" + mLastConfiguredScanSetting); 472 473 if (curScanSetting != Integer.MIN_VALUE && 474 curScanSetting != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { 475 if (curScanSetting != mLastConfiguredScanSetting) { 476 int scanWindow = getScanWindowMillis(client.settings); 477 int scanInterval = getScanIntervalMillis(client.settings); 478 // convert scanWindow and scanInterval from ms to LE scan units(0.625ms) 479 scanWindow = Utils.millsToUnit(scanWindow); 480 scanInterval = Utils.millsToUnit(scanInterval); 481 gattClientScanNative(false); 482 logd("configureRegularScanParams - scanInterval = " + scanInterval + 483 "configureRegularScanParams - scanWindow = " + scanWindow); 484 gattSetScanParametersNative(client.scannerId, scanInterval, scanWindow); 485 gattClientScanNative(true); 486 mLastConfiguredScanSetting = curScanSetting; 487 } 488 } else { 489 mLastConfiguredScanSetting = curScanSetting; 490 logd("configureRegularScanParams() - queue emtpy, scan stopped"); 491 } 492 } 493 494 ScanClient getAggressiveClient(Set<ScanClient> cList) { 495 ScanClient result = null; 496 int curScanSetting = Integer.MIN_VALUE; 497 for (ScanClient client : cList) { 498 // ScanClient scan settings are assumed to be monotonically increasing in value for 499 // more power hungry(higher duty cycle) operation. 500 if (client.settings.getScanMode() > curScanSetting) { 501 result = client; 502 curScanSetting = client.settings.getScanMode(); 503 } 504 } 505 return result; 506 } 507 508 void startRegularScan(ScanClient client) { 509 if (isFilteringSupported() && mFilterIndexStack.isEmpty() 510 && mClientFilterIndexMap.isEmpty()) { 511 initFilterIndexStack(); 512 } 513 if (isFilteringSupported()) { 514 configureScanFilters(client); 515 } 516 // Start scan native only for the first client. 517 if (numRegularScanClients() == 1) { 518 gattClientScanNative(true); 519 } 520 } 521 522 private int numRegularScanClients() { 523 int num = 0; 524 for (ScanClient client: mRegularScanClients) { 525 if (client.settings.getScanMode() != ScanSettings.SCAN_MODE_OPPORTUNISTIC) { 526 num++; 527 } 528 } 529 return num; 530 } 531 532 void startBatchScan(ScanClient client) { 533 if (mFilterIndexStack.isEmpty() && isFilteringSupported()) { 534 initFilterIndexStack(); 535 } 536 configureScanFilters(client); 537 if (!isOpportunisticScanClient(client)) { 538 // Reset batch scan. May need to stop the existing batch scan and update scan params. 539 resetBatchScan(client); 540 } 541 } 542 543 private boolean isExemptFromScanDowngrade(ScanClient client) { 544 return isOpportunisticScanClient(client) 545 || isFirstMatchScanClient(client) 546 || !shouldUseAllPassFilter(client); 547 } 548 549 private boolean isOpportunisticScanClient(ScanClient client) { 550 return client.settings.getScanMode() == ScanSettings.SCAN_MODE_OPPORTUNISTIC; 551 } 552 553 private boolean isFirstMatchScanClient(ScanClient client) { 554 return (client.settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0; 555 } 556 557 private void resetBatchScan(ScanClient client) { 558 int scannerId = client.scannerId; 559 BatchScanParams batchScanParams = getBatchScanParams(); 560 // Stop batch if batch scan params changed and previous params is not null. 561 if (mBatchScanParms != null && (!mBatchScanParms.equals(batchScanParams))) { 562 logd("stopping BLe Batch"); 563 resetCountDownLatch(); 564 gattClientStopBatchScanNative(scannerId); 565 waitForCallback(); 566 // Clear pending results as it's illegal to config storage if there are still 567 // pending results. 568 flushBatchResults(scannerId); 569 } 570 // Start batch if batchScanParams changed and current params is not null. 571 if (batchScanParams != null && (!batchScanParams.equals(mBatchScanParms))) { 572 int notifyThreshold = 95; 573 logd("Starting BLE batch scan"); 574 int resultType = getResultType(batchScanParams); 575 int fullScanPercent = getFullScanStoragePercent(resultType); 576 resetCountDownLatch(); 577 logd("configuring batch scan storage, appIf " + client.scannerId); 578 gattClientConfigBatchScanStorageNative(client.scannerId, fullScanPercent, 579 100 - fullScanPercent, notifyThreshold); 580 waitForCallback(); 581 resetCountDownLatch(); 582 int scanInterval = 583 Utils.millsToUnit(getBatchScanIntervalMillis(batchScanParams.scanMode)); 584 int scanWindow = 585 Utils.millsToUnit(getBatchScanWindowMillis(batchScanParams.scanMode)); 586 gattClientStartBatchScanNative(scannerId, resultType, scanInterval, 587 scanWindow, 0, DISCARD_OLDEST_WHEN_BUFFER_FULL); 588 waitForCallback(); 589 } 590 mBatchScanParms = batchScanParams; 591 setBatchAlarm(); 592 } 593 594 private int getFullScanStoragePercent(int resultType) { 595 switch (resultType) { 596 case SCAN_RESULT_TYPE_FULL: 597 return 100; 598 case SCAN_RESULT_TYPE_TRUNCATED: 599 return 0; 600 case SCAN_RESULT_TYPE_BOTH: 601 return 50; 602 default: 603 return 50; 604 } 605 } 606 607 private BatchScanParams getBatchScanParams() { 608 if (mBatchClients.isEmpty()) { 609 return null; 610 } 611 BatchScanParams params = new BatchScanParams(); 612 // TODO: split full batch scan results and truncated batch scan results to different 613 // collections. 614 for (ScanClient client : mBatchClients) { 615 params.scanMode = Math.max(params.scanMode, client.settings.getScanMode()); 616 if (client.settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_FULL) { 617 params.fullScanscannerId = client.scannerId; 618 } else { 619 params.truncatedScanscannerId = client.scannerId; 620 } 621 } 622 return params; 623 } 624 625 private int getBatchScanWindowMillis(int scanMode) { 626 switch (scanMode) { 627 case ScanSettings.SCAN_MODE_LOW_LATENCY: 628 return SCAN_MODE_BATCH_LOW_LATENCY_WINDOW_MS; 629 case ScanSettings.SCAN_MODE_BALANCED: 630 return SCAN_MODE_BATCH_BALANCED_WINDOW_MS; 631 case ScanSettings.SCAN_MODE_LOW_POWER: 632 return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS; 633 default: 634 return SCAN_MODE_BATCH_LOW_POWER_WINDOW_MS; 635 } 636 } 637 638 private int getBatchScanIntervalMillis(int scanMode) { 639 switch (scanMode) { 640 case ScanSettings.SCAN_MODE_LOW_LATENCY: 641 return SCAN_MODE_BATCH_LOW_LATENCY_INTERVAL_MS; 642 case ScanSettings.SCAN_MODE_BALANCED: 643 return SCAN_MODE_BATCH_BALANCED_INTERVAL_MS; 644 case ScanSettings.SCAN_MODE_LOW_POWER: 645 return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS; 646 default: 647 return SCAN_MODE_BATCH_LOW_POWER_INTERVAL_MS; 648 } 649 } 650 651 // Set the batch alarm to be triggered within a short window after batch interval. This 652 // allows system to optimize wake up time while still allows a degree of precise control. 653 private void setBatchAlarm() { 654 // Cancel any pending alarm just in case. 655 mAlarmManager.cancel(mBatchScanIntervalIntent); 656 if (mBatchClients.isEmpty()) { 657 return; 658 } 659 long batchTriggerIntervalMillis = getBatchTriggerIntervalMillis(); 660 // Allows the alarm to be triggered within 661 // [batchTriggerIntervalMillis, 1.1 * batchTriggerIntervalMillis] 662 long windowLengthMillis = batchTriggerIntervalMillis / 10; 663 long windowStartMillis = SystemClock.elapsedRealtime() + batchTriggerIntervalMillis; 664 mAlarmManager.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, 665 windowStartMillis, windowLengthMillis, 666 mBatchScanIntervalIntent); 667 } 668 669 void stopRegularScan(ScanClient client) { 670 // Remove scan filters and recycle filter indices. 671 if (client == null) return; 672 int deliveryMode = getDeliveryMode(client); 673 if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) { 674 for (ScanFilter filter : client.filters) { 675 int entriesToFree = getNumOfTrackingAdvertisements(client.settings); 676 if (!manageAllocationOfTrackingAdvertisement(entriesToFree, false)) { 677 Log.e(TAG, "Error freeing for onfound/onlost filter resources " 678 + entriesToFree); 679 try { 680 mService.onScanManagerErrorCallback(client.scannerId, 681 ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 682 } catch (RemoteException e) { 683 Log.e(TAG, "failed on onScanManagerCallback at freeing", e); 684 } 685 } 686 } 687 } 688 mRegularScanClients.remove(client); 689 if (numRegularScanClients() == 0) { 690 logd("stop scan"); 691 gattClientScanNative(false); 692 } 693 removeScanFilters(client.scannerId); 694 } 695 696 void regularScanTimeout(ScanClient client) { 697 if (!isExemptFromScanDowngrade(client) && client.stats.isScanningTooLong()) { 698 Log.w(TAG, 699 "Moving scan client to opportunistic (scannerId " + client.scannerId + ")"); 700 setOpportunisticScanClient(client); 701 client.stats.setScanTimeout(); 702 } 703 704 // The scan should continue for background scans 705 configureRegularScanParams(); 706 if (numRegularScanClients() == 0) { 707 logd("stop scan"); 708 gattClientScanNative(false); 709 } 710 } 711 712 void setOpportunisticScanClient(ScanClient client) { 713 // TODO: Add constructor to ScanSettings.Builder 714 // that can copy values from an existing ScanSettings object 715 ScanSettings.Builder builder = new ScanSettings.Builder(); 716 ScanSettings settings = client.settings; 717 builder.setScanMode(ScanSettings.SCAN_MODE_OPPORTUNISTIC); 718 builder.setCallbackType(settings.getCallbackType()); 719 builder.setScanResultType(settings.getScanResultType()); 720 builder.setReportDelay(settings.getReportDelayMillis()); 721 builder.setNumOfMatches(settings.getNumOfMatches()); 722 client.settings = builder.build(); 723 } 724 725 // Find the regular scan client information. 726 ScanClient getRegularScanClient(int scannerId) { 727 for (ScanClient client : mRegularScanClients) { 728 if (client.scannerId == scannerId) return client; 729 } 730 return null; 731 } 732 733 void stopBatchScan(ScanClient client) { 734 mBatchClients.remove(client); 735 removeScanFilters(client.scannerId); 736 if (!isOpportunisticScanClient(client)) { 737 resetBatchScan(client); 738 } 739 } 740 741 void flushBatchResults(int scannerId) { 742 logd("flushPendingBatchResults - scannerId = " + scannerId); 743 if (mBatchScanParms.fullScanscannerId != -1) { 744 resetCountDownLatch(); 745 gattClientReadScanReportsNative(mBatchScanParms.fullScanscannerId, 746 SCAN_RESULT_TYPE_FULL); 747 waitForCallback(); 748 } 749 if (mBatchScanParms.truncatedScanscannerId != -1) { 750 resetCountDownLatch(); 751 gattClientReadScanReportsNative(mBatchScanParms.truncatedScanscannerId, 752 SCAN_RESULT_TYPE_TRUNCATED); 753 waitForCallback(); 754 } 755 setBatchAlarm(); 756 } 757 758 void cleanup() { 759 mAlarmManager.cancel(mBatchScanIntervalIntent); 760 // Protect against multiple calls of cleanup. 761 if (mBatchAlarmReceiverRegistered) { 762 mService.unregisterReceiver(mBatchAlarmReceiver); 763 } 764 mBatchAlarmReceiverRegistered = false; 765 } 766 767 private long getBatchTriggerIntervalMillis() { 768 long intervalMillis = Long.MAX_VALUE; 769 for (ScanClient client : mBatchClients) { 770 if (client.settings != null && client.settings.getReportDelayMillis() > 0) { 771 intervalMillis = Math.min(intervalMillis, 772 client.settings.getReportDelayMillis()); 773 } 774 } 775 return intervalMillis; 776 } 777 778 // Add scan filters. The logic is: 779 // If no offload filter can/needs to be set, set ALL_PASS filter. 780 // Otherwise offload all filters to hardware and enable all filters. 781 private void configureScanFilters(ScanClient client) { 782 int scannerId = client.scannerId; 783 int deliveryMode = getDeliveryMode(client); 784 int trackEntries = 0; 785 if (!shouldAddAllPassFilterToController(client, deliveryMode)) { 786 return; 787 } 788 789 resetCountDownLatch(); 790 gattClientScanFilterEnableNative(scannerId, true); 791 waitForCallback(); 792 793 if (shouldUseAllPassFilter(client)) { 794 int filterIndex = (deliveryMode == DELIVERY_MODE_BATCH) ? 795 ALL_PASS_FILTER_INDEX_BATCH_SCAN : ALL_PASS_FILTER_INDEX_REGULAR_SCAN; 796 resetCountDownLatch(); 797 // Don't allow Onfound/onlost with all pass 798 configureFilterParamter(scannerId, client, ALL_PASS_FILTER_SELECTION, 799 filterIndex, 0); 800 waitForCallback(); 801 } else { 802 Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>(); 803 for (ScanFilter filter : client.filters) { 804 ScanFilterQueue queue = new ScanFilterQueue(); 805 queue.addScanFilter(filter); 806 int featureSelection = queue.getFeatureSelection(); 807 int filterIndex = mFilterIndexStack.pop(); 808 while (!queue.isEmpty()) { 809 resetCountDownLatch(); 810 addFilterToController(scannerId, queue.pop(), filterIndex); 811 waitForCallback(); 812 } 813 resetCountDownLatch(); 814 if (deliveryMode == DELIVERY_MODE_ON_FOUND_LOST) { 815 trackEntries = getNumOfTrackingAdvertisements(client.settings); 816 if (!manageAllocationOfTrackingAdvertisement(trackEntries, true)) { 817 Log.e(TAG, "No hardware resources for onfound/onlost filter " + 818 trackEntries); 819 try { 820 mService.onScanManagerErrorCallback(scannerId, 821 ScanCallback.SCAN_FAILED_INTERNAL_ERROR); 822 } catch (RemoteException e) { 823 Log.e(TAG, "failed on onScanManagerCallback", e); 824 } 825 } 826 } 827 configureFilterParamter(scannerId, client, featureSelection, filterIndex, 828 trackEntries); 829 waitForCallback(); 830 clientFilterIndices.add(filterIndex); 831 } 832 mClientFilterIndexMap.put(scannerId, clientFilterIndices); 833 } 834 } 835 836 // Check whether the filter should be added to controller. 837 // Note only on ALL_PASS filter should be added. 838 private boolean shouldAddAllPassFilterToController(ScanClient client, int deliveryMode) { 839 // Not an ALL_PASS client, need to add filter. 840 if (!shouldUseAllPassFilter(client)) { 841 return true; 842 } 843 844 if (deliveryMode == DELIVERY_MODE_BATCH) { 845 mAllPassBatchClients.add(client.scannerId); 846 return mAllPassBatchClients.size() == 1; 847 } else { 848 mAllPassRegularClients.add(client.scannerId); 849 return mAllPassRegularClients.size() == 1; 850 } 851 } 852 853 private void removeScanFilters(int scannerId) { 854 Deque<Integer> filterIndices = mClientFilterIndexMap.remove(scannerId); 855 if (filterIndices != null) { 856 mFilterIndexStack.addAll(filterIndices); 857 for (Integer filterIndex : filterIndices) { 858 resetCountDownLatch(); 859 gattClientScanFilterParamDeleteNative(scannerId, filterIndex); 860 waitForCallback(); 861 } 862 } 863 // Remove if ALL_PASS filters are used. 864 removeFilterIfExisits(mAllPassRegularClients, scannerId, 865 ALL_PASS_FILTER_INDEX_REGULAR_SCAN); 866 removeFilterIfExisits(mAllPassBatchClients, scannerId, 867 ALL_PASS_FILTER_INDEX_BATCH_SCAN); 868 } 869 870 private void removeFilterIfExisits(Set<Integer> clients, int scannerId, int filterIndex) { 871 if (!clients.contains(scannerId)) { 872 return; 873 } 874 clients.remove(scannerId); 875 // Remove ALL_PASS filter iff no app is using it. 876 if (clients.isEmpty()) { 877 resetCountDownLatch(); 878 gattClientScanFilterParamDeleteNative(scannerId, filterIndex); 879 waitForCallback(); 880 } 881 } 882 883 private ScanClient getBatchScanClient(int scannerId) { 884 for (ScanClient client : mBatchClients) { 885 if (client.scannerId == scannerId) { 886 return client; 887 } 888 } 889 return null; 890 } 891 892 /** 893 * Return batch scan result type value defined in bt stack. 894 */ 895 private int getResultType(BatchScanParams params) { 896 if (params.fullScanscannerId != -1 && params.truncatedScanscannerId != -1) { 897 return SCAN_RESULT_TYPE_BOTH; 898 } 899 if (params.truncatedScanscannerId != -1) { 900 return SCAN_RESULT_TYPE_TRUNCATED; 901 } 902 if (params.fullScanscannerId != -1) { 903 return SCAN_RESULT_TYPE_FULL; 904 } 905 return -1; 906 } 907 908 // Check if ALL_PASS filter should be used for the client. 909 private boolean shouldUseAllPassFilter(ScanClient client) { 910 if (client == null) { 911 return true; 912 } 913 if (client.filters == null || client.filters.isEmpty()) { 914 return true; 915 } 916 return client.filters.size() > mFilterIndexStack.size(); 917 } 918 919 private void addFilterToController(int scannerId, ScanFilterQueue.Entry entry, 920 int filterIndex) { 921 logd("addFilterToController: " + entry.type); 922 switch (entry.type) { 923 case ScanFilterQueue.TYPE_DEVICE_ADDRESS: 924 logd("add address " + entry.address); 925 gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0, 926 0, 927 "", entry.address, (byte) entry.addr_type, new byte[0], new byte[0]); 928 break; 929 930 case ScanFilterQueue.TYPE_SERVICE_DATA: 931 gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0, 932 0, 933 "", "", (byte) 0, entry.data, entry.data_mask); 934 break; 935 936 case ScanFilterQueue.TYPE_SERVICE_UUID: 937 case ScanFilterQueue.TYPE_SOLICIT_UUID: 938 gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 939 entry.uuid.getLeastSignificantBits(), 940 entry.uuid.getMostSignificantBits(), 941 entry.uuid_mask.getLeastSignificantBits(), 942 entry.uuid_mask.getMostSignificantBits(), 943 "", "", (byte) 0, new byte[0], new byte[0]); 944 break; 945 946 case ScanFilterQueue.TYPE_LOCAL_NAME: 947 logd("adding filters: " + entry.name); 948 gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, 0, 0, 0, 0, 0, 949 0, 950 entry.name, "", (byte) 0, new byte[0], new byte[0]); 951 break; 952 953 case ScanFilterQueue.TYPE_MANUFACTURER_DATA: 954 int len = entry.data.length; 955 if (entry.data_mask.length != len) 956 return; 957 gattClientScanFilterAddNative(scannerId, entry.type, filterIndex, entry.company, 958 entry.company_mask, 0, 0, 0, 0, "", "", (byte) 0, 959 entry.data, entry.data_mask); 960 break; 961 } 962 } 963 964 private void initFilterIndexStack() { 965 int maxFiltersSupported = 966 AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported(); 967 // Start from index 3 as: 968 // index 0 is reserved for ALL_PASS filter in Settings app. 969 // index 1 is reserved for ALL_PASS filter for regular scan apps. 970 // index 2 is reserved for ALL_PASS filter for batch scan apps. 971 for (int i = 3; i < maxFiltersSupported; ++i) { 972 mFilterIndexStack.add(i); 973 } 974 } 975 976 // Configure filter parameters. 977 private void configureFilterParamter(int scannerId, ScanClient client, int featureSelection, 978 int filterIndex, int numOfTrackingEntries) { 979 int deliveryMode = getDeliveryMode(client); 980 int rssiThreshold = Byte.MIN_VALUE; 981 ScanSettings settings = client.settings; 982 int onFoundTimeout = getOnFoundOnLostTimeoutMillis(settings, true); 983 int onLostTimeout = getOnFoundOnLostTimeoutMillis(settings, false); 984 int onFoundCount = getOnFoundOnLostSightings(settings); 985 onLostTimeout = 10000; 986 logd("configureFilterParamter " + onFoundTimeout + " " + onLostTimeout + " " 987 + onFoundCount + " " + numOfTrackingEntries); 988 FilterParams FiltValue = new FilterParams(scannerId, filterIndex, featureSelection, 989 LIST_LOGIC_TYPE, FILTER_LOGIC_TYPE, rssiThreshold, rssiThreshold, deliveryMode, 990 onFoundTimeout, onLostTimeout, onFoundCount, numOfTrackingEntries); 991 gattClientScanFilterParamAddNative(FiltValue); 992 } 993 994 // Get delivery mode based on scan settings. 995 private int getDeliveryMode(ScanClient client) { 996 if (client == null) { 997 return DELIVERY_MODE_IMMEDIATE; 998 } 999 ScanSettings settings = client.settings; 1000 if (settings == null) { 1001 return DELIVERY_MODE_IMMEDIATE; 1002 } 1003 if ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0 1004 || (settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) { 1005 return DELIVERY_MODE_ON_FOUND_LOST; 1006 } 1007 return settings.getReportDelayMillis() == 0 ? DELIVERY_MODE_IMMEDIATE 1008 : DELIVERY_MODE_BATCH; 1009 } 1010 1011 private int getScanWindowMillis(ScanSettings settings) { 1012 if (settings == null) { 1013 return SCAN_MODE_LOW_POWER_WINDOW_MS; 1014 } 1015 switch (settings.getScanMode()) { 1016 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1017 return SCAN_MODE_LOW_LATENCY_WINDOW_MS; 1018 case ScanSettings.SCAN_MODE_BALANCED: 1019 return SCAN_MODE_BALANCED_WINDOW_MS; 1020 case ScanSettings.SCAN_MODE_LOW_POWER: 1021 return SCAN_MODE_LOW_POWER_WINDOW_MS; 1022 default: 1023 return SCAN_MODE_LOW_POWER_WINDOW_MS; 1024 } 1025 } 1026 1027 private int getScanIntervalMillis(ScanSettings settings) { 1028 if (settings == null) 1029 return SCAN_MODE_LOW_POWER_INTERVAL_MS; 1030 switch (settings.getScanMode()) { 1031 case ScanSettings.SCAN_MODE_LOW_LATENCY: 1032 return SCAN_MODE_LOW_LATENCY_INTERVAL_MS; 1033 case ScanSettings.SCAN_MODE_BALANCED: 1034 return SCAN_MODE_BALANCED_INTERVAL_MS; 1035 case ScanSettings.SCAN_MODE_LOW_POWER: 1036 return SCAN_MODE_LOW_POWER_INTERVAL_MS; 1037 default: 1038 return SCAN_MODE_LOW_POWER_INTERVAL_MS; 1039 } 1040 } 1041 1042 private int getOnFoundOnLostTimeoutMillis(ScanSettings settings, boolean onFound) { 1043 int factor; 1044 int timeout = ONLOST_ONFOUND_BASE_TIMEOUT_MS; 1045 1046 if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) { 1047 factor = MATCH_MODE_AGGRESSIVE_TIMEOUT_FACTOR; 1048 } else { 1049 factor = MATCH_MODE_STICKY_TIMEOUT_FACTOR; 1050 } 1051 if (!onFound) factor = factor * ONLOST_FACTOR; 1052 return (timeout*factor); 1053 } 1054 1055 private int getOnFoundOnLostSightings(ScanSettings settings) { 1056 if (settings == null) 1057 return ONFOUND_SIGHTINGS_AGGRESSIVE; 1058 if (settings.getMatchMode() == ScanSettings.MATCH_MODE_AGGRESSIVE) { 1059 return ONFOUND_SIGHTINGS_AGGRESSIVE; 1060 } else { 1061 return ONFOUND_SIGHTINGS_STICKY; 1062 } 1063 } 1064 1065 private int getNumOfTrackingAdvertisements(ScanSettings settings) { 1066 if (settings == null) 1067 return 0; 1068 int val=0; 1069 int maxTotalTrackableAdvertisements = 1070 AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements(); 1071 // controller based onfound onlost resources are scarce commodity; the 1072 // assignment of filters to num of beacons to track is configurable based 1073 // on hw capabilities. Apps give an intent and allocation of onfound 1074 // resources or failure there of is done based on availibility - FCFS model 1075 switch (settings.getNumOfMatches()) { 1076 case ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT: 1077 val = 1; 1078 break; 1079 case ScanSettings.MATCH_NUM_FEW_ADVERTISEMENT: 1080 val = 2; 1081 break; 1082 case ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT: 1083 val = maxTotalTrackableAdvertisements/2; 1084 break; 1085 default: 1086 val = 1; 1087 logd("Invalid setting for getNumOfMatches() " + settings.getNumOfMatches()); 1088 } 1089 return val; 1090 } 1091 1092 private boolean manageAllocationOfTrackingAdvertisement(int numOfTrackableAdvertisement, 1093 boolean allocate) { 1094 int maxTotalTrackableAdvertisements = 1095 AdapterService.getAdapterService().getTotalNumOfTrackableAdvertisements(); 1096 synchronized(curUsedTrackableAdvertisements) { 1097 int availableEntries = maxTotalTrackableAdvertisements 1098 - curUsedTrackableAdvertisements; 1099 if (allocate) { 1100 if (availableEntries >= numOfTrackableAdvertisement) { 1101 curUsedTrackableAdvertisements += numOfTrackableAdvertisement; 1102 return true; 1103 } else { 1104 return false; 1105 } 1106 } else { 1107 if (numOfTrackableAdvertisement > curUsedTrackableAdvertisements) { 1108 return false; 1109 } else { 1110 curUsedTrackableAdvertisements -= numOfTrackableAdvertisement; 1111 return true; 1112 } 1113 } 1114 } 1115 } 1116 1117 1118 /************************** Regular scan related native methods **************************/ 1119 private native void registerScannerNative(long app_uuid_lsb, long app_uuid_msb); 1120 private native void unregisterScannerNative(int scannerId); 1121 1122 private native void gattClientScanNative(boolean start); 1123 1124 private native void gattSetScanParametersNative(int client_if, int scan_interval, 1125 int scan_window); 1126 1127 /************************** Filter related native methods ********************************/ 1128 private native void gattClientScanFilterAddNative(int client_if, 1129 int filter_type, int filter_index, int company_id, 1130 int company_id_mask, long uuid_lsb, long uuid_msb, 1131 long uuid_mask_lsb, long uuid_mask_msb, String name, 1132 String address, byte addr_type, byte[] data, byte[] mask); 1133 1134 private native void gattClientScanFilterDeleteNative(int client_if, 1135 int filter_type, int filter_index, int company_id, 1136 int company_id_mask, long uuid_lsb, long uuid_msb, 1137 long uuid_mask_lsb, long uuid_mask_msb, String name, 1138 String address, byte addr_type, byte[] data, byte[] mask); 1139 1140 private native void gattClientScanFilterParamAddNative(FilterParams FiltValue); 1141 1142 // Note this effectively remove scan filters for ALL clients. 1143 private native void gattClientScanFilterParamClearAllNative( 1144 int client_if); 1145 1146 private native void gattClientScanFilterParamDeleteNative( 1147 int client_if, int filt_index); 1148 1149 private native void gattClientScanFilterClearNative(int client_if, 1150 int filter_index); 1151 1152 private native void gattClientScanFilterEnableNative(int client_if, 1153 boolean enable); 1154 1155 /************************** Batch related native methods *********************************/ 1156 private native void gattClientConfigBatchScanStorageNative(int client_if, 1157 int max_full_reports_percent, int max_truncated_reports_percent, 1158 int notify_threshold_percent); 1159 1160 private native void gattClientStartBatchScanNative(int client_if, int scan_mode, 1161 int scan_interval_unit, int scan_window_unit, int address_type, int discard_rule); 1162 1163 private native void gattClientStopBatchScanNative(int client_if); 1164 1165 private native void gattClientReadScanReportsNative(int client_if, int scan_type); 1166 } 1167 1168 private void logd(String s) { 1169 if (DBG) Log.d(TAG, s); 1170 } 1171} 1172