1 /* 2 * Copyright (C) 2024 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.le_scan; 18 19 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; 20 import static android.Manifest.permission.BLUETOOTH_SCAN; 21 import static android.Manifest.permission.UPDATE_DEVICE_STATS; 22 import static android.bluetooth.BluetoothUtils.extractBytes; 23 24 import static com.android.bluetooth.Utils.checkCallerTargetSdk; 25 import static com.android.bluetooth.Utils.checkScanPermissionForDataDelivery; 26 import static com.android.bluetooth.flags.Flags.leaudioBassScanWithInternalScanController; 27 28 import static java.util.Objects.requireNonNull; 29 30 import android.annotation.RequiresPermission; 31 import android.annotation.SuppressLint; 32 import android.app.AppOpsManager; 33 import android.app.PendingIntent; 34 import android.bluetooth.BluetoothAdapter; 35 import android.bluetooth.BluetoothDevice; 36 import android.bluetooth.BluetoothUtils; 37 import android.bluetooth.le.BluetoothLeScanner; 38 import android.bluetooth.le.IPeriodicAdvertisingCallback; 39 import android.bluetooth.le.IScannerCallback; 40 import android.bluetooth.le.ScanCallback; 41 import android.bluetooth.le.ScanFilter; 42 import android.bluetooth.le.ScanRecord; 43 import android.bluetooth.le.ScanResult; 44 import android.bluetooth.le.ScanSettings; 45 import android.companion.CompanionDeviceManager; 46 import android.content.AttributionSource; 47 import android.content.Intent; 48 import android.net.MacAddress; 49 import android.os.Binder; 50 import android.os.Build; 51 import android.os.Handler; 52 import android.os.HandlerThread; 53 import android.os.IBinder; 54 import android.os.Looper; 55 import android.os.Message; 56 import android.os.RemoteException; 57 import android.os.SystemClock; 58 import android.os.UserHandle; 59 import android.os.WorkSource; 60 import android.provider.DeviceConfig; 61 import android.text.format.DateUtils; 62 import android.util.Log; 63 64 import com.android.bluetooth.R; 65 import com.android.bluetooth.Utils; 66 import com.android.bluetooth.btservice.AdapterService; 67 import com.android.bluetooth.btservice.BluetoothAdapterProxy; 68 import com.android.bluetooth.btservice.ProfileService; 69 import com.android.bluetooth.util.NumberUtils; 70 import com.android.internal.annotations.VisibleForTesting; 71 72 import libcore.util.HexEncoding; 73 74 import com.google.protobuf.ByteString; 75 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.Collections; 79 import java.util.HashMap; 80 import java.util.HashSet; 81 import java.util.List; 82 import java.util.Map; 83 import java.util.Set; 84 import java.util.UUID; 85 import java.util.concurrent.TimeUnit; 86 import java.util.function.Predicate; 87 import java.util.stream.Collectors; 88 89 public class ScanController { 90 private static final String TAG = ScanController.class.getSimpleName(); 91 92 /** The default floor value for LE batch scan report delays greater than 0 */ 93 static final long DEFAULT_REPORT_DELAY_FLOOR = 5000L; 94 95 // Batch scan related constants. 96 private static final int TRUNCATED_RESULT_SIZE = 11; 97 98 // onFoundLost related constants 99 @VisibleForTesting static final int ADVT_STATE_ONFOUND = 0; 100 private static final int ADVT_STATE_ONLOST = 1; 101 102 private static final int ET_LEGACY_MASK = 0x10; 103 104 /** Example raw beacons captured from a Blue Charm BC011 */ 105 private static final String[] TEST_MODE_BEACONS = 106 new String[] { 107 "020106", 108 "0201060303AAFE1716AAFE10EE01626C7565636861726D626561636F6E730009168020691E0EFE13551109426C7565436861726D5F313639363835000000", 109 "0201060303AAFE1716AAFE00EE626C7565636861726D31000000000001000009168020691E0EFE13551109426C7565436861726D5F313639363835000000", 110 "0201060303AAFE1116AAFE20000BF017000008874803FB93540916802069080EFE13551109426C7565436861726D5F313639363835000000000000000000", 111 "0201061AFF4C000215426C7565436861726D426561636F6E730EFE1355C509168020691E0EFE13551109426C7565436861726D5F31363936383500000000", 112 }; 113 114 private final PendingIntent.CancelListener mScanIntentCancelListener = 115 new PendingIntent.CancelListener() { 116 public void onCanceled(PendingIntent intent) { 117 Log.d(TAG, "scanning PendingIntent canceled"); 118 stopScanInternal(intent); 119 } 120 }; 121 122 private final Map<Integer, Integer> mFilterIndexToMsftAdvMonitorMap = new HashMap<>(); 123 private final Object mTestModeLock = new Object(); 124 125 private final BluetoothAdapter mAdapter; 126 private final AdapterService mAdapterService; 127 private final String mExposureNotificationPackage; 128 private final Predicate<ScanResult> mLocationDenylistPredicate; 129 private final Looper mMainLooper; 130 private final ScanBinder mBinder; 131 private final HandlerThread mScanThread; 132 private final AppOpsManager mAppOps; 133 private final CompanionDeviceManager mCompanionManager; 134 private final ScanManager mScanManager; 135 private final PeriodicScanManager mPeriodicScanManager; 136 137 private volatile boolean mTestModeEnabled = false; 138 private ScannerMap mScannerMap = new ScannerMap(); 139 private Handler mTestModeHandler; 140 ScanController(AdapterService adapterService)141 public ScanController(AdapterService adapterService) { 142 mAdapter = BluetoothAdapter.getDefaultAdapter(); 143 mAdapterService = requireNonNull(adapterService); 144 mExposureNotificationPackage = 145 mAdapterService.getString(R.string.exposure_notification_package); 146 mLocationDenylistPredicate = 147 (scanResult) -> { 148 final MacAddress parsedAddress = 149 MacAddress.fromString(scanResult.getDevice().getAddress()); 150 if (mAdapterService 151 .getLocationDenylistMac() 152 .test(parsedAddress.toByteArray())) { 153 Log.v(TAG, "Skipping device matching denylist: " + scanResult.getDevice()); 154 return true; 155 } 156 final ScanRecord scanRecord = scanResult.getScanRecord(); 157 if (scanRecord.matchesAnyField( 158 mAdapterService.getLocationDenylistAdvertisingData())) { 159 Log.v(TAG, "Skipping data matching denylist: " + scanRecord); 160 return true; 161 } 162 return false; 163 }; 164 mMainLooper = adapterService.getMainLooper(); 165 mBinder = new ScanBinder(this); 166 mScanThread = new HandlerThread("BluetoothScanManager"); 167 mScanThread.start(); 168 mAppOps = mAdapterService.getSystemService(AppOpsManager.class); 169 mCompanionManager = mAdapterService.getSystemService(CompanionDeviceManager.class); 170 mScanManager = 171 ScanObjectsFactory.getInstance() 172 .createScanManager( 173 mAdapterService, 174 this, 175 BluetoothAdapterProxy.getInstance(), 176 mScanThread.getLooper()); 177 mPeriodicScanManager = ScanObjectsFactory.getInstance().createPeriodicScanManager(); 178 } 179 cleanup()180 public void cleanup() { 181 Log.i(TAG, "Cleanup ScanController"); 182 mBinder.cleanup(); 183 mScanThread.quitSafely(); 184 mScannerMap.clear(); 185 mScanManager.cleanup(); 186 mPeriodicScanManager.cleanup(); 187 } 188 getScannerMap()189 ScannerMap getScannerMap() { 190 return mScannerMap; 191 } 192 193 @VisibleForTesting setScannerMap(ScannerMap scannerMap)194 void setScannerMap(ScannerMap scannerMap) { 195 mScannerMap = scannerMap; 196 } 197 198 /** Notify Scan manager of bluetooth profile connection state changes */ notifyProfileConnectionStateChange(int profile, int fromState, int toState)199 public void notifyProfileConnectionStateChange(int profile, int fromState, int toState) { 200 mScanManager.handleBluetoothProfileConnectionStateChanged(profile, fromState, toState); 201 } 202 getBinder()203 public IBinder getBinder() { 204 return mBinder; 205 } 206 setTestModeEnabled(boolean enableTestMode)207 public void setTestModeEnabled(boolean enableTestMode) { 208 synchronized (mTestModeLock) { 209 if (mTestModeHandler == null) { 210 mTestModeHandler = 211 new Handler(mMainLooper) { 212 public void handleMessage(Message msg) { 213 synchronized (mTestModeLock) { 214 if (!mTestModeEnabled) { 215 return; 216 } 217 for (String test : TEST_MODE_BEACONS) { 218 onScanResultInternal( 219 0x1b, 220 0x1, 221 "DD:34:02:05:5C:4D", 222 1, 223 0, 224 0xff, 225 127, 226 -54, 227 0x0, 228 HexEncoding.decode(test), 229 "DD:34:02:05:5C:4E"); 230 } 231 sendEmptyMessageDelayed(0, DateUtils.SECOND_IN_MILLIS); 232 } 233 } 234 }; 235 } 236 if (enableTestMode == mTestModeEnabled) { 237 return; 238 } 239 mTestModeEnabled = enableTestMode; 240 mTestModeHandler.removeMessages(0); 241 mTestModeHandler.sendEmptyMessageDelayed( 242 0, enableTestMode ? DateUtils.SECOND_IN_MILLIS : 0); 243 } 244 } 245 PendingIntentInfo( PendingIntent intent, ScanSettings settings, List<ScanFilter> filters, String callingPackage, int callingUid)246 record PendingIntentInfo( 247 PendingIntent intent, 248 ScanSettings settings, 249 List<ScanFilter> filters, 250 String callingPackage, 251 int callingUid) { 252 @Override 253 public boolean equals(Object other) { 254 if (!(other instanceof PendingIntentInfo)) { 255 return false; 256 } 257 return intent.equals(((PendingIntentInfo) other).intent); 258 } 259 260 @Override 261 public int hashCode() { 262 return intent == null ? 0 : intent.hashCode(); 263 } 264 } 265 266 /************************************************************************** 267 * Callback functions - CLIENT 268 *************************************************************************/ 269 270 // EN format defined here: 271 // https://blog.google/documents/70/Exposure_Notification_-_Bluetooth_Specification_v1.2.2.pdf 272 private static final byte[] EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE = 273 new byte[] { 274 // size 2, flag field, flags byte (value is not important) 275 (byte) 0x02, (byte) 0x01 276 }; 277 278 private static final int EXPOSURE_NOTIFICATION_FLAGS_LENGTH = 0x2 + 1; 279 private static final byte[] EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE = 280 new byte[] { 281 // size 3, complete 16 bit UUID, EN UUID 282 (byte) 0x03, (byte) 0x03, (byte) 0x6F, (byte) 0xFD, 283 // size 23, data for 16 bit UUID, EN UUID 284 (byte) 0x17, (byte) 0x16, (byte) 0x6F, (byte) 0xFD, 285 // ...payload 286 }; 287 private static final int EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH = 0x03 + 0x17 + 2; 288 arrayStartsWith(byte[] array, byte[] prefix)289 private static boolean arrayStartsWith(byte[] array, byte[] prefix) { 290 if (array.length < prefix.length) { 291 return false; 292 } 293 for (int i = 0; i < prefix.length; i++) { 294 if (prefix[i] != array[i]) { 295 return false; 296 } 297 } 298 return true; 299 } 300 getSanitizedExposureNotification(ScanResult result)301 private static ScanResult getSanitizedExposureNotification(ScanResult result) { 302 ScanRecord record = result.getScanRecord(); 303 // Remove the flags part of the payload, if present 304 if (record.getBytes().length > EXPOSURE_NOTIFICATION_FLAGS_LENGTH 305 && arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_FLAGS_PREAMBLE)) { 306 record = 307 ScanRecord.parseFromBytes( 308 Arrays.copyOfRange( 309 record.getBytes(), 310 EXPOSURE_NOTIFICATION_FLAGS_LENGTH, 311 record.getBytes().length)); 312 } 313 314 if (record.getBytes().length != EXPOSURE_NOTIFICATION_PAYLOAD_LENGTH) { 315 return null; 316 } 317 if (!arrayStartsWith(record.getBytes(), EXPOSURE_NOTIFICATION_PAYLOAD_PREAMBLE)) { 318 return null; 319 } 320 321 return new ScanResult(null, 0, 0, 0, 0, 0, result.getRssi(), 0, record, 0); 322 } 323 324 /** Callback method for a scan result. */ onScanResult( int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress)325 void onScanResult( 326 int eventType, 327 int addressType, 328 String address, 329 int primaryPhy, 330 int secondaryPhy, 331 int advertisingSid, 332 int txPower, 333 int rssi, 334 int periodicAdvInt, 335 byte[] advData, 336 String originalAddress) { 337 // When in testing mode, ignore all real-world events 338 if (mTestModeEnabled) return; 339 340 AppScanStats.recordScanRadioResultCount(); 341 onScanResultInternal( 342 eventType, 343 addressType, 344 address, 345 primaryPhy, 346 secondaryPhy, 347 advertisingSid, 348 txPower, 349 rssi, 350 periodicAdvInt, 351 advData, 352 originalAddress); 353 } 354 onScanResultInternal( int eventType, int addressType, String address, int primaryPhy, int secondaryPhy, int advertisingSid, int txPower, int rssi, int periodicAdvInt, byte[] advData, String originalAddress)355 private void onScanResultInternal( 356 int eventType, 357 int addressType, 358 String address, 359 int primaryPhy, 360 int secondaryPhy, 361 int advertisingSid, 362 int txPower, 363 int rssi, 364 int periodicAdvInt, 365 byte[] advData, 366 String originalAddress) { 367 Log.v( 368 TAG, 369 "onScanResult() - eventType=0x" 370 + Integer.toHexString(eventType) 371 + ", addressType=" 372 + addressType 373 + ", address=" 374 + BluetoothUtils.toAnonymizedAddress(address) 375 + ", primaryPhy=" 376 + primaryPhy 377 + ", secondaryPhy=" 378 + secondaryPhy 379 + ", advertisingSid=0x" 380 + Integer.toHexString(advertisingSid) 381 + ", txPower=" 382 + txPower 383 + ", rssi=" 384 + rssi 385 + ", periodicAdvInt=0x" 386 + Integer.toHexString(periodicAdvInt) 387 + ", originalAddress=" 388 + originalAddress); 389 390 // Retain the original behavior of returning bluetoothAddress when identityAddress is null 391 String identityAddress = Utils.getBrEdrAddress(address, mAdapterService); 392 393 if (!address.equals(identityAddress)) { 394 Log.v( 395 TAG, 396 "found identityAddress of " 397 + address 398 + ", replace originalAddress as " 399 + identityAddress); 400 originalAddress = identityAddress; 401 } 402 403 byte[] legacyAdvData = Arrays.copyOfRange(advData, 0, 62); 404 405 BluetoothDevice device = mAdapter.getRemoteLeDevice(address, addressType); 406 407 for (ScanClient client : mScanManager.getRegularScanQueue()) { 408 ScannerMap.ScannerApp app = mScannerMap.getById(client.mScannerId); 409 if (app == null) { 410 Log.v(TAG, "App is null; skip."); 411 continue; 412 } 413 414 final ScanSettings settings = client.mSettings; 415 final byte[] scanRecordData; 416 // This is for compatibility with applications that assume fixed size scan data. 417 if (settings.getLegacy()) { 418 if ((eventType & ET_LEGACY_MASK) == 0) { 419 // If this is legacy scan, but nonlegacy result - skip. 420 Log.v(TAG, "Legacy scan, non legacy result; skip."); 421 continue; 422 } else { 423 // Some apps are used to fixed-size advertise data. 424 scanRecordData = legacyAdvData; 425 } 426 } else { 427 scanRecordData = advData; 428 } 429 430 ScanRecord scanRecord = ScanRecord.parseFromBytes(scanRecordData); 431 ScanResult result = 432 new ScanResult( 433 device, 434 eventType, 435 primaryPhy, 436 secondaryPhy, 437 advertisingSid, 438 txPower, 439 rssi, 440 periodicAdvInt, 441 scanRecord, 442 SystemClock.elapsedRealtimeNanos()); 443 444 if (client.mHasDisavowedLocation) { 445 if (mLocationDenylistPredicate.test(result)) { 446 Log.i(TAG, "Skipping client " + client.mScannerId + " for location deny list"); 447 continue; 448 } 449 } 450 451 boolean hasPermission = hasScanResultPermission(client); 452 if (!hasPermission) { 453 for (String associatedDevice : client.mAssociatedDevices) { 454 if (associatedDevice.equalsIgnoreCase(address)) { 455 hasPermission = true; 456 break; 457 } 458 } 459 } 460 if (!hasPermission && client.mEligibleForSanitizedExposureNotification) { 461 ScanResult sanitized = getSanitizedExposureNotification(result); 462 if (sanitized != null) { 463 hasPermission = true; 464 result = sanitized; 465 } 466 } 467 boolean matchResult = matchesFilters(client, result, originalAddress); 468 if (!hasPermission || !matchResult) { 469 Log.v( 470 TAG, 471 "Skipping client: permission=" + hasPermission + " matches=" + matchResult); 472 continue; 473 } 474 475 int callbackType = settings.getCallbackType(); 476 if (!(callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES 477 || callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES_AUTO_BATCH)) { 478 Log.v(TAG, "Skipping client: Not CALLBACK_TYPE_ALL_MATCHES"); 479 continue; 480 } 481 482 try { 483 app.mAppScanStats.addResult(client.mScannerId); 484 if (app.mCallback != null) { 485 app.mCallback.onScanResult(result); 486 } else { 487 Log.v(TAG, "Callback is null, sending scan results by pendingIntent"); 488 List<ScanResult> results = new ArrayList<>(Arrays.asList(result)); 489 sendResultsByPendingIntent( 490 app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); 491 } 492 } catch (RemoteException | PendingIntent.CanceledException e) { 493 Log.e(TAG, "Exception: " + e); 494 handleDeadScanClient(client); 495 } 496 } 497 } 498 sendResultByPendingIntent( PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client)499 private void sendResultByPendingIntent( 500 PendingIntentInfo pii, ScanResult result, int callbackType, ScanClient client) { 501 List<ScanResult> results = new ArrayList<>(Arrays.asList(result)); 502 try { 503 sendResultsByPendingIntent(pii, results, callbackType); 504 } catch (PendingIntent.CanceledException e) { 505 final long token = Binder.clearCallingIdentity(); 506 try { 507 stopScanInternal(client.mScannerId); 508 unregisterScannerInternal(client.mScannerId); 509 } finally { 510 Binder.restoreCallingIdentity(token); 511 } 512 } 513 } 514 sendResultsByPendingIntent( PendingIntentInfo pii, List<ScanResult> results, int callbackType)515 private void sendResultsByPendingIntent( 516 PendingIntentInfo pii, List<ScanResult> results, int callbackType) 517 throws PendingIntent.CanceledException { 518 Intent extrasIntent = new Intent(); 519 extrasIntent.putParcelableArrayListExtra( 520 BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT, new ArrayList<>(results)); 521 extrasIntent.putExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, callbackType); 522 pii.intent.send(mAdapterService, 0, extrasIntent); 523 } 524 sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode)525 private void sendErrorByPendingIntent(PendingIntentInfo pii, int errorCode) 526 throws PendingIntent.CanceledException { 527 Intent extrasIntent = new Intent(); 528 extrasIntent.putExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, errorCode); 529 pii.intent.send(mAdapterService, 0, extrasIntent); 530 } 531 532 /** Callback method for scanner registration. */ onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb)533 void onScannerRegistered(int status, int scannerId, long uuidLsb, long uuidMsb) 534 throws RemoteException { 535 UUID uuid = new UUID(uuidMsb, uuidLsb); 536 Log.d( 537 TAG, 538 "onScannerRegistered() - UUID=" 539 + uuid 540 + ", scannerId=" 541 + scannerId 542 + ", status=" 543 + status); 544 545 // First check the callback map 546 ScannerMap.ScannerApp cbApp = mScannerMap.getByUuid(uuid); 547 if (cbApp != null) { 548 if (status == 0) { 549 cbApp.mId = scannerId; 550 // If app is callback based, setup a death recipient. App will initiate the start. 551 // Otherwise, if PendingIntent based, start the scan directly. 552 if (cbApp.mCallback != null) { 553 cbApp.linkToDeath(new ScannerDeathRecipient(scannerId, cbApp.mName)); 554 } else { 555 continuePiStartScan(scannerId, cbApp); 556 } 557 } else { 558 mScannerMap.remove(scannerId); 559 } 560 if (cbApp.mCallback != null) { 561 cbApp.mCallback.onScannerRegistered(status, scannerId); 562 } 563 } 564 } 565 566 /** Determines if the given scan client has the appropriate permissions to receive callbacks. */ hasScanResultPermission(final ScanClient client)567 private boolean hasScanResultPermission(final ScanClient client) { 568 if (leaudioBassScanWithInternalScanController() && client.mIsInternalClient) { 569 // Bypass permission check for internal clients 570 return true; 571 } 572 if (client.mHasNetworkSettingsPermission 573 || client.mHasNetworkSetupWizardPermission 574 || client.mHasScanWithoutLocationPermission) { 575 return true; 576 } 577 if (client.mHasDisavowedLocation) { 578 return true; 579 } 580 return client.mHasLocationPermission 581 && !Utils.blockedByLocationOff(mAdapterService, client.mUserHandle); 582 } 583 permittedResults(final ScanClient client, Set<ScanResult> results)584 private List<ScanResult> permittedResults(final ScanClient client, Set<ScanResult> results) { 585 if (hasScanResultPermission(client)) { 586 return new ArrayList<>(results); 587 } 588 589 List<ScanResult> permittedResults = new ArrayList<>(); 590 for (ScanResult scanResult : results) { 591 for (String associatedDevice : client.mAssociatedDevices) { 592 if (associatedDevice.equalsIgnoreCase(scanResult.getDevice().getAddress())) { 593 permittedResults.add(scanResult); 594 } 595 } 596 } 597 return permittedResults; 598 } 599 600 // Check if a scan record matches a specific filters. matchesFilters(ScanClient client, ScanResult scanResult)601 private static boolean matchesFilters(ScanClient client, ScanResult scanResult) { 602 return matchesFilters(client, scanResult, null); 603 } 604 605 // Check if a scan record matches a specific filters or original address matchesFilters( ScanClient client, ScanResult scanResult, String originalAddress)606 private static boolean matchesFilters( 607 ScanClient client, ScanResult scanResult, String originalAddress) { 608 if (client.mFilters == null || client.mFilters.isEmpty()) { 609 // TODO: Do we really wanna return true here? 610 return true; 611 } 612 for (ScanFilter filter : client.mFilters) { 613 // Need to check the filter matches, and the original address without changing the API 614 if (filter.matches(scanResult)) { 615 return true; 616 } 617 if (originalAddress != null 618 && originalAddress.equalsIgnoreCase(filter.getDeviceAddress())) { 619 return true; 620 } 621 } 622 return false; 623 } 624 handleDeadScanClient(ScanClient client)625 private void handleDeadScanClient(ScanClient client) { 626 if (client.mAppDied) { 627 Log.w(TAG, "Already dead client " + client.mScannerId); 628 return; 629 } 630 client.mAppDied = true; 631 if (client.mStats != null) { 632 client.mStats.isAppDead = true; 633 } 634 stopScanInternal(client.mScannerId); 635 } 636 637 /** Callback method for scan filter enablement/disablement. */ onScanFilterEnableDisabled(int action, int status, int clientIf)638 void onScanFilterEnableDisabled(int action, int status, int clientIf) { 639 Log.d( 640 TAG, 641 "onScanFilterEnableDisabled() - clientIf=" 642 + clientIf 643 + ", status=" 644 + status 645 + ", action=" 646 + action); 647 mScanManager.callbackDone(clientIf, status); 648 } 649 650 /** Callback method for configuration of scan filter params. */ onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace)651 void onScanFilterParamsConfigured(int action, int status, int clientIf, int availableSpace) { 652 Log.d( 653 TAG, 654 "onScanFilterParamsConfigured() - clientIf=" 655 + clientIf 656 + ", status=" 657 + status 658 + ", action=" 659 + action 660 + ", availableSpace=" 661 + availableSpace); 662 mScanManager.callbackDone(clientIf, status); 663 } 664 665 /** Callback method for configuration of scan filter. */ onScanFilterConfig( int action, int status, int clientIf, int filterType, int availableSpace)666 void onScanFilterConfig( 667 int action, int status, int clientIf, int filterType, int availableSpace) { 668 Log.d( 669 TAG, 670 "onScanFilterConfig() - clientIf=" 671 + clientIf 672 + ", action = " 673 + action 674 + " status = " 675 + status 676 + ", filterType=" 677 + filterType 678 + ", availableSpace=" 679 + availableSpace); 680 681 mScanManager.callbackDone(clientIf, status); 682 } 683 684 /** Callback method for configuration of batch scan storage. */ onBatchScanStorageConfigured(int status, int clientIf)685 void onBatchScanStorageConfigured(int status, int clientIf) { 686 Log.d(TAG, "onBatchScanStorageConfigured() - clientIf=" + clientIf + ", status=" + status); 687 mScanManager.callbackDone(clientIf, status); 688 } 689 690 /** Callback method for start/stop of batch scan. */ 691 // TODO: split into two different callbacks : onBatchScanStarted and onBatchScanStopped. onBatchScanStartStopped(int startStopAction, int status, int clientIf)692 void onBatchScanStartStopped(int startStopAction, int status, int clientIf) { 693 Log.d( 694 TAG, 695 "onBatchScanStartStopped() - clientIf=" 696 + clientIf 697 + ", status=" 698 + status 699 + ", startStopAction=" 700 + startStopAction); 701 mScanManager.callbackDone(clientIf, status); 702 } 703 findBatchScanClientById(int scannerId)704 ScanClient findBatchScanClientById(int scannerId) { 705 for (ScanClient client : mScanManager.getBatchScanQueue()) { 706 if (client.mScannerId == scannerId) { 707 return client; 708 } 709 } 710 return null; 711 } 712 713 /** Callback method for batch scan reports */ onBatchScanReports( int status, int scannerId, int reportType, int numRecords, byte[] recordData)714 void onBatchScanReports( 715 int status, int scannerId, int reportType, int numRecords, byte[] recordData) 716 throws RemoteException { 717 // When in testing mode, ignore all real-world events 718 if (mTestModeEnabled) return; 719 720 AppScanStats.recordBatchScanRadioResultCount(numRecords); 721 onBatchScanReportsInternal(status, scannerId, reportType, numRecords, recordData); 722 } 723 724 @VisibleForTesting onBatchScanReportsInternal( int status, int scannerId, int reportType, int numRecords, byte[] recordData)725 void onBatchScanReportsInternal( 726 int status, int scannerId, int reportType, int numRecords, byte[] recordData) 727 throws RemoteException { 728 Log.d( 729 TAG, 730 "onBatchScanReports() - scannerId=" 731 + scannerId 732 + ", status=" 733 + status 734 + ", reportType=" 735 + reportType 736 + ", numRecords=" 737 + numRecords); 738 739 Set<ScanResult> results = parseBatchScanResults(numRecords, reportType, recordData); 740 if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) { 741 // We only support single client for truncated mode. 742 ScannerMap.ScannerApp app = mScannerMap.getById(scannerId); 743 if (app == null) { 744 return; 745 } 746 747 ScanClient client = findBatchScanClientById(scannerId); 748 if (client == null) { 749 return; 750 } 751 752 List<ScanResult> permittedResults = permittedResults(client, results); 753 754 if (client.mHasDisavowedLocation) { 755 permittedResults.removeIf(mLocationDenylistPredicate); 756 } 757 if (permittedResults.isEmpty()) { 758 mScanManager.callbackDone(scannerId, status); 759 return; 760 } 761 762 if (app.mCallback != null) { 763 app.mCallback.onBatchScanResults(permittedResults); 764 mScanManager.batchScanResultDelivered(); 765 } else { 766 // PendingIntent based 767 try { 768 sendResultsByPendingIntent( 769 app.mInfo, permittedResults, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); 770 } catch (PendingIntent.CanceledException e) { 771 Log.d(TAG, "Exception while sending result", e); 772 } 773 } 774 } else { 775 for (ScanClient client : mScanManager.getFullBatchScanQueue()) { 776 // Deliver results for each client. 777 deliverBatchScan(client, results); 778 } 779 } 780 mScanManager.callbackDone(scannerId, status); 781 } 782 sendBatchScanResults( ScannerMap.ScannerApp app, ScanClient client, List<ScanResult> results)783 private void sendBatchScanResults( 784 ScannerMap.ScannerApp app, ScanClient client, List<ScanResult> results) { 785 if (results.isEmpty()) { 786 return; 787 } 788 try { 789 if (app.mCallback != null) { 790 if (mScanManager.isAutoBatchScanClientEnabled(client)) { 791 Log.d(TAG, "sendBatchScanResults() to onScanResult()" + client); 792 for (ScanResult result : results) { 793 app.mAppScanStats.addResult(client.mScannerId); 794 app.mCallback.onScanResult(result); 795 } 796 } else { 797 Log.d(TAG, "sendBatchScanResults() to onBatchScanResults()" + client); 798 app.mCallback.onBatchScanResults(results); 799 } 800 } else { 801 sendResultsByPendingIntent( 802 app.mInfo, results, ScanSettings.CALLBACK_TYPE_ALL_MATCHES); 803 } 804 } catch (RemoteException | PendingIntent.CanceledException e) { 805 Log.e(TAG, "Exception: " + e); 806 handleDeadScanClient(client); 807 } 808 mScanManager.batchScanResultDelivered(); 809 } 810 811 // Check and deliver scan results for different scan clients. deliverBatchScan(ScanClient client, Set<ScanResult> allResults)812 private void deliverBatchScan(ScanClient client, Set<ScanResult> allResults) 813 throws RemoteException { 814 ScannerMap.ScannerApp app = mScannerMap.getById(client.mScannerId); 815 if (app == null) { 816 return; 817 } 818 819 List<ScanResult> permittedResults = permittedResults(client, allResults); 820 821 if (client.mFilters == null || client.mFilters.isEmpty()) { 822 sendBatchScanResults(app, client, permittedResults); 823 return; 824 } 825 // Reconstruct the scan results. 826 List<ScanResult> results = new ArrayList<>(); 827 for (ScanResult scanResult : permittedResults) { 828 if (matchesFilters(client, scanResult)) { 829 results.add(scanResult); 830 } 831 } 832 sendBatchScanResults(app, client, results); 833 } 834 parseBatchScanResults( int numRecords, int reportType, byte[] batchRecord)835 private Set<ScanResult> parseBatchScanResults( 836 int numRecords, int reportType, byte[] batchRecord) { 837 if (numRecords == 0) { 838 return Collections.emptySet(); 839 } 840 Log.d(TAG, "current time is " + SystemClock.elapsedRealtimeNanos()); 841 if (reportType == ScanManager.SCAN_RESULT_TYPE_TRUNCATED) { 842 return parseTruncatedResults(numRecords, batchRecord); 843 } else { 844 return parseFullResults(numRecords, batchRecord); 845 } 846 } 847 parseTruncatedResults(int numRecords, byte[] batchRecord)848 private Set<ScanResult> parseTruncatedResults(int numRecords, byte[] batchRecord) { 849 Log.d(TAG, "batch record " + Arrays.toString(batchRecord)); 850 Set<ScanResult> results = new HashSet<ScanResult>(numRecords); 851 long now = SystemClock.elapsedRealtimeNanos(); 852 for (int i = 0; i < numRecords; ++i) { 853 byte[] record = 854 extractBytes(batchRecord, i * TRUNCATED_RESULT_SIZE, TRUNCATED_RESULT_SIZE); 855 byte[] address = extractBytes(record, 0, 6); 856 Utils.reverse(address); 857 BluetoothDevice device = mAdapter.getRemoteDevice(address); 858 int rssi = record[8]; 859 long timestampNanos = now - parseTimestampNanos(extractBytes(record, 9, 2)); 860 results.add( 861 new ScanResult( 862 device, ScanRecord.parseFromBytes(new byte[0]), rssi, timestampNanos)); 863 } 864 return results; 865 } 866 867 @VisibleForTesting parseTimestampNanos(byte[] data)868 long parseTimestampNanos(byte[] data) { 869 long timestampUnit = NumberUtils.littleEndianByteArrayToInt(data); 870 // Timestamp is in every 50 ms. 871 return TimeUnit.MILLISECONDS.toNanos(timestampUnit * 50); 872 } 873 parseFullResults(int numRecords, byte[] batchRecord)874 private Set<ScanResult> parseFullResults(int numRecords, byte[] batchRecord) { 875 Log.d(TAG, "Batch record : " + Arrays.toString(batchRecord)); 876 Set<ScanResult> results = new HashSet<ScanResult>(numRecords); 877 int position = 0; 878 long now = SystemClock.elapsedRealtimeNanos(); 879 while (position < batchRecord.length) { 880 byte[] address = extractBytes(batchRecord, position, 6); 881 // TODO: remove temp hack. 882 Utils.reverse(address); 883 BluetoothDevice device = mAdapter.getRemoteDevice(address); 884 position += 6; 885 // Skip address type. 886 position++; 887 // Skip tx power level. 888 position++; 889 int rssi = batchRecord[position++]; 890 long timestampNanos = now - parseTimestampNanos(extractBytes(batchRecord, position, 2)); 891 position += 2; 892 893 // Combine advertise packet and scan response packet. 894 int advertisePacketLen = batchRecord[position++]; 895 byte[] advertiseBytes = extractBytes(batchRecord, position, advertisePacketLen); 896 position += advertisePacketLen; 897 int scanResponsePacketLen = batchRecord[position++]; 898 byte[] scanResponseBytes = extractBytes(batchRecord, position, scanResponsePacketLen); 899 position += scanResponsePacketLen; 900 byte[] scanRecord = new byte[advertisePacketLen + scanResponsePacketLen]; 901 System.arraycopy(advertiseBytes, 0, scanRecord, 0, advertisePacketLen); 902 System.arraycopy( 903 scanResponseBytes, 0, scanRecord, advertisePacketLen, scanResponsePacketLen); 904 Log.d(TAG, "ScanRecord : " + Arrays.toString(scanRecord)); 905 results.add( 906 new ScanResult( 907 device, ScanRecord.parseFromBytes(scanRecord), rssi, timestampNanos)); 908 } 909 return results; 910 } 911 onBatchScanThresholdCrossed(int clientIf)912 void onBatchScanThresholdCrossed(int clientIf) { 913 Log.d(TAG, "onBatchScanThresholdCrossed() - clientIf=" + clientIf); 914 flushPendingBatchResultsInternal(clientIf); 915 } 916 createOnTrackAdvFoundLostObject( int clientIf, int advPacketLen, byte[] advPacket, int scanResponseLen, byte[] scanResponse, int filtIndex, int advState, int advInfoPresent, String address, int addrType, int txPower, int rssiValue, int timeStamp)917 AdvtFilterOnFoundOnLostInfo createOnTrackAdvFoundLostObject( 918 int clientIf, 919 int advPacketLen, 920 byte[] advPacket, 921 int scanResponseLen, 922 byte[] scanResponse, 923 int filtIndex, 924 int advState, 925 int advInfoPresent, 926 String address, 927 int addrType, 928 int txPower, 929 int rssiValue, 930 int timeStamp) { 931 return new AdvtFilterOnFoundOnLostInfo( 932 clientIf, 933 advPacketLen, 934 ByteString.copyFrom(advPacket), 935 scanResponseLen, 936 ByteString.copyFrom(scanResponse), 937 filtIndex, 938 advState, 939 advInfoPresent, 940 address, 941 addrType, 942 txPower, 943 rssiValue, 944 timeStamp); 945 } 946 onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo)947 void onTrackAdvFoundLost(AdvtFilterOnFoundOnLostInfo trackingInfo) throws RemoteException { 948 Log.d( 949 TAG, 950 "onTrackAdvFoundLost() - scannerId= " 951 + trackingInfo.clientIf() 952 + " address = " 953 + trackingInfo.address() 954 + " addressType = " 955 + trackingInfo.addressType() 956 + " adv_state = " 957 + trackingInfo.advState()); 958 959 ScannerMap.ScannerApp app = mScannerMap.getById(trackingInfo.clientIf()); 960 if (app == null) { 961 Log.e(TAG, "app is null"); 962 return; 963 } 964 965 BluetoothDevice device = 966 mAdapter.getRemoteLeDevice(trackingInfo.address(), trackingInfo.addressType()); 967 int advertiserState = trackingInfo.advState(); 968 ScanResult result = 969 new ScanResult( 970 device, 971 ScanRecord.parseFromBytes(trackingInfo.getResult()), 972 trackingInfo.rssiValue(), 973 SystemClock.elapsedRealtimeNanos()); 974 975 for (ScanClient client : mScanManager.getRegularScanQueue()) { 976 if (client.mScannerId == trackingInfo.clientIf()) { 977 ScanSettings settings = client.mSettings; 978 if ((advertiserState == ADVT_STATE_ONFOUND) 979 && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) 980 != 0)) { 981 if (app.mCallback != null) { 982 app.mCallback.onFoundOrLost(true, result); 983 } else { 984 sendResultByPendingIntent( 985 app.mInfo, result, ScanSettings.CALLBACK_TYPE_FIRST_MATCH, client); 986 } 987 } else if ((advertiserState == ADVT_STATE_ONLOST) 988 && ((settings.getCallbackType() & ScanSettings.CALLBACK_TYPE_MATCH_LOST) 989 != 0)) { 990 if (app.mCallback != null) { 991 app.mCallback.onFoundOrLost(false, result); 992 } else { 993 sendResultByPendingIntent( 994 app.mInfo, result, ScanSettings.CALLBACK_TYPE_MATCH_LOST, client); 995 } 996 } else { 997 Log.d( 998 TAG, 999 "Not reporting onlost/onfound : " 1000 + advertiserState 1001 + " scannerId = " 1002 + client.mScannerId 1003 + " callbackType " 1004 + settings.getCallbackType()); 1005 } 1006 } 1007 } 1008 } 1009 1010 /** Callback method for configuration of scan parameters. */ onScanParamSetupCompleted(int status, int scannerId)1011 void onScanParamSetupCompleted(int status, int scannerId) { 1012 Log.d(TAG, "onScanParamSetupCompleted() - scannerId=" + scannerId + ", status=" + status); 1013 ScannerMap.ScannerApp app = mScannerMap.getById(scannerId); 1014 if (app == null || app.mCallback == null) { 1015 Log.e(TAG, "Advertise app or callback is null"); 1016 return; 1017 } 1018 } 1019 1020 // callback from ScanManager for dispatch of errors apps. onScanManagerErrorCallback(int scannerId, int errorCode)1021 void onScanManagerErrorCallback(int scannerId, int errorCode) throws RemoteException { 1022 ScannerMap.ScannerApp app = mScannerMap.getById(scannerId); 1023 if (app == null) { 1024 Log.e(TAG, "App null"); 1025 return; 1026 } 1027 if (app.mCallback != null) { 1028 app.mCallback.onScanManagerErrorCallback(errorCode); 1029 } else { 1030 try { 1031 sendErrorByPendingIntent(app.mInfo, errorCode); 1032 } catch (PendingIntent.CanceledException e) { 1033 Log.e(TAG, "Error sending error code via PendingIntent:" + e); 1034 } 1035 } 1036 } 1037 msftMonitorHandleFromFilterIndex(int filterIndex)1038 int msftMonitorHandleFromFilterIndex(int filterIndex) { 1039 if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) { 1040 Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist"); 1041 return -1; 1042 } 1043 return mFilterIndexToMsftAdvMonitorMap.get(filterIndex); 1044 } 1045 onMsftAdvMonitorAdd(int filterIndex, int monitorHandle, int status)1046 void onMsftAdvMonitorAdd(int filterIndex, int monitorHandle, int status) { 1047 if (status != 0) { 1048 Log.e( 1049 TAG, 1050 "Error adding advertisement monitor with filter index '" + filterIndex + "'"); 1051 return; 1052 } 1053 if (mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) { 1054 Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' already added"); 1055 return; 1056 } 1057 mFilterIndexToMsftAdvMonitorMap.put(filterIndex, monitorHandle); 1058 } 1059 onMsftAdvMonitorRemove(int filterIndex, int status)1060 void onMsftAdvMonitorRemove(int filterIndex, int status) { 1061 if (status != 0) { 1062 Log.e( 1063 TAG, 1064 "Error removing advertisement monitor with filter index '" + filterIndex + "'"); 1065 } 1066 if (!mFilterIndexToMsftAdvMonitorMap.containsKey(filterIndex)) { 1067 Log.e(TAG, "Monitor with filterIndex'" + filterIndex + "' does not exist"); 1068 return; 1069 } 1070 mFilterIndexToMsftAdvMonitorMap.remove(filterIndex); 1071 } 1072 onMsftAdvMonitorEnable(int status)1073 void onMsftAdvMonitorEnable(int status) { 1074 if (status != 0) { 1075 Log.e(TAG, "Error enabling advertisement monitor"); 1076 } 1077 } 1078 1079 /************************************************************************** 1080 * Scan functions - Shared CLIENT/SERVER 1081 *************************************************************************/ 1082 1083 @RequiresPermission(BLUETOOTH_SCAN) registerScanner( IScannerCallback callback, WorkSource workSource, AttributionSource source)1084 void registerScanner( 1085 IScannerCallback callback, WorkSource workSource, AttributionSource source) { 1086 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "registerScanner")) { 1087 return; 1088 } 1089 1090 enforceImpersonationPermissionIfNeeded(workSource); 1091 1092 AppScanStats app = mScannerMap.getAppScanStatsByUid(Binder.getCallingUid()); 1093 if (app != null 1094 && app.isScanningTooFrequently() 1095 && !Utils.checkCallerHasPrivilegedPermission(mAdapterService)) { 1096 Log.e(TAG, "App '" + app.mAppName + "' is scanning too frequently"); 1097 try { 1098 callback.onScannerRegistered(ScanCallback.SCAN_FAILED_SCANNING_TOO_FREQUENTLY, -1); 1099 } catch (RemoteException e) { 1100 Log.e(TAG, "Exception: " + e); 1101 } 1102 return; 1103 } 1104 registerScannerInternal(callback, source, workSource); 1105 } 1106 1107 /** Intended for internal use within the Bluetooth app. Bypass permission check */ registerScannerInternal( IScannerCallback callback, AttributionSource attrSource, WorkSource workSource)1108 public void registerScannerInternal( 1109 IScannerCallback callback, AttributionSource attrSource, WorkSource workSource) { 1110 UUID uuid = UUID.randomUUID(); 1111 Log.d(TAG, "registerScanner() - UUID=" + uuid); 1112 1113 mScannerMap.add(uuid, attrSource, workSource, callback, mAdapterService, this); 1114 mScanManager.registerScanner(uuid); 1115 } 1116 1117 @RequiresPermission(BLUETOOTH_SCAN) unregisterScanner(int scannerId, AttributionSource source)1118 void unregisterScanner(int scannerId, AttributionSource source) { 1119 if (!checkScanPermissionForDataDelivery( 1120 mAdapterService, source, TAG, "unregisterScanner")) { 1121 return; 1122 } 1123 1124 unregisterScannerInternal(scannerId); 1125 } 1126 1127 /** Intended for internal use within the Bluetooth app. Bypass permission check */ unregisterScannerInternal(int scannerId)1128 public void unregisterScannerInternal(int scannerId) { 1129 Log.d(TAG, "unregisterScanner() - scannerId=" + scannerId); 1130 mScannerMap.remove(scannerId); 1131 mScanManager.unregisterScanner(scannerId); 1132 } 1133 getAssociatedDevices(String callingPackage)1134 private List<String> getAssociatedDevices(String callingPackage) { 1135 if (mCompanionManager == null) { 1136 return Collections.emptyList(); 1137 } 1138 1139 final long identity = Binder.clearCallingIdentity(); 1140 try { 1141 return mCompanionManager.getAllAssociations().stream() 1142 .filter( 1143 info -> 1144 info.getPackageName().equals(callingPackage) 1145 && !info.isSelfManaged() 1146 && info.getDeviceMacAddress() != null) 1147 .map(info -> info.getDeviceMacAddress().toString()) 1148 .collect(Collectors.toList()); 1149 } catch (SecurityException se) { 1150 // Not an app with associated devices 1151 } catch (Exception e) { 1152 Log.e(TAG, "Cannot check device associations for " + callingPackage, e); 1153 } finally { 1154 Binder.restoreCallingIdentity(identity); 1155 } 1156 return Collections.emptyList(); 1157 } 1158 1159 @RequiresPermission(BLUETOOTH_SCAN) startScan( int scannerId, ScanSettings settings, List<ScanFilter> filters, AttributionSource source)1160 void startScan( 1161 int scannerId, 1162 ScanSettings settings, 1163 List<ScanFilter> filters, 1164 AttributionSource source) { 1165 Log.d(TAG, "Start scan with filters"); 1166 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "startScan")) { 1167 return; 1168 } 1169 1170 enforcePrivilegedPermissionIfNeeded(settings); 1171 String callingPackage = source.getPackageName(); 1172 settings = enforceReportDelayFloor(settings); 1173 enforcePrivilegedPermissionIfNeeded(filters); 1174 final ScanClient scanClient = new ScanClient(scannerId, settings, filters); 1175 scanClient.mUserHandle = Binder.getCallingUserHandle(); 1176 mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); 1177 scanClient.mEligibleForSanitizedExposureNotification = 1178 callingPackage.equals(mExposureNotificationPackage); 1179 1180 scanClient.mHasDisavowedLocation = 1181 Utils.hasDisavowedLocationForScan(mAdapterService, source, mTestModeEnabled); 1182 1183 scanClient.mIsQApp = 1184 checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q); 1185 if (!scanClient.mHasDisavowedLocation) { 1186 if (scanClient.mIsQApp) { 1187 scanClient.mHasLocationPermission = 1188 Utils.checkCallerHasFineLocation( 1189 mAdapterService, source, scanClient.mUserHandle); 1190 } else { 1191 scanClient.mHasLocationPermission = 1192 Utils.checkCallerHasCoarseOrFineLocation( 1193 mAdapterService, source, scanClient.mUserHandle); 1194 } 1195 } 1196 scanClient.mHasNetworkSettingsPermission = 1197 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService); 1198 scanClient.mHasNetworkSetupWizardPermission = 1199 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService); 1200 scanClient.mHasScanWithoutLocationPermission = 1201 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService); 1202 scanClient.mAssociatedDevices = getAssociatedDevices(callingPackage); 1203 1204 startScan(scannerId, settings, filters, scanClient); 1205 } 1206 1207 /** Intended for internal use within the Bluetooth app. Bypass permission check */ startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters)1208 public void startScanInternal(int scannerId, ScanSettings settings, List<ScanFilter> filters) { 1209 final ScanClient scanClient = new ScanClient(scannerId, settings, filters); 1210 scanClient.mIsInternalClient = true; 1211 scanClient.mUserHandle = Binder.getCallingUserHandle(); 1212 scanClient.mEligibleForSanitizedExposureNotification = false; 1213 scanClient.mHasDisavowedLocation = false; 1214 scanClient.mIsQApp = true; 1215 scanClient.mHasNetworkSettingsPermission = 1216 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService); 1217 scanClient.mHasNetworkSetupWizardPermission = 1218 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService); 1219 scanClient.mHasScanWithoutLocationPermission = 1220 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService); 1221 scanClient.mAssociatedDevices = Collections.emptyList(); 1222 1223 startScan(scannerId, settings, filters, scanClient); 1224 } 1225 startScan( int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient)1226 private void startScan( 1227 int scannerId, ScanSettings settings, List<ScanFilter> filters, ScanClient scanClient) { 1228 AppScanStats app = mScannerMap.getAppScanStatsById(scannerId); 1229 if (app != null) { 1230 scanClient.mStats = app; 1231 mScanManager.fetchAppForegroundState(scanClient); 1232 boolean isFilteredScan = (filters != null) && !filters.isEmpty(); 1233 boolean isCallbackScan = false; 1234 1235 ScannerMap.ScannerApp cbApp = mScannerMap.getById(scannerId); 1236 if (cbApp != null) { 1237 isCallbackScan = cbApp.mCallback != null; 1238 } 1239 app.recordScanStart( 1240 settings, 1241 filters, 1242 isFilteredScan, 1243 isCallbackScan, 1244 scannerId, 1245 cbApp == null ? null : cbApp.mAttributionTag); 1246 } 1247 1248 mScanManager.startScan(scanClient); 1249 } 1250 1251 @RequiresPermission(BLUETOOTH_SCAN) registerPiAndStartScan( PendingIntent pendingIntent, ScanSettings settings, List<ScanFilter> filters, AttributionSource source)1252 void registerPiAndStartScan( 1253 PendingIntent pendingIntent, 1254 ScanSettings settings, 1255 List<ScanFilter> filters, 1256 AttributionSource source) { 1257 Log.d(TAG, "Start scan with filters, for PendingIntent"); 1258 if (!checkScanPermissionForDataDelivery( 1259 mAdapterService, source, TAG, "registerPiAndStartScan")) { 1260 return; 1261 } 1262 1263 enforcePrivilegedPermissionIfNeeded(settings); 1264 settings = enforceReportDelayFloor(settings); 1265 enforcePrivilegedPermissionIfNeeded(filters); 1266 UUID uuid = UUID.randomUUID(); 1267 String callingPackage = source.getPackageName(); 1268 int callingUid = source.getUid(); 1269 PendingIntentInfo piInfo = 1270 new PendingIntentInfo(pendingIntent, settings, filters, callingPackage, callingUid); 1271 Log.d( 1272 TAG, 1273 "startScan(PI) -" 1274 + (" UUID=" + uuid) 1275 + (" Package=" + callingPackage) 1276 + (" UID=" + callingUid)); 1277 1278 // Don't start scan if the Pi scan already in mScannerMap. 1279 if (mScannerMap.getByPendingIntentInfo(pendingIntent) != null) { 1280 Log.d(TAG, "Don't startScan(PI) since the same Pi scan already in mScannerMap."); 1281 return; 1282 } 1283 1284 ScannerMap.ScannerApp app = mScannerMap.add(uuid, source, piInfo, mAdapterService, this); 1285 1286 app.mUserHandle = UserHandle.getUserHandleForUid(Binder.getCallingUid()); 1287 mAppOps.checkPackage(Binder.getCallingUid(), callingPackage); 1288 app.mEligibleForSanitizedExposureNotification = 1289 callingPackage.equals(mExposureNotificationPackage); 1290 1291 app.mHasDisavowedLocation = 1292 Utils.hasDisavowedLocationForScan(mAdapterService, source, mTestModeEnabled); 1293 1294 if (!app.mHasDisavowedLocation) { 1295 try { 1296 if (checkCallerTargetSdk(mAdapterService, callingPackage, Build.VERSION_CODES.Q)) { 1297 app.mHasLocationPermission = 1298 Utils.checkCallerHasFineLocation( 1299 mAdapterService, source, app.mUserHandle); 1300 } else { 1301 app.mHasLocationPermission = 1302 Utils.checkCallerHasCoarseOrFineLocation( 1303 mAdapterService, source, app.mUserHandle); 1304 } 1305 } catch (SecurityException se) { 1306 // No need to throw here. Just mark as not granted. 1307 app.mHasLocationPermission = false; 1308 } 1309 } 1310 app.mHasNetworkSettingsPermission = 1311 Utils.checkCallerHasNetworkSettingsPermission(mAdapterService); 1312 app.mHasNetworkSetupWizardPermission = 1313 Utils.checkCallerHasNetworkSetupWizardPermission(mAdapterService); 1314 app.mHasScanWithoutLocationPermission = 1315 Utils.checkCallerHasScanWithoutLocationPermission(mAdapterService); 1316 app.mAssociatedDevices = getAssociatedDevices(callingPackage); 1317 mScanManager.registerScanner(uuid); 1318 1319 // If this fails, we should stop the scan immediately. 1320 if (!pendingIntent.addCancelListener(Runnable::run, mScanIntentCancelListener)) { 1321 Log.d(TAG, "scanning PendingIntent is already cancelled, stopping scan."); 1322 stopScan(pendingIntent, source); 1323 } 1324 } 1325 1326 /** Start a scan with pending intent. */ 1327 @VisibleForTesting continuePiStartScan(int scannerId, ScannerMap.ScannerApp app)1328 void continuePiStartScan(int scannerId, ScannerMap.ScannerApp app) { 1329 final PendingIntentInfo piInfo = app.mInfo; 1330 final ScanClient scanClient = 1331 new ScanClient(scannerId, piInfo.settings, piInfo.filters, piInfo.callingUid); 1332 scanClient.mHasLocationPermission = app.mHasLocationPermission; 1333 scanClient.mUserHandle = app.mUserHandle; 1334 scanClient.mIsQApp = 1335 checkCallerTargetSdk(mAdapterService, app.mName, Build.VERSION_CODES.Q); 1336 scanClient.mEligibleForSanitizedExposureNotification = 1337 app.mEligibleForSanitizedExposureNotification; 1338 scanClient.mHasNetworkSettingsPermission = app.mHasNetworkSettingsPermission; 1339 scanClient.mHasNetworkSetupWizardPermission = app.mHasNetworkSetupWizardPermission; 1340 scanClient.mHasScanWithoutLocationPermission = app.mHasScanWithoutLocationPermission; 1341 scanClient.mAssociatedDevices = app.mAssociatedDevices; 1342 scanClient.mHasDisavowedLocation = app.mHasDisavowedLocation; 1343 1344 AppScanStats scanStats = mScannerMap.getAppScanStatsById(scannerId); 1345 if (scanStats != null) { 1346 scanClient.mStats = scanStats; 1347 mScanManager.fetchAppForegroundState(scanClient); 1348 boolean isFilteredScan = (piInfo.filters != null) && !piInfo.filters.isEmpty(); 1349 scanStats.recordScanStart( 1350 piInfo.settings, 1351 piInfo.filters, 1352 isFilteredScan, 1353 false, 1354 scannerId, 1355 app.mAttributionTag); 1356 } 1357 1358 mScanManager.startScan(scanClient); 1359 } 1360 1361 @RequiresPermission(BLUETOOTH_SCAN) flushPendingBatchResults(int scannerId, AttributionSource source)1362 void flushPendingBatchResults(int scannerId, AttributionSource source) { 1363 if (!checkScanPermissionForDataDelivery( 1364 mAdapterService, source, TAG, "flushPendingBatchResults")) { 1365 return; 1366 } 1367 flushPendingBatchResultsInternal(scannerId); 1368 } 1369 flushPendingBatchResultsInternal(int scannerId)1370 private void flushPendingBatchResultsInternal(int scannerId) { 1371 Log.d(TAG, "flushPendingBatchResultsInternal - scannerId=" + scannerId); 1372 mScanManager.flushBatchScanResults(new ScanClient(scannerId)); 1373 } 1374 1375 @RequiresPermission(BLUETOOTH_SCAN) stopScan(int scannerId, AttributionSource source)1376 void stopScan(int scannerId, AttributionSource source) { 1377 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "stopScan")) { 1378 return; 1379 } 1380 stopScanInternal(scannerId); 1381 } 1382 1383 /** Intended for internal use within the Bluetooth app. Bypass permission check */ stopScanInternal(int scannerId)1384 public void stopScanInternal(int scannerId) { 1385 int scanQueueSize = 1386 mScanManager.getBatchScanQueue().size() + mScanManager.getRegularScanQueue().size(); 1387 Log.d(TAG, "stopScan() - queue size =" + scanQueueSize); 1388 1389 AppScanStats app = mScannerMap.getAppScanStatsById(scannerId); 1390 if (app != null) { 1391 app.recordScanStop(scannerId); 1392 } 1393 1394 mScanManager.stopScan(scannerId); 1395 } 1396 1397 @RequiresPermission(BLUETOOTH_SCAN) stopScan(PendingIntent intent, AttributionSource source)1398 void stopScan(PendingIntent intent, AttributionSource source) { 1399 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "stopScan")) { 1400 return; 1401 } 1402 stopScanInternal(intent); 1403 } 1404 1405 /** Intended for internal use within the Bluetooth app. Bypass permission check */ stopScanInternal(PendingIntent intent)1406 private void stopScanInternal(PendingIntent intent) { 1407 ScannerMap.ScannerApp app = mScannerMap.getByPendingIntentInfo(intent); 1408 Log.v(TAG, "stopScan(PendingIntent): app found = " + app); 1409 if (app != null) { 1410 intent.removeCancelListener(mScanIntentCancelListener); 1411 final int scannerId = app.mId; 1412 stopScanInternal(scannerId); 1413 // Also unregister the scanner 1414 unregisterScannerInternal(scannerId); 1415 } 1416 } 1417 1418 /************************************************************************** 1419 * PERIODIC SCANNING 1420 *************************************************************************/ 1421 @RequiresPermission(BLUETOOTH_SCAN) registerSync( ScanResult scanResult, int skip, int timeout, IPeriodicAdvertisingCallback callback, AttributionSource source)1422 void registerSync( 1423 ScanResult scanResult, 1424 int skip, 1425 int timeout, 1426 IPeriodicAdvertisingCallback callback, 1427 AttributionSource source) { 1428 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "registerSync")) { 1429 return; 1430 } 1431 mPeriodicScanManager.startSync(scanResult, skip, timeout, callback); 1432 } 1433 1434 @RequiresPermission(BLUETOOTH_SCAN) unregisterSync(IPeriodicAdvertisingCallback callback, AttributionSource source)1435 void unregisterSync(IPeriodicAdvertisingCallback callback, AttributionSource source) { 1436 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "unregisterSync")) { 1437 return; 1438 } 1439 mPeriodicScanManager.stopSync(callback); 1440 } 1441 1442 @RequiresPermission(BLUETOOTH_SCAN) transferSync( BluetoothDevice bda, int serviceData, int syncHandle, AttributionSource source)1443 void transferSync( 1444 BluetoothDevice bda, int serviceData, int syncHandle, AttributionSource source) { 1445 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "transferSync")) { 1446 return; 1447 } 1448 mPeriodicScanManager.transferSync(bda, serviceData, syncHandle); 1449 } 1450 1451 @RequiresPermission(BLUETOOTH_SCAN) transferSetInfo( BluetoothDevice bda, int serviceData, int advHandle, IPeriodicAdvertisingCallback callback, AttributionSource source)1452 void transferSetInfo( 1453 BluetoothDevice bda, 1454 int serviceData, 1455 int advHandle, 1456 IPeriodicAdvertisingCallback callback, 1457 AttributionSource source) { 1458 if (!checkScanPermissionForDataDelivery(mAdapterService, source, TAG, "transferSetInfo")) { 1459 return; 1460 } 1461 mPeriodicScanManager.transferSetInfo(bda, serviceData, advHandle, callback); 1462 } 1463 1464 @RequiresPermission(BLUETOOTH_SCAN) numHwTrackFiltersAvailable(AttributionSource source)1465 int numHwTrackFiltersAvailable(AttributionSource source) { 1466 if (!checkScanPermissionForDataDelivery( 1467 mAdapterService, source, TAG, "numHwTrackFiltersAvailable")) { 1468 return 0; 1469 } 1470 return (mAdapterService.getTotalNumOfTrackableAdvertisements() 1471 - mScanManager.getCurrentUsedTrackingAdvertisement()); 1472 } 1473 1474 /** 1475 * DeathRecipient handler used to unregister applications that disconnect ungracefully (ie. 1476 * crash or forced close). 1477 */ 1478 class ScannerDeathRecipient implements IBinder.DeathRecipient { 1479 int mScannerId; 1480 private final String mPackageName; 1481 ScannerDeathRecipient(int scannerId, String packageName)1482 ScannerDeathRecipient(int scannerId, String packageName) { 1483 mScannerId = scannerId; 1484 mPackageName = packageName; 1485 } 1486 1487 @Override binderDied()1488 public void binderDied() { 1489 Log.d( 1490 TAG, 1491 "Binder is dead - unregistering scanner (" 1492 + mPackageName 1493 + " " 1494 + mScannerId 1495 + ")!"); 1496 1497 ScanClient client = getScanClient(mScannerId); 1498 if (client != null) { 1499 handleDeadScanClient(client); 1500 } 1501 } 1502 getScanClient(int clientIf)1503 private ScanClient getScanClient(int clientIf) { 1504 for (ScanClient client : mScanManager.getRegularScanQueue()) { 1505 if (client.mScannerId == clientIf) { 1506 return client; 1507 } 1508 } 1509 for (ScanClient client : mScanManager.getBatchScanQueue()) { 1510 if (client.mScannerId == clientIf) { 1511 return client; 1512 } 1513 } 1514 return null; 1515 } 1516 } 1517 needsPrivilegedPermissionForScan(ScanSettings settings)1518 private boolean needsPrivilegedPermissionForScan(ScanSettings settings) { 1519 // BLE scan only mode needs special permission. 1520 if (mAdapterService.getState() != BluetoothAdapter.STATE_ON) { 1521 return true; 1522 } 1523 1524 // Regular scan, no special permission. 1525 if (settings == null) { 1526 return false; 1527 } 1528 1529 // Ambient discovery mode, needs privileged permission. 1530 if (settings.getScanMode() == ScanSettings.SCAN_MODE_AMBIENT_DISCOVERY) { 1531 return true; 1532 } 1533 1534 // Regular scan, no special permission. 1535 if (settings.getReportDelayMillis() == 0) { 1536 return false; 1537 } 1538 1539 // Batch scan, truncated mode needs permission. 1540 return settings.getScanResultType() == ScanSettings.SCAN_RESULT_TYPE_ABBREVIATED; 1541 } 1542 1543 /* 1544 * The ScanFilter#setDeviceAddress API overloads are @SystemApi access methods. This 1545 * requires that the permissions be BLUETOOTH_PRIVILEGED. 1546 */ 1547 @SuppressLint("AndroidFrameworkRequiresPermission") enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters)1548 private void enforcePrivilegedPermissionIfNeeded(List<ScanFilter> filters) { 1549 Log.d(TAG, "enforcePrivilegedPermissionIfNeeded(" + filters + ")"); 1550 // Some 3p API cases may have null filters, need to allow 1551 if (filters != null) { 1552 for (ScanFilter filter : filters) { 1553 // The only case to enforce here is if there is an address 1554 // If there is an address, enforce if the correct combination criteria is met. 1555 if (filter.getDeviceAddress() != null) { 1556 // At this point we have an address, that means a caller used the 1557 // setDeviceAddress(address) public API for the ScanFilter 1558 // We don't want to enforce if the type is PUBLIC and the IRK is null 1559 // However, if we have a different type that means the caller used a new 1560 // @SystemApi such as setDeviceAddress(address, type) or 1561 // setDeviceAddress(address, type, irk) which are both @SystemApi and require 1562 // permissions to be enforced 1563 if (filter.getAddressType() == BluetoothDevice.ADDRESS_TYPE_PUBLIC 1564 && filter.getIrk() == null) { 1565 // Do not enforce 1566 } else { 1567 mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); 1568 } 1569 } 1570 } 1571 } 1572 } 1573 1574 @SuppressLint("AndroidFrameworkRequiresPermission") enforcePrivilegedPermissionIfNeeded(ScanSettings settings)1575 private void enforcePrivilegedPermissionIfNeeded(ScanSettings settings) { 1576 if (needsPrivilegedPermissionForScan(settings)) { 1577 mAdapterService.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, null); 1578 } 1579 } 1580 1581 // Enforce caller has UPDATE_DEVICE_STATS permission, which allows the caller to blame other 1582 // apps for Bluetooth usage. A {@link SecurityException} will be thrown if the caller app does 1583 // not have UPDATE_DEVICE_STATS permission. 1584 @RequiresPermission(UPDATE_DEVICE_STATS) enforceImpersonationPermission()1585 private void enforceImpersonationPermission() { 1586 mAdapterService.enforceCallingOrSelfPermission( 1587 UPDATE_DEVICE_STATS, "Need UPDATE_DEVICE_STATS permission"); 1588 } 1589 1590 @SuppressLint("AndroidFrameworkRequiresPermission") enforceImpersonationPermissionIfNeeded(WorkSource workSource)1591 private void enforceImpersonationPermissionIfNeeded(WorkSource workSource) { 1592 if (workSource != null) { 1593 enforceImpersonationPermission(); 1594 } 1595 } 1596 1597 /** 1598 * Ensures the report delay is either 0 or at least the floor value (5000ms) 1599 * 1600 * @param settings are the scan settings passed into a request to start le scanning 1601 * @return the passed in ScanSettings object if the report delay is 0 or above the floor value; 1602 * a new ScanSettings object with the report delay being the floor value if the original 1603 * report delay was between 0 and the floor value (exclusive of both) 1604 */ 1605 @VisibleForTesting enforceReportDelayFloor(ScanSettings settings)1606 ScanSettings enforceReportDelayFloor(ScanSettings settings) { 1607 if (settings.getReportDelayMillis() == 0) { 1608 return settings; 1609 } 1610 1611 // Need to clear identity to pass device config permission check 1612 final long callerToken = Binder.clearCallingIdentity(); 1613 try { 1614 long floor = 1615 DeviceConfig.getLong( 1616 DeviceConfig.NAMESPACE_BLUETOOTH, 1617 "report_delay", 1618 DEFAULT_REPORT_DELAY_FLOOR); 1619 1620 if (settings.getReportDelayMillis() > floor) { 1621 return settings; 1622 } else { 1623 return new ScanSettings.Builder() 1624 .setCallbackType(settings.getCallbackType()) 1625 .setLegacy(settings.getLegacy()) 1626 .setMatchMode(settings.getMatchMode()) 1627 .setNumOfMatches(settings.getNumOfMatches()) 1628 .setPhy(settings.getPhy()) 1629 .setReportDelay(floor) 1630 .setScanMode(settings.getScanMode()) 1631 .setScanResultType(settings.getScanResultType()) 1632 .build(); 1633 } 1634 } finally { 1635 Binder.restoreCallingIdentity(callerToken); 1636 } 1637 } 1638 dumpRegisterId(StringBuilder sb)1639 public void dumpRegisterId(StringBuilder sb) { 1640 sb.append(" Scanner:\n"); 1641 mScannerMap.dumpApps(sb, ProfileService::println); 1642 } 1643 dump(StringBuilder sb)1644 public void dump(StringBuilder sb) { 1645 sb.append("GATT Scanner Map\n"); 1646 mScannerMap.dump(sb); 1647 } 1648 } 1649