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