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