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