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