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