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