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