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