1 /* 2 * Copyright (C) 2012-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.btservice; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.Manifest.permission.BLUETOOTH_SCAN; 21 22 import android.app.ActivityThread; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothAssignedNumbers; 25 import android.bluetooth.BluetoothClass; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothHeadset; 28 import android.bluetooth.BluetoothProfile; 29 import android.bluetooth.IBluetoothConnectionCallback; 30 import android.content.Attributable; 31 import android.content.BroadcastReceiver; 32 import android.content.Context; 33 import android.content.Intent; 34 import android.content.IntentFilter; 35 import android.net.MacAddress; 36 import android.os.Handler; 37 import android.os.Looper; 38 import android.os.Message; 39 import android.os.ParcelUuid; 40 import android.os.RemoteException; 41 import android.util.Log; 42 43 import com.android.bluetooth.BluetoothStatsLog; 44 import com.android.bluetooth.R; 45 import com.android.bluetooth.Utils; 46 import com.android.bluetooth.hfp.HeadsetHalConstants; 47 import com.android.internal.annotations.VisibleForTesting; 48 49 import java.util.ArrayList; 50 import java.util.HashMap; 51 import java.util.HashSet; 52 import java.util.LinkedList; 53 import java.util.Queue; 54 import java.util.Set; 55 import java.util.function.Predicate; 56 57 final class RemoteDevices { 58 private static final boolean DBG = false; 59 private static final String TAG = "BluetoothRemoteDevices"; 60 61 // Maximum number of device properties to remember 62 private static final int MAX_DEVICE_QUEUE_SIZE = 200; 63 64 private static BluetoothAdapter sAdapter; 65 private static AdapterService sAdapterService; 66 private static ArrayList<BluetoothDevice> sSdpTracker; 67 private final Object mObject = new Object(); 68 69 private static final int UUID_INTENT_DELAY = 6000; 70 private static final int MESSAGE_UUID_INTENT = 1; 71 72 private final HashMap<String, DeviceProperties> mDevices; 73 private Queue<String> mDeviceQueue; 74 75 private final Handler mHandler; 76 private class RemoteDevicesHandler extends Handler { 77 78 /** 79 * Handler must be created from an explicit looper to avoid threading ambiguity 80 * @param looper The looper that this handler should be executed on 81 */ RemoteDevicesHandler(Looper looper)82 RemoteDevicesHandler(Looper looper) { 83 super(looper); 84 } 85 86 @Override handleMessage(Message msg)87 public void handleMessage(Message msg) { 88 switch (msg.what) { 89 case MESSAGE_UUID_INTENT: 90 BluetoothDevice device = (BluetoothDevice) msg.obj; 91 Attributable.setAttributionSource(device, 92 ActivityThread.currentAttributionSource()); 93 if (device != null) { 94 DeviceProperties prop = getDeviceProperties(device); 95 sendUuidIntent(device, prop); 96 } 97 break; 98 } 99 } 100 } 101 102 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 103 @Override 104 public void onReceive(Context context, Intent intent) { 105 String action = intent.getAction(); 106 switch (action) { 107 case BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED: 108 onHfIndicatorValueChanged(intent); 109 break; 110 case BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT: 111 onVendorSpecificHeadsetEvent(intent); 112 break; 113 case BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED: 114 onHeadsetConnectionStateChanged(intent); 115 break; 116 default: 117 Log.w(TAG, "Unhandled intent: " + intent); 118 break; 119 } 120 } 121 }; 122 123 /** 124 * Predicate that tests if the given {@link BluetoothDevice} is well-known 125 * to be used for physical location. 126 */ 127 private final Predicate<BluetoothDevice> mLocationDenylistPredicate = (device) -> { 128 final MacAddress parsedAddress = MacAddress.fromString(device.getAddress()); 129 if (sAdapterService.getLocationDenylistMac().test(parsedAddress.toByteArray())) { 130 Log.v(TAG, "Skipping device matching denylist: " + parsedAddress); 131 return true; 132 } 133 final String name = Utils.getName(device); 134 if (sAdapterService.getLocationDenylistName().test(name)) { 135 Log.v(TAG, "Skipping name matching denylist: " + name); 136 return true; 137 } 138 return false; 139 }; 140 RemoteDevices(AdapterService service, Looper looper)141 RemoteDevices(AdapterService service, Looper looper) { 142 sAdapter = BluetoothAdapter.getDefaultAdapter(); 143 sAdapterService = service; 144 sSdpTracker = new ArrayList<BluetoothDevice>(); 145 mDevices = new HashMap<String, DeviceProperties>(); 146 mDeviceQueue = new LinkedList<String>(); 147 mHandler = new RemoteDevicesHandler(looper); 148 } 149 150 /** 151 * Init should be called before using this RemoteDevices object 152 */ init()153 void init() { 154 IntentFilter filter = new IntentFilter(); 155 filter.addAction(BluetoothHeadset.ACTION_HF_INDICATORS_VALUE_CHANGED); 156 filter.addAction(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT); 157 filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." 158 + BluetoothAssignedNumbers.PLANTRONICS); 159 filter.addCategory(BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_COMPANY_ID_CATEGORY + "." 160 + BluetoothAssignedNumbers.APPLE); 161 filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 162 sAdapterService.registerReceiver(mReceiver, filter); 163 } 164 165 /** 166 * Clean up should be called when this object is no longer needed, must be called after init() 167 */ cleanup()168 void cleanup() { 169 // Unregister receiver first, mAdapterService is never null 170 sAdapterService.unregisterReceiver(mReceiver); 171 reset(); 172 } 173 174 /** 175 * Reset should be called when the state of this object needs to be cleared 176 * RemoteDevices is still usable after reset 177 */ reset()178 void reset() { 179 if (sSdpTracker != null) { 180 sSdpTracker.clear(); 181 } 182 183 if (mDevices != null) { 184 mDevices.clear(); 185 } 186 187 if (mDeviceQueue != null) { 188 mDeviceQueue.clear(); 189 } 190 } 191 192 @Override clone()193 public Object clone() throws CloneNotSupportedException { 194 throw new CloneNotSupportedException(); 195 } 196 getDeviceProperties(BluetoothDevice device)197 DeviceProperties getDeviceProperties(BluetoothDevice device) { 198 synchronized (mDevices) { 199 return mDevices.get(device.getAddress()); 200 } 201 } 202 getDevice(byte[] address)203 BluetoothDevice getDevice(byte[] address) { 204 DeviceProperties prop = mDevices.get(Utils.getAddressStringFromByte(address)); 205 if (prop == null) { 206 return null; 207 } 208 return prop.getDevice(); 209 } 210 211 @VisibleForTesting addDeviceProperties(byte[] address)212 DeviceProperties addDeviceProperties(byte[] address) { 213 synchronized (mDevices) { 214 DeviceProperties prop = new DeviceProperties(); 215 prop.mDevice = sAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 216 prop.mAddress = address; 217 String key = Utils.getAddressStringFromByte(address); 218 DeviceProperties pv = mDevices.put(key, prop); 219 220 if (pv == null) { 221 mDeviceQueue.offer(key); 222 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) { 223 String deleteKey = mDeviceQueue.poll(); 224 for (BluetoothDevice device : sAdapterService.getBondedDevices()) { 225 if (device.getAddress().equals(deleteKey)) { 226 return prop; 227 } 228 } 229 debugLog("Removing device " + deleteKey + " from property map"); 230 mDevices.remove(deleteKey); 231 } 232 } 233 return prop; 234 } 235 } 236 237 class DeviceProperties { 238 private String mName; 239 private byte[] mAddress; 240 private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED; 241 private short mRssi; 242 private String mAlias; 243 private BluetoothDevice mDevice; 244 private boolean mIsBondingInitiatedLocally; 245 private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 246 @VisibleForTesting int mBondState; 247 @VisibleForTesting int mDeviceType; 248 @VisibleForTesting ParcelUuid[] mUuids; 249 DeviceProperties()250 DeviceProperties() { 251 mBondState = BluetoothDevice.BOND_NONE; 252 } 253 254 /** 255 * @return the mName 256 */ getName()257 String getName() { 258 synchronized (mObject) { 259 return mName; 260 } 261 } 262 263 /** 264 * @return the mClass 265 */ getBluetoothClass()266 int getBluetoothClass() { 267 synchronized (mObject) { 268 return mBluetoothClass; 269 } 270 } 271 272 /** 273 * @return the mUuids 274 */ getUuids()275 ParcelUuid[] getUuids() { 276 synchronized (mObject) { 277 return mUuids; 278 } 279 } 280 281 /** 282 * @return the mAddress 283 */ getAddress()284 byte[] getAddress() { 285 synchronized (mObject) { 286 return mAddress; 287 } 288 } 289 290 /** 291 * @return the mDevice 292 */ getDevice()293 BluetoothDevice getDevice() { 294 synchronized (mObject) { 295 return mDevice; 296 } 297 } 298 299 /** 300 * @return mRssi 301 */ getRssi()302 short getRssi() { 303 synchronized (mObject) { 304 return mRssi; 305 } 306 } 307 /** 308 * @return mDeviceType 309 */ getDeviceType()310 int getDeviceType() { 311 synchronized (mObject) { 312 return mDeviceType; 313 } 314 } 315 316 /** 317 * @return the mAlias 318 */ getAlias()319 String getAlias() { 320 synchronized (mObject) { 321 return mAlias; 322 } 323 } 324 325 /** 326 * @param mAlias the mAlias to set 327 */ setAlias(BluetoothDevice device, String mAlias)328 void setAlias(BluetoothDevice device, String mAlias) { 329 synchronized (mObject) { 330 this.mAlias = mAlias; 331 sAdapterService.setDevicePropertyNative(mAddress, 332 AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, mAlias.getBytes()); 333 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED); 334 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 335 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias); 336 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 337 Utils.getTempAllowlistBroadcastOptions()); 338 } 339 } 340 341 /** 342 * @param mBondState the mBondState to set 343 */ setBondState(int newBondState)344 void setBondState(int newBondState) { 345 synchronized (mObject) { 346 if ((mBondState == BluetoothDevice.BOND_BONDED 347 && newBondState == BluetoothDevice.BOND_BONDING) 348 || newBondState == BluetoothDevice.BOND_NONE) { 349 /* Clearing the Uuids local copy when the device is unpaired. If not cleared, 350 cachedBluetoothDevice issued a connect using the local cached copy of uuids, 351 without waiting for the ACTION_UUID intent. 352 This was resulting in multiple calls to connect().*/ 353 mUuids = null; 354 mAlias = null; 355 } 356 mBondState = newBondState; 357 } 358 } 359 360 /** 361 * @return the mBondState 362 */ getBondState()363 int getBondState() { 364 synchronized (mObject) { 365 return mBondState; 366 } 367 } 368 isBonding()369 boolean isBonding() { 370 return getBondState() == BluetoothDevice.BOND_BONDING; 371 } 372 isBondingOrBonded()373 boolean isBondingOrBonded() { 374 return isBonding() || getBondState() == BluetoothDevice.BOND_BONDED; 375 } 376 377 /** 378 * @param isBondingInitiatedLocally wether bonding is initiated locally 379 */ setBondingInitiatedLocally(boolean isBondingInitiatedLocally)380 void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) { 381 synchronized (mObject) { 382 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally; 383 } 384 } 385 386 /** 387 * @return the isBondingInitiatedLocally 388 */ isBondingInitiatedLocally()389 boolean isBondingInitiatedLocally() { 390 synchronized (mObject) { 391 return mIsBondingInitiatedLocally; 392 } 393 } 394 getBatteryLevel()395 int getBatteryLevel() { 396 synchronized (mObject) { 397 return mBatteryLevel; 398 } 399 } 400 401 /** 402 * @param batteryLevel the mBatteryLevel to set 403 */ setBatteryLevel(int batteryLevel)404 void setBatteryLevel(int batteryLevel) { 405 synchronized (mObject) { 406 this.mBatteryLevel = batteryLevel; 407 } 408 } 409 } 410 sendUuidIntent(BluetoothDevice device, DeviceProperties prop)411 private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop) { 412 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 413 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 414 intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null ? null : prop.mUuids); 415 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 416 Utils.getTempAllowlistBroadcastOptions()); 417 418 //Remove the outstanding UUID request 419 sSdpTracker.remove(device); 420 } 421 422 /** 423 * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, 424 * we must add device first before setting it's properties. This is a helper method for doing 425 * that. 426 */ setBondingInitiatedLocally(byte[] address)427 void setBondingInitiatedLocally(byte[] address) { 428 DeviceProperties properties; 429 430 BluetoothDevice device = getDevice(address); 431 if (device == null) { 432 properties = addDeviceProperties(address); 433 } else { 434 properties = getDeviceProperties(device); 435 } 436 437 properties.setBondingInitiatedLocally(true); 438 } 439 440 /** 441 * Update battery level in device properties 442 * @param device The remote device to be updated 443 * @param batteryLevel Battery level Indicator between 0-100, 444 * {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error 445 */ 446 @VisibleForTesting updateBatteryLevel(BluetoothDevice device, int batteryLevel)447 void updateBatteryLevel(BluetoothDevice device, int batteryLevel) { 448 if (device == null || batteryLevel < 0 || batteryLevel > 100) { 449 warnLog("Invalid parameters device=" + String.valueOf(device == null) 450 + ", batteryLevel=" + String.valueOf(batteryLevel)); 451 return; 452 } 453 DeviceProperties deviceProperties = getDeviceProperties(device); 454 if (deviceProperties == null) { 455 deviceProperties = addDeviceProperties(Utils.getByteAddress(device)); 456 } 457 synchronized (mObject) { 458 int currentBatteryLevel = deviceProperties.getBatteryLevel(); 459 if (batteryLevel == currentBatteryLevel) { 460 debugLog("Same battery level for device " + device + " received " + String.valueOf( 461 batteryLevel) + "%"); 462 return; 463 } 464 deviceProperties.setBatteryLevel(batteryLevel); 465 } 466 sendBatteryLevelChangedBroadcast(device, batteryLevel); 467 Log.d(TAG, "Updated device " + device + " battery level to " + batteryLevel + "%"); 468 } 469 470 /** 471 * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device 472 * @param device device whose battery level property needs to be reset 473 */ 474 @VisibleForTesting resetBatteryLevel(BluetoothDevice device)475 void resetBatteryLevel(BluetoothDevice device) { 476 if (device == null) { 477 warnLog("Device is null"); 478 return; 479 } 480 DeviceProperties deviceProperties = getDeviceProperties(device); 481 if (deviceProperties == null) { 482 return; 483 } 484 synchronized (mObject) { 485 if (deviceProperties.getBatteryLevel() == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 486 debugLog("Battery level was never set or is already reset, device=" + device); 487 return; 488 } 489 deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 490 } 491 sendBatteryLevelChangedBroadcast(device, BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 492 Log.d(TAG, "Reset battery level, device=" + device); 493 } 494 sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)495 private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) { 496 Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); 497 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 498 intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel); 499 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 500 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 501 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 502 Utils.getTempAllowlistBroadcastOptions()); 503 } 504 areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)505 private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) { 506 final int length1 = uuids1 == null ? 0 : uuids1.length; 507 final int length2 = uuids2 == null ? 0 : uuids2.length; 508 if (length1 != length2) { 509 return false; 510 } 511 Set<ParcelUuid> set = new HashSet<>(); 512 for (int i = 0; i < length1; ++i) { 513 set.add(uuids1[i]); 514 } 515 for (int i = 0; i < length2; ++i) { 516 set.remove(uuids2[i]); 517 } 518 return set.isEmpty(); 519 } 520 devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)521 void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { 522 Intent intent; 523 byte[] val; 524 int type; 525 BluetoothDevice bdDevice = getDevice(address); 526 DeviceProperties device; 527 if (bdDevice == null) { 528 debugLog("Added new device property"); 529 device = addDeviceProperties(address); 530 bdDevice = getDevice(address); 531 } else { 532 device = getDeviceProperties(bdDevice); 533 } 534 535 if (types.length <= 0) { 536 errorLog("No properties to update"); 537 return; 538 } 539 540 for (int j = 0; j < types.length; j++) { 541 type = types[j]; 542 val = values[j]; 543 if (val.length > 0) { 544 synchronized (mObject) { 545 debugLog("Property type: " + type); 546 switch (type) { 547 case AbstractionLayer.BT_PROPERTY_BDNAME: 548 final String newName = new String(val); 549 if (newName.equals(device.mName)) { 550 debugLog("Skip name update for " + bdDevice); 551 break; 552 } 553 device.mName = newName; 554 intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); 555 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 556 intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName); 557 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 558 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 559 Utils.getTempAllowlistBroadcastOptions()); 560 debugLog("Remote Device name is: " + device.mName); 561 break; 562 case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: 563 device.mAlias = new String(val); 564 debugLog("Remote device alias is: " + device.mAlias); 565 break; 566 case AbstractionLayer.BT_PROPERTY_BDADDR: 567 device.mAddress = val; 568 debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val)); 569 break; 570 case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 571 final int newClass = Utils.byteArrayToInt(val); 572 if (newClass == device.mBluetoothClass) { 573 debugLog("Skip class update for " + bdDevice); 574 break; 575 } 576 device.mBluetoothClass = Utils.byteArrayToInt(val); 577 intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); 578 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 579 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 580 new BluetoothClass(device.mBluetoothClass)); 581 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 582 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 583 Utils.getTempAllowlistBroadcastOptions()); 584 debugLog("Remote class is:" + device.mBluetoothClass); 585 break; 586 case AbstractionLayer.BT_PROPERTY_UUIDS: 587 int numUuids = val.length / AbstractionLayer.BT_UUID_SIZE; 588 final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val); 589 if (areUuidsEqual(newUuids, device.mUuids)) { 590 debugLog( "Skip uuids update for " + bdDevice.getAddress()); 591 break; 592 } 593 device.mUuids = newUuids; 594 if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) { 595 sAdapterService.deviceUuidUpdated(bdDevice); 596 sendUuidIntent(bdDevice, device); 597 } 598 break; 599 case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE: 600 // The device type from hal layer, defined in bluetooth.h, 601 // matches the type defined in BluetoothDevice.java 602 device.mDeviceType = Utils.byteArrayToInt(val); 603 break; 604 case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: 605 // RSSI from hal is in one byte 606 device.mRssi = val[0]; 607 break; 608 } 609 } 610 } 611 } 612 } 613 deviceFoundCallback(byte[] address)614 void deviceFoundCallback(byte[] address) { 615 // The device properties are already registered - we can send the intent 616 // now 617 BluetoothDevice device = getDevice(address); 618 debugLog("deviceFoundCallback: Remote Address is:" + device); 619 DeviceProperties deviceProp = getDeviceProperties(device); 620 if (deviceProp == null) { 621 errorLog("Device Properties is null for Device:" + device); 622 return; 623 } 624 625 Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); 626 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 627 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 628 new BluetoothClass(deviceProp.mBluetoothClass)); 629 intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi); 630 intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName); 631 632 final ArrayList<DiscoveringPackage> packages = sAdapterService.getDiscoveringPackages(); 633 synchronized (packages) { 634 for (DiscoveringPackage pkg : packages) { 635 if (pkg.hasDisavowedLocation()) { 636 if (mLocationDenylistPredicate.test(device)) { 637 continue; 638 } 639 } 640 641 intent.setPackage(pkg.getPackageName()); 642 643 if (pkg.getPermission() != null) { 644 sAdapterService.sendBroadcastMultiplePermissions(intent, 645 new String[] { BLUETOOTH_SCAN, pkg.getPermission() }, 646 Utils.getTempAllowlistBroadcastOptions()); 647 } else { 648 sAdapterService.sendBroadcastMultiplePermissions(intent, 649 new String[] { BLUETOOTH_SCAN }, 650 Utils.getTempAllowlistBroadcastOptions()); 651 } 652 } 653 } 654 } 655 aclStateChangeCallback(int status, byte[] address, int newState, int hciReason)656 void aclStateChangeCallback(int status, byte[] address, int newState, int hciReason) { 657 BluetoothDevice device = getDevice(address); 658 659 if (device == null) { 660 errorLog("aclStateChangeCallback: device is NULL, address=" 661 + Utils.getAddressStringFromByte(address) + ", newState=" + newState); 662 return; 663 } 664 int state = sAdapterService.getState(); 665 666 Intent intent = null; 667 if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) { 668 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) { 669 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); 670 } else if (state == BluetoothAdapter.STATE_BLE_ON 671 || state == BluetoothAdapter.STATE_BLE_TURNING_ON) { 672 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED); 673 } 674 debugLog( 675 "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) 676 + " Connected: " + device); 677 } else { 678 if (device.getBondState() == BluetoothDevice.BOND_BONDING) { 679 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding. 680 intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL); 681 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 682 intent.setPackage(sAdapterService.getString(R.string.pairing_ui_package)); 683 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 684 Utils.getTempAllowlistBroadcastOptions()); 685 } 686 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) { 687 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); 688 } else if (state == BluetoothAdapter.STATE_BLE_ON 689 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { 690 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); 691 } 692 // Reset battery level on complete disconnection 693 if (sAdapterService.getConnectionState(device) == 0) { 694 resetBatteryLevel(device); 695 } 696 debugLog( 697 "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) 698 + " Disconnected: " + device); 699 } 700 701 int connectionState = newState == AbstractionLayer.BT_ACL_STATE_CONNECTED 702 ? BluetoothAdapter.STATE_CONNECTED : BluetoothAdapter.STATE_DISCONNECTED; 703 int metricId = sAdapterService.getMetricId(device); 704 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED, 705 sAdapterService.obfuscateAddress(device), connectionState, metricId); 706 BluetoothClass deviceClass = device.getBluetoothClass(); 707 int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice(); 708 BluetoothStatsLog.write(BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED, 709 sAdapterService.obfuscateAddress(device), classOfDevice, metricId); 710 711 if (intent != null) { 712 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 713 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 714 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 715 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 716 sAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT, 717 Utils.getTempAllowlistBroadcastOptions()); 718 719 synchronized (sAdapterService.getBluetoothConnectionCallbacks()) { 720 Set<IBluetoothConnectionCallback> bluetoothConnectionCallbacks = 721 sAdapterService.getBluetoothConnectionCallbacks(); 722 for (IBluetoothConnectionCallback callback : bluetoothConnectionCallbacks) { 723 try { 724 if (connectionState == BluetoothAdapter.STATE_CONNECTED) { 725 callback.onDeviceConnected(device); 726 } else { 727 callback.onDeviceDisconnected(device, 728 AdapterService.hciToAndroidDisconnectReason(hciReason)); 729 } 730 } catch (RemoteException ex) { 731 Log.e(TAG, "RemoteException in calling IBluetoothConnectionCallback"); 732 } 733 } 734 } 735 } else { 736 Log.e(TAG, "aclStateChangeCallback intent is null. deviceBondState: " 737 + device.getBondState()); 738 } 739 } 740 741 fetchUuids(BluetoothDevice device)742 void fetchUuids(BluetoothDevice device) { 743 if (sSdpTracker.contains(device)) { 744 return; 745 } 746 747 // If no UUIDs are cached and the device is bonding, wait for SDP after the device is bonded 748 DeviceProperties deviceProperties = getDeviceProperties(device); 749 if (deviceProperties != null && deviceProperties.isBonding() 750 && getDeviceProperties(device).getUuids() == null) { 751 return; 752 } 753 754 sSdpTracker.add(device); 755 756 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 757 message.obj = device; 758 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 759 760 // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP. 761 if (deviceProperties == null || !deviceProperties.isBonding()) { 762 sAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress())); 763 } 764 } 765 updateUuids(BluetoothDevice device)766 void updateUuids(BluetoothDevice device) { 767 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 768 message.obj = device; 769 mHandler.sendMessage(message); 770 } 771 772 /** 773 * Handles headset connection state change event 774 * @param intent must be {@link BluetoothHeadset#ACTION_CONNECTION_STATE_CHANGED} intent 775 */ 776 @VisibleForTesting onHeadsetConnectionStateChanged(Intent intent)777 void onHeadsetConnectionStateChanged(Intent intent) { 778 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 779 if (device == null) { 780 Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null"); 781 return; 782 } 783 if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) 784 == BluetoothProfile.STATE_DISCONNECTED) { 785 // TODO: Rework this when non-HFP sources of battery level indication is added 786 resetBatteryLevel(device); 787 } 788 } 789 790 @VisibleForTesting onHfIndicatorValueChanged(Intent intent)791 void onHfIndicatorValueChanged(Intent intent) { 792 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 793 if (device == null) { 794 Log.e(TAG, "onHfIndicatorValueChanged() remote device is null"); 795 return; 796 } 797 int indicatorId = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1); 798 int indicatorValue = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -1); 799 if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) { 800 updateBatteryLevel(device, indicatorValue); 801 } 802 } 803 804 /** 805 * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent 806 * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent 807 */ 808 @VisibleForTesting onVendorSpecificHeadsetEvent(Intent intent)809 void onVendorSpecificHeadsetEvent(Intent intent) { 810 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 811 if (device == null) { 812 Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null"); 813 return; 814 } 815 String cmd = 816 intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD); 817 if (cmd == null) { 818 Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null"); 819 return; 820 } 821 int cmdType = 822 intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, 823 -1); 824 // Only process set command 825 if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) { 826 debugLog("onVendorSpecificHeadsetEvent() only SET command is processed"); 827 return; 828 } 829 Object[] args = (Object[]) intent.getExtras() 830 .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS); 831 if (args == null) { 832 Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null"); 833 return; 834 } 835 int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 836 switch (cmd) { 837 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT: 838 batteryPercent = getBatteryLevelFromXEventVsc(args); 839 break; 840 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV: 841 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args); 842 break; 843 } 844 if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 845 updateBatteryLevel(device, batteryPercent); 846 infoLog("Updated device " + device + " battery level to " + String.valueOf( 847 batteryPercent) + "%"); 848 } 849 } 850 851 /** 852 * Parse 853 * AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue] 854 * vendor specific event 855 * @param args Array of arguments on the right side of assignment 856 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 857 * when there is an error parsing the arguments 858 */ 859 @VisibleForTesting getBatteryLevelFromAppleBatteryVsc(Object[] args)860 static int getBatteryLevelFromAppleBatteryVsc(Object[] args) { 861 if (args.length == 0) { 862 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments"); 863 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 864 } 865 int numKvPair; 866 if (args[0] instanceof Integer) { 867 numKvPair = (Integer) args[0]; 868 } else { 869 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments"); 870 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 871 } 872 if (args.length != (numKvPair * 2 + 1)) { 873 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match"); 874 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 875 } 876 int indicatorType; 877 int indicatorValue = -1; 878 for (int i = 0; i < numKvPair; ++i) { 879 Object indicatorTypeObj = args[2 * i + 1]; 880 if (indicatorTypeObj instanceof Integer) { 881 indicatorType = (Integer) indicatorTypeObj; 882 } else { 883 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type"); 884 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 885 } 886 if (indicatorType 887 != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) { 888 continue; 889 } 890 Object indicatorValueObj = args[2 * i + 2]; 891 if (indicatorValueObj instanceof Integer) { 892 indicatorValue = (Integer) indicatorValueObj; 893 } else { 894 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value"); 895 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 896 } 897 break; 898 } 899 return (indicatorValue < 0 || indicatorValue > 9) ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN 900 : (indicatorValue + 1) * 10; 901 } 902 903 /** 904 * Parse 905 * AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging] 906 * vendor specific event 907 * @param args Array of arguments on the right side of SET command 908 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 909 * when there is an error parsing the arguments 910 */ 911 @VisibleForTesting getBatteryLevelFromXEventVsc(Object[] args)912 static int getBatteryLevelFromXEventVsc(Object[] args) { 913 if (args.length == 0) { 914 Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments"); 915 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 916 } 917 Object eventNameObj = args[0]; 918 if (!(eventNameObj instanceof String)) { 919 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name"); 920 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 921 } 922 String eventName = (String) eventNameObj; 923 if (!eventName.equals( 924 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) { 925 infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName); 926 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 927 } 928 if (args.length != 5) { 929 Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong battery level event length: " 930 + String.valueOf(args.length)); 931 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 932 } 933 if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) { 934 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values"); 935 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 936 } 937 int batteryLevel = (Integer) args[1]; 938 int numberOfLevels = (Integer) args[2]; 939 if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) { 940 Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel=" 941 + String.valueOf(batteryLevel) + ", numberOfLevels=" + String.valueOf( 942 numberOfLevels)); 943 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 944 } 945 return batteryLevel * 100 / (numberOfLevels - 1); 946 } 947 errorLog(String msg)948 private static void errorLog(String msg) { 949 Log.e(TAG, msg); 950 } 951 debugLog(String msg)952 private static void debugLog(String msg) { 953 if (DBG) { 954 Log.d(TAG, msg); 955 } 956 } 957 infoLog(String msg)958 private static void infoLog(String msg) { 959 if (DBG) { 960 Log.i(TAG, msg); 961 } 962 } 963 warnLog(String msg)964 private static void warnLog(String msg) { 965 Log.w(TAG, msg); 966 } 967 968 } 969