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_PRIVILEGED; 21 import static android.Manifest.permission.BLUETOOTH_SCAN; 22 23 import android.annotation.RequiresPermission; 24 import android.app.admin.SecurityLog; 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothAssignedNumbers; 27 import android.bluetooth.BluetoothClass; 28 import android.bluetooth.BluetoothDevice; 29 import android.bluetooth.BluetoothHeadset; 30 import android.bluetooth.BluetoothManager; 31 import android.bluetooth.BluetoothProfile; 32 import android.bluetooth.BluetoothProtoEnums; 33 import android.bluetooth.BluetoothSinkAudioPolicy; 34 import android.bluetooth.IBluetoothConnectionCallback; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.net.MacAddress; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.ParcelUuid; 42 import android.os.RemoteException; 43 import android.os.SystemProperties; 44 import android.util.Log; 45 46 import androidx.annotation.NonNull; 47 48 import com.android.bluetooth.BluetoothStatsLog; 49 import com.android.bluetooth.R; 50 import com.android.bluetooth.Utils; 51 import com.android.bluetooth.bas.BatteryService; 52 import com.android.bluetooth.flags.Flags; 53 import com.android.bluetooth.hfp.HeadsetHalConstants; 54 import com.android.internal.annotations.VisibleForTesting; 55 56 import java.io.UnsupportedEncodingException; 57 import java.util.ArrayDeque; 58 import java.util.ArrayList; 59 import java.util.HashMap; 60 import java.util.HashSet; 61 import java.util.List; 62 import java.util.Objects; 63 import java.util.Set; 64 import java.util.function.Predicate; 65 66 /** Remote device manager. This class is currently mostly used for HF and AG remote devices. */ 67 public class RemoteDevices { 68 private static final String TAG = "BluetoothRemoteDevices"; 69 70 // Maximum number of device properties to remember 71 private static final int MAX_DEVICE_QUEUE_SIZE = 200; 72 73 private BluetoothAdapter mAdapter; 74 private AdapterService mAdapterService; 75 private ArrayList<BluetoothDevice> mSdpTracker; 76 private final Object mObject = new Object(); 77 78 private static final int UUID_INTENT_DELAY = 6000; 79 private static final int MESSAGE_UUID_INTENT = 1; 80 private static final String LOG_SOURCE_DIS = "DIS"; 81 82 private final HashMap<String, DeviceProperties> mDevices; 83 private final HashMap<String, String> mDualDevicesMap; 84 private final ArrayDeque<String> mDeviceQueue; 85 86 /** 87 * Bluetooth HFP v1.8 specifies the Battery Charge indicator of AG can take values from {@code 88 * 0} to {@code 5}, but it does not specify how to map the values back to percentages. The 89 * following mapping is used: - Level 0: 0% - Level 1: midpoint of 1-25% - Level 2: midpoint of 90 * 26-50% - Level 3: midpoint of 51-75% - Level 4: midpoint of 76-99% - Level 5: 100% 91 */ 92 private static final int HFP_BATTERY_CHARGE_INDICATOR_0 = 0; 93 94 private static final int HFP_BATTERY_CHARGE_INDICATOR_1 = 13; 95 private static final int HFP_BATTERY_CHARGE_INDICATOR_2 = 38; 96 private static final int HFP_BATTERY_CHARGE_INDICATOR_3 = 63; 97 private static final int HFP_BATTERY_CHARGE_INDICATOR_4 = 88; 98 private static final int HFP_BATTERY_CHARGE_INDICATOR_5 = 100; 99 100 private final Handler mHandler; 101 private final Handler mMainHandler; 102 103 private class RemoteDevicesHandler extends Handler { 104 105 /** 106 * Handler must be created from an explicit looper to avoid threading ambiguity 107 * 108 * @param looper The looper that this handler should be executed on 109 */ RemoteDevicesHandler(Looper looper)110 RemoteDevicesHandler(Looper looper) { 111 super(looper); 112 } 113 114 @Override handleMessage(Message msg)115 public void handleMessage(Message msg) { 116 switch (msg.what) { 117 case MESSAGE_UUID_INTENT: 118 BluetoothDevice device = (BluetoothDevice) msg.obj; 119 if (device != null) { 120 // SDP Sending delayed SDP UUID intent 121 MetricsLogger.getInstance() 122 .cacheCount(BluetoothProtoEnums.SDP_SENDING_DELAYED_UUID, 1); 123 DeviceProperties prop = getDeviceProperties(device); 124 sendUuidIntent(device, prop); 125 } else { 126 // SDP Not sending delayed SDP UUID intent b/c device is not there 127 MetricsLogger.getInstance() 128 .cacheCount(BluetoothProtoEnums.SDP_NOT_SENDING_DELAYED_UUID, 1); 129 } 130 break; 131 } 132 } 133 } 134 135 /** 136 * Predicate that tests if the given {@link BluetoothDevice} is well-known to be used for 137 * physical location. 138 */ 139 private final Predicate<BluetoothDevice> mLocationDenylistPredicate = 140 (device) -> { 141 final MacAddress parsedAddress = MacAddress.fromString(device.getAddress()); 142 if (mAdapterService.getLocationDenylistMac().test(parsedAddress.toByteArray())) { 143 Log.v(TAG, "Skipping device matching denylist: " + device); 144 return true; 145 } 146 final String name = Utils.getName(device); 147 if (mAdapterService.getLocationDenylistName().test(name)) { 148 Log.v(TAG, "Skipping name matching denylist: " + name); 149 return true; 150 } 151 return false; 152 }; 153 RemoteDevices(AdapterService service, Looper looper)154 RemoteDevices(AdapterService service, Looper looper) { 155 mAdapter = ((Context) service).getSystemService(BluetoothManager.class).getAdapter(); 156 mAdapterService = service; 157 mSdpTracker = new ArrayList<>(); 158 mDevices = new HashMap<>(); 159 mDualDevicesMap = new HashMap<>(); 160 mDeviceQueue = new ArrayDeque<>(); 161 mHandler = new RemoteDevicesHandler(looper); 162 mMainHandler = new Handler(Looper.getMainLooper()); 163 } 164 165 /** 166 * Reset should be called when the state of this object needs to be cleared RemoteDevices is 167 * still usable after reset 168 */ 169 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) reset()170 void reset() { 171 mSdpTracker.clear(); 172 173 // Unregister Handler and stop all queued messages. 174 mMainHandler.removeCallbacksAndMessages(null); 175 176 synchronized (mDevices) { 177 debugLog("reset(): Broadcasting ACL_DISCONNECTED"); 178 179 mDevices.forEach( 180 (address, deviceProperties) -> { 181 BluetoothDevice bluetoothDevice = deviceProperties.getDevice(); 182 183 debugLog( 184 "reset(): address=" 185 + address 186 + ", connected=" 187 + bluetoothDevice.isConnected()); 188 189 if (bluetoothDevice.isConnected()) { 190 int transport = 191 deviceProperties.getConnectionHandle( 192 BluetoothDevice.TRANSPORT_BREDR) 193 != BluetoothDevice.ERROR 194 ? BluetoothDevice.TRANSPORT_BREDR 195 : BluetoothDevice.TRANSPORT_LE; 196 mAdapterService.notifyAclDisconnected(bluetoothDevice, transport); 197 Intent intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); 198 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice); 199 intent.addFlags( 200 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 201 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 202 mAdapterService.sendBroadcast(intent, BLUETOOTH_CONNECT); 203 } 204 }); 205 mDevices.clear(); 206 } 207 208 mDualDevicesMap.clear(); 209 mDeviceQueue.clear(); 210 } 211 212 @Override clone()213 public Object clone() throws CloneNotSupportedException { 214 throw new CloneNotSupportedException(); 215 } 216 getDeviceProperties(BluetoothDevice device)217 DeviceProperties getDeviceProperties(BluetoothDevice device) { 218 if (device == null) { 219 return null; 220 } 221 222 synchronized (mDevices) { 223 String address = mDualDevicesMap.get(device.getAddress()); 224 // If the device is not in the dual map, use its original address 225 if (address == null || mDevices.get(address) == null) { 226 address = device.getAddress(); 227 } 228 return mDevices.get(address); 229 } 230 } 231 getDevice(byte[] address)232 BluetoothDevice getDevice(byte[] address) { 233 String addressString = Utils.getAddressStringFromByte(address); 234 String deviceAddress = mDualDevicesMap.get(addressString); 235 // If the device is not in the dual map, use its original address 236 if (deviceAddress == null || mDevices.get(deviceAddress) == null) { 237 deviceAddress = addressString; 238 } 239 240 DeviceProperties prop = mDevices.get(deviceAddress); 241 if (prop != null) { 242 return prop.getDevice(); 243 } 244 return null; 245 } 246 247 @VisibleForTesting addDeviceProperties(byte[] address)248 DeviceProperties addDeviceProperties(byte[] address) { 249 synchronized (mDevices) { 250 DeviceProperties prop = new DeviceProperties(); 251 prop.setDevice(mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address))); 252 prop.setAddress(address); 253 String key = Utils.getAddressStringFromByte(address); 254 DeviceProperties pv = mDevices.put(key, prop); 255 256 if (pv == null) { 257 mDeviceQueue.offer(key); 258 if (mDeviceQueue.size() > MAX_DEVICE_QUEUE_SIZE) { 259 String deleteKey = mDeviceQueue.poll(); 260 for (BluetoothDevice device : mAdapterService.getBondedDevices()) { 261 if (device.getAddress().equals(deleteKey)) { 262 return prop; 263 } 264 } 265 debugLog("Removing device " + deleteKey + " from property map"); 266 mDevices.remove(deleteKey); 267 } 268 } 269 return prop; 270 } 271 } 272 273 class DeviceProperties { 274 private String mName; 275 private byte[] mAddress; 276 private String mIdentityAddress; 277 private boolean mIsConsolidated = false; 278 private int mBluetoothClass = BluetoothClass.Device.Major.UNCATEGORIZED; 279 private int mBredrConnectionHandle = BluetoothDevice.ERROR; 280 private int mLeConnectionHandle = BluetoothDevice.ERROR; 281 private short mRssi; 282 private String mAlias; 283 private BluetoothDevice mDevice; 284 private boolean mIsBondingInitiatedLocally; 285 private int mBatteryLevelFromHfp = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 286 private int mBatteryLevelFromBatteryService = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 287 private boolean mIsCoordinatedSetMember; 288 private int mAshaCapability; 289 private int mAshaTruncatedHiSyncId; 290 private String mModelName; 291 @VisibleForTesting int mBondState; 292 @VisibleForTesting int mDeviceType; 293 @VisibleForTesting ParcelUuid[] mUuids; 294 private BluetoothSinkAudioPolicy mAudioPolicy; 295 DeviceProperties()296 DeviceProperties() { 297 mBondState = BluetoothDevice.BOND_NONE; 298 } 299 300 /** 301 * @return the mName 302 */ getName()303 String getName() { 304 synchronized (mObject) { 305 return mName; 306 } 307 } 308 309 /** 310 * @param name the mName to set 311 */ setName(String name)312 void setName(String name) { 313 synchronized (mObject) { 314 this.mName = name; 315 } 316 } 317 318 /** 319 * @return the mIdentityAddress 320 */ getIdentityAddress()321 String getIdentityAddress() { 322 synchronized (mObject) { 323 return mIdentityAddress; 324 } 325 } 326 327 /** 328 * @param identityAddress the mIdentityAddress to set 329 */ setIdentityAddress(String identityAddress)330 void setIdentityAddress(String identityAddress) { 331 synchronized (mObject) { 332 this.mIdentityAddress = identityAddress; 333 } 334 } 335 336 /** 337 * @return mIsConsolidated 338 */ isConsolidated()339 boolean isConsolidated() { 340 synchronized (mObject) { 341 return mIsConsolidated; 342 } 343 } 344 345 /** 346 * @param isConsolidated the mIsConsolidated to set 347 */ setIsConsolidated(boolean isConsolidated)348 void setIsConsolidated(boolean isConsolidated) { 349 synchronized (mObject) { 350 this.mIsConsolidated = isConsolidated; 351 } 352 } 353 354 /** 355 * @return the mClass 356 */ getBluetoothClass()357 int getBluetoothClass() { 358 synchronized (mObject) { 359 return mBluetoothClass; 360 } 361 } 362 363 /** 364 * @param bluetoothClass the mBluetoothClass to set 365 */ setBluetoothClass(int bluetoothClass)366 void setBluetoothClass(int bluetoothClass) { 367 synchronized (mObject) { 368 this.mBluetoothClass = bluetoothClass; 369 } 370 } 371 372 /** 373 * @param transport the transport on which the connection exists 374 * @return the mConnectionHandle 375 */ getConnectionHandle(int transport)376 int getConnectionHandle(int transport) { 377 synchronized (mObject) { 378 if (transport == BluetoothDevice.TRANSPORT_BREDR) { 379 return mBredrConnectionHandle; 380 } else if (transport == BluetoothDevice.TRANSPORT_LE) { 381 return mLeConnectionHandle; 382 } else { 383 return BluetoothDevice.ERROR; 384 } 385 } 386 } 387 388 /** 389 * @param connectionHandle the connectionHandle to set 390 * @param transport the transport on which to set the handle 391 */ setConnectionHandle(int connectionHandle, int transport)392 void setConnectionHandle(int connectionHandle, int transport) { 393 synchronized (mObject) { 394 if (transport == BluetoothDevice.TRANSPORT_BREDR) { 395 mBredrConnectionHandle = connectionHandle; 396 } else if (transport == BluetoothDevice.TRANSPORT_LE) { 397 mLeConnectionHandle = connectionHandle; 398 } else { 399 errorLog("setConnectionHandle() unexpected transport value " + transport); 400 } 401 } 402 } 403 404 /** 405 * @return the mUuids 406 */ getUuids()407 ParcelUuid[] getUuids() { 408 synchronized (mObject) { 409 return mUuids; 410 } 411 } 412 413 /** 414 * @param uuids the mUuids to set 415 */ setUuids(ParcelUuid[] uuids)416 void setUuids(ParcelUuid[] uuids) { 417 synchronized (mObject) { 418 this.mUuids = uuids; 419 } 420 } 421 422 /** 423 * @return the mAddress 424 */ getAddress()425 byte[] getAddress() { 426 synchronized (mObject) { 427 return mAddress; 428 } 429 } 430 431 /** 432 * @param address the mAddress to set 433 */ setAddress(byte[] address)434 void setAddress(byte[] address) { 435 synchronized (mObject) { 436 this.mAddress = address; 437 } 438 } 439 440 /** 441 * @return the mDevice 442 */ getDevice()443 BluetoothDevice getDevice() { 444 synchronized (mObject) { 445 return mDevice; 446 } 447 } 448 449 /** 450 * @param device the mDevice to set 451 */ setDevice(BluetoothDevice device)452 void setDevice(BluetoothDevice device) { 453 synchronized (mObject) { 454 this.mDevice = device; 455 } 456 } 457 458 /** 459 * @return mRssi 460 */ getRssi()461 short getRssi() { 462 synchronized (mObject) { 463 return mRssi; 464 } 465 } 466 467 /** 468 * @param rssi the mRssi to set 469 */ setRssi(short rssi)470 void setRssi(short rssi) { 471 synchronized (mObject) { 472 this.mRssi = rssi; 473 } 474 } 475 476 /** 477 * @return mDeviceType 478 */ getDeviceType()479 int getDeviceType() { 480 synchronized (mObject) { 481 return mDeviceType; 482 } 483 } 484 485 /** 486 * @param deviceType the mDeviceType to set 487 */ 488 @VisibleForTesting setDeviceType(int deviceType)489 void setDeviceType(int deviceType) { 490 synchronized (mObject) { 491 this.mDeviceType = deviceType; 492 } 493 } 494 495 /** 496 * @return the mAlias 497 */ getAlias()498 String getAlias() { 499 synchronized (mObject) { 500 return mAlias; 501 } 502 } 503 504 /** 505 * @param mAlias the mAlias to set 506 */ setAlias(BluetoothDevice device, String mAlias)507 void setAlias(BluetoothDevice device, String mAlias) { 508 synchronized (mObject) { 509 this.mAlias = mAlias; 510 mAdapterService 511 .getNative() 512 .setDeviceProperty( 513 mAddress, 514 AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME, 515 mAlias.getBytes()); 516 Intent intent = new Intent(BluetoothDevice.ACTION_ALIAS_CHANGED); 517 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 518 intent.putExtra(BluetoothDevice.EXTRA_NAME, mAlias); 519 mAdapterService.sendBroadcast( 520 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 521 } 522 } 523 524 /** 525 * @param newBondState the mBondState to set 526 */ setBondState(int newBondState)527 void setBondState(int newBondState) { 528 synchronized (mObject) { 529 this.mBondState = newBondState; 530 if (newBondState == BluetoothDevice.BOND_NONE) { 531 /* Clearing the Uuids local copy when the device is unpaired. If not cleared, 532 cachedBluetoothDevice issued a connect using the local cached copy of uuids, 533 without waiting for the ACTION_UUID intent. 534 This was resulting in multiple calls to connect().*/ 535 mUuids = null; 536 mAlias = null; 537 } 538 } 539 } 540 541 /** 542 * @return the mBondState 543 */ getBondState()544 int getBondState() { 545 synchronized (mObject) { 546 return mBondState; 547 } 548 } 549 isBonding()550 boolean isBonding() { 551 return getBondState() == BluetoothDevice.BOND_BONDING; 552 } 553 isBondingOrBonded()554 boolean isBondingOrBonded() { 555 return isBonding() || getBondState() == BluetoothDevice.BOND_BONDED; 556 } 557 558 /** 559 * @param isBondingInitiatedLocally whether bonding is initiated locally 560 */ setBondingInitiatedLocally(boolean isBondingInitiatedLocally)561 void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) { 562 synchronized (mObject) { 563 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally; 564 } 565 } 566 567 /** 568 * @return the isBondingInitiatedLocally 569 */ isBondingInitiatedLocally()570 boolean isBondingInitiatedLocally() { 571 synchronized (mObject) { 572 return mIsBondingInitiatedLocally; 573 } 574 } 575 576 /** 577 * @return mBatteryLevel 578 */ getBatteryLevel()579 int getBatteryLevel() { 580 synchronized (mObject) { 581 if (mBatteryLevelFromBatteryService != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 582 return mBatteryLevelFromBatteryService; 583 } 584 return mBatteryLevelFromHfp; 585 } 586 } 587 setBatteryLevelFromHfp(int batteryLevel)588 void setBatteryLevelFromHfp(int batteryLevel) { 589 synchronized (mObject) { 590 if (mBatteryLevelFromHfp == batteryLevel) { 591 return; 592 } 593 mBatteryLevelFromHfp = batteryLevel; 594 } 595 } 596 setBatteryLevelFromBatteryService(int batteryLevel)597 void setBatteryLevelFromBatteryService(int batteryLevel) { 598 synchronized (mObject) { 599 if (mBatteryLevelFromBatteryService == batteryLevel) { 600 return; 601 } 602 mBatteryLevelFromBatteryService = batteryLevel; 603 } 604 } 605 606 /** 607 * @return the mIsCoordinatedSetMember 608 */ isCoordinatedSetMember()609 boolean isCoordinatedSetMember() { 610 synchronized (mObject) { 611 return mIsCoordinatedSetMember; 612 } 613 } 614 615 /** 616 * @param isCoordinatedSetMember the mIsCoordinatedSetMember to set 617 */ setIsCoordinatedSetMember(boolean isCoordinatedSetMember)618 void setIsCoordinatedSetMember(boolean isCoordinatedSetMember) { 619 if ((mAdapterService.getSupportedProfilesBitMask() 620 & (1 << BluetoothProfile.CSIP_SET_COORDINATOR)) 621 == 0) { 622 debugLog("CSIP is not supported"); 623 return; 624 } 625 synchronized (mObject) { 626 this.mIsCoordinatedSetMember = isCoordinatedSetMember; 627 } 628 } 629 630 /** 631 * @return the mAshaCapability 632 */ getAshaCapability()633 int getAshaCapability() { 634 synchronized (mObject) { 635 return mAshaCapability; 636 } 637 } 638 setAshaCapability(int ashaCapability)639 void setAshaCapability(int ashaCapability) { 640 synchronized (mObject) { 641 this.mAshaCapability = ashaCapability; 642 } 643 } 644 645 /** 646 * @return the mAshaTruncatedHiSyncId 647 */ getAshaTruncatedHiSyncId()648 int getAshaTruncatedHiSyncId() { 649 synchronized (mObject) { 650 return mAshaTruncatedHiSyncId; 651 } 652 } 653 setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId)654 void setAshaTruncatedHiSyncId(int ashaTruncatedHiSyncId) { 655 synchronized (mObject) { 656 this.mAshaTruncatedHiSyncId = ashaTruncatedHiSyncId; 657 } 658 } 659 setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies)660 public void setHfAudioPolicyForRemoteAg(BluetoothSinkAudioPolicy policies) { 661 mAudioPolicy = policies; 662 } 663 getHfAudioPolicyForRemoteAg()664 public BluetoothSinkAudioPolicy getHfAudioPolicyForRemoteAg() { 665 return mAudioPolicy; 666 } 667 setModelName(String modelName)668 public void setModelName(String modelName) { 669 mModelName = modelName; 670 try { 671 mAdapterService.setMetadata( 672 this.mDevice, 673 BluetoothDevice.METADATA_MODEL_NAME, 674 mModelName.getBytes("UTF-8")); 675 } catch (UnsupportedEncodingException uee) { 676 Log.e(TAG, "setModelName: UTF-8 not supported?!?"); // this should not happen 677 } 678 } 679 680 /** 681 * @return the mModelName 682 */ getModelName()683 String getModelName() { 684 synchronized (mObject) { 685 return mModelName; 686 } 687 } 688 } 689 sendUuidIntent(BluetoothDevice device, DeviceProperties prop)690 private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop) { 691 // Send uuids within the stack before the broadcast is sent out 692 ParcelUuid[] uuids = prop == null ? null : prop.getUuids(); 693 mAdapterService.sendUuidsInternal(device, uuids); 694 695 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 696 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 697 intent.putExtra(BluetoothDevice.EXTRA_UUID, uuids); 698 mAdapterService.sendBroadcast( 699 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 700 701 // SDP Sent UUID Intent here 702 MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_SENT_UUID, 1); 703 // Remove the outstanding UUID request 704 mSdpTracker.remove(device); 705 } 706 707 /** 708 * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, 709 * we must add device first before setting it's properties. This is a helper method for doing 710 * that. 711 */ setBondingInitiatedLocally(byte[] address)712 void setBondingInitiatedLocally(byte[] address) { 713 DeviceProperties properties; 714 715 BluetoothDevice device = getDevice(address); 716 if (device == null) { 717 properties = addDeviceProperties(address); 718 } else { 719 properties = getDeviceProperties(device); 720 } 721 722 properties.setBondingInitiatedLocally(true); 723 } 724 725 /** 726 * Update battery level in device properties 727 * 728 * @param device The remote device to be updated 729 * @param batteryLevel Battery level Indicator between 0-100, {@link 730 * BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error 731 * @param isBas true if the battery level is from the battery service 732 */ 733 @VisibleForTesting updateBatteryLevel(BluetoothDevice device, int batteryLevel, boolean isBas)734 void updateBatteryLevel(BluetoothDevice device, int batteryLevel, boolean isBas) { 735 if (device == null || batteryLevel < 0 || batteryLevel > 100) { 736 warnLog( 737 "Invalid parameters device=" 738 + String.valueOf(device == null) 739 + ", batteryLevel=" 740 + String.valueOf(batteryLevel)); 741 return; 742 } 743 DeviceProperties deviceProperties = getDeviceProperties(device); 744 if (deviceProperties == null) { 745 deviceProperties = addDeviceProperties(Utils.getByteAddress(device)); 746 } 747 int prevBatteryLevel = deviceProperties.getBatteryLevel(); 748 if (isBas) { 749 deviceProperties.setBatteryLevelFromBatteryService(batteryLevel); 750 } else { 751 deviceProperties.setBatteryLevelFromHfp(batteryLevel); 752 } 753 int newBatteryLevel = deviceProperties.getBatteryLevel(); 754 if (prevBatteryLevel == newBatteryLevel) { 755 debugLog( 756 "Same battery level for device " 757 + device 758 + " received " 759 + String.valueOf(batteryLevel) 760 + "%"); 761 return; 762 } 763 sendBatteryLevelChangedBroadcast(device, newBatteryLevel); 764 Log.d(TAG, "Updated device " + device + " battery level to " + newBatteryLevel + "%"); 765 } 766 767 /** 768 * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device 769 * 770 * @param device device whose battery level property needs to be reset 771 */ 772 @VisibleForTesting resetBatteryLevel(BluetoothDevice device, boolean isBas)773 void resetBatteryLevel(BluetoothDevice device, boolean isBas) { 774 if (device == null) { 775 warnLog("Device is null"); 776 return; 777 } 778 DeviceProperties deviceProperties = getDeviceProperties(device); 779 if (deviceProperties == null) { 780 return; 781 } 782 int prevBatteryLevel = deviceProperties.getBatteryLevel(); 783 if (isBas) { 784 deviceProperties.setBatteryLevelFromBatteryService( 785 BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 786 } else { 787 deviceProperties.setBatteryLevelFromHfp(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 788 } 789 790 int newBatteryLevel = deviceProperties.getBatteryLevel(); 791 if (prevBatteryLevel == newBatteryLevel) { 792 debugLog("Battery level was not changed due to reset, device=" + device); 793 return; 794 } 795 sendBatteryLevelChangedBroadcast(device, newBatteryLevel); 796 Log.d(TAG, "Updated device " + device + " battery level to " + newBatteryLevel + "%"); 797 } 798 sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)799 private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) { 800 Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); 801 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 802 intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel); 803 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 804 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 805 mAdapterService.sendBroadcast( 806 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 807 } 808 809 /** 810 * Converts HFP's Battery Charge indicator values of {@code 0 -- 5} to an integer percentage. 811 */ 812 @VisibleForTesting batteryChargeIndicatorToPercentge(int indicator)813 static int batteryChargeIndicatorToPercentge(int indicator) { 814 int percent; 815 switch (indicator) { 816 case 5: 817 percent = HFP_BATTERY_CHARGE_INDICATOR_5; 818 break; 819 case 4: 820 percent = HFP_BATTERY_CHARGE_INDICATOR_4; 821 break; 822 case 3: 823 percent = HFP_BATTERY_CHARGE_INDICATOR_3; 824 break; 825 case 2: 826 percent = HFP_BATTERY_CHARGE_INDICATOR_2; 827 break; 828 case 1: 829 percent = HFP_BATTERY_CHARGE_INDICATOR_1; 830 break; 831 case 0: 832 percent = HFP_BATTERY_CHARGE_INDICATOR_0; 833 break; 834 default: 835 percent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 836 } 837 Log.d(TAG, "Battery charge indicator: " + indicator + "; converted to: " + percent + "%"); 838 return percent; 839 } 840 areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)841 private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) { 842 final int length1 = uuids1 == null ? 0 : uuids1.length; 843 final int length2 = uuids2 == null ? 0 : uuids2.length; 844 if (length1 != length2) { 845 return false; 846 } 847 Set<ParcelUuid> set = new HashSet<>(); 848 for (int i = 0; i < length1; ++i) { 849 set.add(uuids1[i]); 850 } 851 for (int i = 0; i < length2; ++i) { 852 set.remove(uuids2[i]); 853 } 854 return set.isEmpty(); 855 } 856 devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)857 void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { 858 Intent intent; 859 byte[] val; 860 int type; 861 BluetoothDevice bdDevice = getDevice(address); 862 DeviceProperties deviceProperties; 863 if (bdDevice == null) { 864 debugLog("Added new device property, device=" + bdDevice); 865 deviceProperties = addDeviceProperties(address); 866 bdDevice = getDevice(address); 867 } else { 868 deviceProperties = getDeviceProperties(bdDevice); 869 } 870 871 if (types.length <= 0) { 872 errorLog("No properties to update"); 873 return; 874 } 875 876 for (int j = 0; j < types.length; j++) { 877 type = types[j]; 878 val = values[j]; 879 if (val.length > 0) { 880 synchronized (mObject) { 881 debugLog("Update property, device=" + bdDevice + ", type: " + type); 882 switch (type) { 883 case AbstractionLayer.BT_PROPERTY_BDNAME: 884 final String newName = new String(val); 885 if (newName.equals(deviceProperties.getName())) { 886 debugLog("Skip name update for " + bdDevice); 887 break; 888 } 889 deviceProperties.setName(newName); 890 intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); 891 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 892 intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProperties.getName()); 893 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 894 mAdapterService.sendBroadcast( 895 intent, 896 BLUETOOTH_CONNECT, 897 Utils.getTempBroadcastOptions().toBundle()); 898 debugLog("Remote device name is: " + deviceProperties.getName()); 899 break; 900 case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: 901 deviceProperties.setAlias(bdDevice, new String(val)); 902 debugLog("Remote device alias is: " + deviceProperties.getAlias()); 903 break; 904 case AbstractionLayer.BT_PROPERTY_BDADDR: 905 deviceProperties.setAddress(val); 906 debugLog( 907 "Remote Address is:" 908 + Utils.getRedactedAddressStringFromByte(val)); 909 break; 910 case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 911 final int newBluetoothClass = Utils.byteArrayToInt(val); 912 if (newBluetoothClass == deviceProperties.getBluetoothClass()) { 913 debugLog( 914 "Skip class update, device=" 915 + bdDevice 916 + ", cod=0x" 917 + Integer.toHexString(newBluetoothClass)); 918 break; 919 } 920 deviceProperties.setBluetoothClass(newBluetoothClass); 921 intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); 922 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 923 intent.putExtra( 924 BluetoothDevice.EXTRA_CLASS, 925 new BluetoothClass(deviceProperties.getBluetoothClass())); 926 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 927 mAdapterService.sendBroadcast( 928 intent, 929 BLUETOOTH_CONNECT, 930 Utils.getTempBroadcastOptions().toBundle()); 931 debugLog( 932 "Remote class update, device=" 933 + bdDevice 934 + ", cod=0x" 935 + Integer.toHexString(newBluetoothClass)); 936 break; 937 case AbstractionLayer.BT_PROPERTY_UUIDS: 938 final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val); 939 if (areUuidsEqual(newUuids, deviceProperties.getUuids())) { 940 // SDP Skip adding UUIDs to property cache if equal 941 debugLog("Skip uuids update for " + bdDevice.getAddress()); 942 MetricsLogger.getInstance() 943 .cacheCount(BluetoothProtoEnums.SDP_UUIDS_EQUAL_SKIP, 1); 944 break; 945 } 946 deviceProperties.setUuids(newUuids); 947 if (mAdapterService.getState() == BluetoothAdapter.STATE_ON) { 948 // SDP Adding UUIDs to property cache and sending intent 949 MetricsLogger.getInstance() 950 .cacheCount( 951 BluetoothProtoEnums.SDP_ADD_UUID_WITH_INTENT, 1); 952 mAdapterService.deviceUuidUpdated(bdDevice); 953 sendUuidIntent(bdDevice, deviceProperties); 954 } else if (mAdapterService.getState() 955 == BluetoothAdapter.STATE_BLE_ON) { 956 // SDP Adding UUIDs to property cache but with no intent 957 MetricsLogger.getInstance() 958 .cacheCount( 959 BluetoothProtoEnums.SDP_ADD_UUID_WITH_NO_INTENT, 1); 960 mAdapterService.deviceUuidUpdated(bdDevice); 961 } else { 962 // SDP Silently dropping UUIDs and with no intent 963 MetricsLogger.getInstance() 964 .cacheCount(BluetoothProtoEnums.SDP_DROP_UUID, 1); 965 } 966 break; 967 case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE: 968 if (deviceProperties.isConsolidated()) { 969 break; 970 } 971 // The device type from hal layer, defined in bluetooth.h, 972 // matches the type defined in BluetoothDevice.java 973 deviceProperties.setDeviceType(Utils.byteArrayToInt(val)); 974 break; 975 case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: 976 // RSSI from hal is in one byte 977 deviceProperties.setRssi(val[0]); 978 break; 979 case AbstractionLayer.BT_PROPERTY_REMOTE_IS_COORDINATED_SET_MEMBER: 980 deviceProperties.setIsCoordinatedSetMember(val[0] != 0); 981 break; 982 case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_CAPABILITY: 983 deviceProperties.setAshaCapability(val[0]); 984 break; 985 case AbstractionLayer.BT_PROPERTY_REMOTE_ASHA_TRUNCATED_HISYNCID: 986 deviceProperties.setAshaTruncatedHiSyncId(val[0]); 987 break; 988 case AbstractionLayer.BT_PROPERTY_REMOTE_MODEL_NUM: 989 final String modelName = new String(val); 990 debugLog("Remote device model name: " + modelName); 991 deviceProperties.setModelName(modelName); 992 BluetoothStatsLog.write( 993 BluetoothStatsLog.BLUETOOTH_DEVICE_INFO_REPORTED, 994 mAdapterService.obfuscateAddress(bdDevice), 995 BluetoothProtoEnums.DEVICE_INFO_INTERNAL, 996 LOG_SOURCE_DIS, 997 null, 998 modelName, 999 null, 1000 null, 1001 mAdapterService.getMetricId(bdDevice), 1002 bdDevice.getAddressType(), 1003 0, 1004 0, 1005 0); 1006 break; 1007 } 1008 } 1009 } 1010 } 1011 } 1012 deviceFoundCallback(byte[] address)1013 void deviceFoundCallback(byte[] address) { 1014 // The device properties are already registered - we can send the intent 1015 // now 1016 BluetoothDevice device = getDevice(address); 1017 DeviceProperties deviceProp = getDeviceProperties(device); 1018 if (deviceProp == null) { 1019 errorLog("deviceFoundCallback: Device Properties is null for Device:" + device); 1020 return; 1021 } 1022 boolean restrict_device_found = 1023 SystemProperties.getBoolean("bluetooth.restrict_discovered_device.enabled", false); 1024 if (restrict_device_found && (deviceProp.mName == null || deviceProp.mName.isEmpty())) { 1025 warnLog("deviceFoundCallback: Device name is null or empty: " + device); 1026 return; 1027 } 1028 1029 infoLog("deviceFoundCallback: Remote Address is:" + device); 1030 Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); 1031 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1032 intent.putExtra( 1033 BluetoothDevice.EXTRA_CLASS, new BluetoothClass(deviceProp.getBluetoothClass())); 1034 intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.getRssi()); 1035 intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.getName()); 1036 intent.putExtra( 1037 BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, 1038 deviceProp.isCoordinatedSetMember()); 1039 1040 final List<DiscoveringPackage> packages = mAdapterService.getDiscoveringPackages(); 1041 synchronized (packages) { 1042 for (DiscoveringPackage pkg : packages) { 1043 if (pkg.hasDisavowedLocation()) { 1044 if (mLocationDenylistPredicate.test(device)) { 1045 continue; 1046 } 1047 } 1048 1049 intent.setPackage(pkg.getPackageName()); 1050 1051 if (pkg.getPermission() != null) { 1052 mAdapterService.sendBroadcastMultiplePermissions( 1053 intent, 1054 new String[] {BLUETOOTH_SCAN, pkg.getPermission()}, 1055 Utils.getTempBroadcastOptions()); 1056 } else { 1057 mAdapterService.sendBroadcastMultiplePermissions( 1058 intent, new String[] {BLUETOOTH_SCAN}, Utils.getTempBroadcastOptions()); 1059 } 1060 } 1061 } 1062 } 1063 addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress)1064 void addressConsolidateCallback(byte[] mainAddress, byte[] secondaryAddress) { 1065 BluetoothDevice device = getDevice(mainAddress); 1066 if (device == null) { 1067 errorLog( 1068 "addressConsolidateCallback: device is NULL, address=" 1069 + Utils.getRedactedAddressStringFromByte(mainAddress) 1070 + ", secondaryAddress=" 1071 + Utils.getRedactedAddressStringFromByte(secondaryAddress)); 1072 return; 1073 } 1074 Log.d( 1075 TAG, 1076 "addressConsolidateCallback device: " 1077 + device 1078 + ", secondaryAddress:" 1079 + Utils.getRedactedAddressStringFromByte(secondaryAddress)); 1080 1081 DeviceProperties deviceProperties = getDeviceProperties(device); 1082 deviceProperties.setIsConsolidated(true); 1083 deviceProperties.setDeviceType(BluetoothDevice.DEVICE_TYPE_DUAL); 1084 deviceProperties.setIdentityAddress(Utils.getAddressStringFromByte(secondaryAddress)); 1085 mDualDevicesMap.put( 1086 deviceProperties.getIdentityAddress(), Utils.getAddressStringFromByte(mainAddress)); 1087 } 1088 1089 /** 1090 * Callback to associate an LE-only device's RPA with its identity address 1091 * 1092 * @param mainAddress the device's RPA 1093 * @param secondaryAddress the device's identity address 1094 */ leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress)1095 void leAddressAssociateCallback(byte[] mainAddress, byte[] secondaryAddress) { 1096 BluetoothDevice device = getDevice(mainAddress); 1097 if (device == null) { 1098 errorLog( 1099 "leAddressAssociateCallback: device is NULL, address=" 1100 + Utils.getRedactedAddressStringFromByte(mainAddress) 1101 + ", secondaryAddress=" 1102 + Utils.getRedactedAddressStringFromByte(secondaryAddress)); 1103 return; 1104 } 1105 Log.d( 1106 TAG, 1107 "leAddressAssociateCallback device: " 1108 + device 1109 + ", secondaryAddress:" 1110 + Utils.getRedactedAddressStringFromByte(secondaryAddress)); 1111 1112 DeviceProperties deviceProperties = getDeviceProperties(device); 1113 deviceProperties.mIdentityAddress = Utils.getAddressStringFromByte(secondaryAddress); 1114 } 1115 1116 @RequiresPermission( 1117 allOf = { 1118 android.Manifest.permission.BLUETOOTH_CONNECT, 1119 android.Manifest.permission.BLUETOOTH_PRIVILEGED, 1120 }) aclStateChangeCallback( int status, byte[] address, int newState, int transportLinkType, int hciReason, int handle)1121 void aclStateChangeCallback( 1122 int status, 1123 byte[] address, 1124 int newState, 1125 int transportLinkType, 1126 int hciReason, 1127 int handle) { 1128 if (status != AbstractionLayer.BT_STATUS_SUCCESS) { 1129 debugLog("aclStateChangeCallback status is " + status + ", skipping"); 1130 return; 1131 } 1132 1133 BluetoothDevice device = getDevice(address); 1134 1135 if (device == null) { 1136 warnLog( 1137 "aclStateChangeCallback: device is NULL, address=" 1138 + Utils.getRedactedAddressStringFromByte(address) 1139 + ", newState=" 1140 + newState); 1141 addDeviceProperties(address); 1142 device = Objects.requireNonNull(getDevice(address)); 1143 } 1144 1145 DeviceProperties deviceProperties = getDeviceProperties(device); 1146 1147 int state = mAdapterService.getState(); 1148 1149 Intent intent = null; 1150 if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) { 1151 deviceProperties.setConnectionHandle(handle, transportLinkType); 1152 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) { 1153 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); 1154 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType); 1155 } else if (state == BluetoothAdapter.STATE_BLE_ON 1156 || state == BluetoothAdapter.STATE_BLE_TURNING_ON) { 1157 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED); 1158 } 1159 BatteryService batteryService = BatteryService.getBatteryService(); 1160 if (batteryService != null && transportLinkType == BluetoothDevice.TRANSPORT_LE) { 1161 batteryService.connectIfPossible(device); 1162 } 1163 mAdapterService.updatePhonePolicyOnAclConnect(device); 1164 SecurityLog.writeEvent( 1165 SecurityLog.TAG_BLUETOOTH_CONNECTION, 1166 Utils.getLoggableAddress(device), /* success */ 1167 1, /* reason */ 1168 ""); 1169 debugLog( 1170 "aclStateChangeCallback: Adapter State: " 1171 + BluetoothAdapter.nameForState(state) 1172 + " Connected: " 1173 + device); 1174 } else { 1175 deviceProperties.setConnectionHandle(BluetoothDevice.ERROR, transportLinkType); 1176 if (device.getBondState() == BluetoothDevice.BOND_BONDING) { 1177 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding. 1178 sendPairingCancelIntent(device); 1179 } else if (device.getBondState() == BluetoothDevice.BOND_NONE) { 1180 removeAddressMapping(Utils.getAddressStringFromByte(address)); 1181 } 1182 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) { 1183 mAdapterService.notifyAclDisconnected(device, transportLinkType); 1184 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); 1185 intent.putExtra(BluetoothDevice.EXTRA_TRANSPORT, transportLinkType); 1186 } else if (state == BluetoothAdapter.STATE_BLE_ON 1187 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { 1188 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); 1189 } 1190 // Reset battery level on complete disconnection 1191 if (mAdapterService.getConnectionState(device) == 0) { 1192 BatteryService batteryService = BatteryService.getBatteryService(); 1193 if (batteryService != null 1194 && batteryService.getConnectionState(device) 1195 != BluetoothProfile.STATE_DISCONNECTED 1196 && transportLinkType == BluetoothDevice.TRANSPORT_LE) { 1197 batteryService.disconnect(device); 1198 } 1199 resetBatteryLevel(device, /* isBas= */ true); 1200 } 1201 1202 if (mAdapterService.isAllProfilesUnknown(device)) { 1203 DeviceProperties deviceProp = getDeviceProperties(device); 1204 if (deviceProp != null) { 1205 deviceProp.setBondingInitiatedLocally(false); 1206 } 1207 } 1208 SecurityLog.writeEvent( 1209 SecurityLog.TAG_BLUETOOTH_DISCONNECTION, 1210 Utils.getLoggableAddress(device), 1211 BluetoothAdapter.BluetoothConnectionCallback.disconnectReasonToString( 1212 AdapterService.hciToAndroidDisconnectReason(hciReason))); 1213 debugLog( 1214 "aclStateChangeCallback: Adapter State: " 1215 + BluetoothAdapter.nameForState(state) 1216 + " Disconnected: " 1217 + device 1218 + " transportLinkType: " 1219 + transportLinkType 1220 + " hciReason: " 1221 + hciReason); 1222 } 1223 1224 int connectionState = 1225 newState == AbstractionLayer.BT_ACL_STATE_CONNECTED 1226 ? BluetoothAdapter.STATE_CONNECTED 1227 : BluetoothAdapter.STATE_DISCONNECTED; 1228 int metricId = mAdapterService.getMetricId(device); 1229 BluetoothStatsLog.write( 1230 BluetoothStatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED, 1231 mAdapterService.obfuscateAddress(device), 1232 connectionState, 1233 metricId, 1234 transportLinkType); 1235 1236 BluetoothClass deviceClass = device.getBluetoothClass(); 1237 int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice(); 1238 BluetoothStatsLog.write( 1239 BluetoothStatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED, 1240 mAdapterService.obfuscateAddress(device), 1241 classOfDevice, 1242 metricId); 1243 1244 if (intent != null) { 1245 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device) 1246 .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) 1247 .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1248 mAdapterService.sendBroadcast( 1249 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1250 1251 synchronized (mAdapterService.getBluetoothConnectionCallbacks()) { 1252 Set<IBluetoothConnectionCallback> bluetoothConnectionCallbacks = 1253 mAdapterService.getBluetoothConnectionCallbacks(); 1254 for (IBluetoothConnectionCallback callback : bluetoothConnectionCallbacks) { 1255 try { 1256 if (connectionState == BluetoothAdapter.STATE_CONNECTED) { 1257 callback.onDeviceConnected(device); 1258 } else { 1259 callback.onDeviceDisconnected( 1260 device, AdapterService.hciToAndroidDisconnectReason(hciReason)); 1261 } 1262 } catch (RemoteException ex) { 1263 Log.e(TAG, "RemoteException in calling IBluetoothConnectionCallback"); 1264 } 1265 } 1266 } 1267 } else { 1268 Log.e( 1269 TAG, 1270 "aclStateChangeCallback intent is null. deviceBondState: " 1271 + device.getBondState()); 1272 } 1273 } 1274 1275 @NonNull sendPairingCancelIntent(BluetoothDevice device)1276 private void sendPairingCancelIntent(BluetoothDevice device) { 1277 Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL); 1278 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 1279 intent.setPackage( 1280 SystemProperties.get( 1281 Utils.PAIRING_UI_PROPERTY, 1282 mAdapterService.getString(R.string.pairing_ui_package))); 1283 1284 Log.i(TAG, "sendPairingCancelIntent: device=" + device); 1285 mAdapterService.sendBroadcast( 1286 intent, BLUETOOTH_CONNECT, Utils.getTempBroadcastOptions().toBundle()); 1287 } 1288 removeAddressMapping(String address)1289 private void removeAddressMapping(String address) { 1290 if (Flags.temporaryPairingDeviceProperties()) { 1291 DeviceProperties deviceProperties = mDevices.get(address); 1292 if (deviceProperties != null) { 1293 String pseudoAddress = mDualDevicesMap.get(address); 1294 if (pseudoAddress != null) { 1295 deviceProperties = mDevices.get(pseudoAddress); 1296 } 1297 } 1298 1299 if (deviceProperties != null) { 1300 int leConnectionHandle = 1301 deviceProperties.getConnectionHandle(BluetoothDevice.TRANSPORT_LE); 1302 int bredrConnectionHandle = 1303 deviceProperties.getConnectionHandle(BluetoothDevice.TRANSPORT_BREDR); 1304 if (leConnectionHandle != BluetoothDevice.ERROR 1305 || bredrConnectionHandle != BluetoothDevice.ERROR) { 1306 // Device still connected, wait for disconnection to remove the properties 1307 return; 1308 } 1309 } 1310 } 1311 1312 synchronized (mDevices) { 1313 mDevices.remove(address); 1314 mDeviceQueue.remove(address); // Remove from LRU cache 1315 1316 // Remove from dual mode device mappings 1317 mDualDevicesMap.values().remove(address); 1318 mDualDevicesMap.remove(address); 1319 } 1320 } 1321 onBondStateChange(BluetoothDevice device, int newState)1322 void onBondStateChange(BluetoothDevice device, int newState) { 1323 String address = device.getAddress(); 1324 1325 if (Flags.removeAddressMapOnUnbond() && newState == BluetoothDevice.BOND_NONE) { 1326 removeAddressMapping(address); 1327 } 1328 } 1329 keyMissingCallback(byte[] address)1330 void keyMissingCallback(byte[] address) { 1331 BluetoothDevice bluetoothDevice = getDevice(address); 1332 if (bluetoothDevice == null) { 1333 errorLog( 1334 "keyMissingCallback: device is NULL, address=" 1335 + Utils.getRedactedAddressStringFromByte(address)); 1336 return; 1337 } 1338 Log.i(TAG, "keyMissingCallback device: " + bluetoothDevice); 1339 1340 if (bluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDED) { 1341 if (!Flags.keyMissingBroadcast()) { 1342 Log.d(TAG, "flag not set - don't send key missing broadcast"); 1343 return; 1344 } 1345 Intent intent = 1346 new Intent(BluetoothDevice.ACTION_KEY_MISSING) 1347 .putExtra(BluetoothDevice.EXTRA_DEVICE, bluetoothDevice) 1348 .addFlags( 1349 Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1350 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1351 mAdapterService.sendBroadcastMultiplePermissions( 1352 intent, 1353 new String[] {BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED}, 1354 Utils.getTempBroadcastOptions()); 1355 } 1356 } 1357 fetchUuids(BluetoothDevice device, int transport)1358 void fetchUuids(BluetoothDevice device, int transport) { 1359 if (mSdpTracker.contains(device)) { 1360 debugLog( 1361 "Skip fetch UUIDs are they are already cached peer:" 1362 + device 1363 + " transport:" 1364 + transport); 1365 MetricsLogger.getInstance() 1366 .cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_CACHED, 1); 1367 return; 1368 } 1369 1370 // If no UUIDs are cached and the device is bonding, wait for SDP after the device is bonded 1371 DeviceProperties deviceProperties = getDeviceProperties(device); 1372 if (deviceProperties != null 1373 && deviceProperties.isBonding() 1374 && getDeviceProperties(device).getUuids() == null) { 1375 debugLog("Skip fetch UUIDs due to bonding peer:" + device + " transport:" + transport); 1376 MetricsLogger.getInstance() 1377 .cacheCount(BluetoothProtoEnums.SDP_FETCH_UUID_SKIP_ALREADY_BONDED, 1); 1378 return; 1379 } 1380 1381 mSdpTracker.add(device); 1382 1383 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 1384 message.obj = device; 1385 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 1386 1387 // Uses cached UUIDs if we are bonding. If not, we fetch the UUIDs with SDP. 1388 if (deviceProperties == null || !deviceProperties.isBonding()) { 1389 debugLog( 1390 "Invoking core stack to spin up SDP cycle peer:" 1391 + device 1392 + " transport:" 1393 + transport); 1394 mAdapterService 1395 .getNative() 1396 .getRemoteServices(Utils.getBytesFromAddress(device.getAddress()), transport); 1397 MetricsLogger.getInstance().cacheCount(BluetoothProtoEnums.SDP_INVOKE_SDP_CYCLE, 1); 1398 } 1399 } 1400 updateUuids(BluetoothDevice device)1401 void updateUuids(BluetoothDevice device) { 1402 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 1403 message.obj = device; 1404 mHandler.sendMessage(message); 1405 } 1406 1407 /** Handles headset connection state change event */ handleHeadsetConnectionStateChanged( BluetoothDevice device, int fromState, int toState)1408 public void handleHeadsetConnectionStateChanged( 1409 BluetoothDevice device, int fromState, int toState) { 1410 mMainHandler.post(() -> onHeadsetConnectionStateChanged(device, fromState, toState)); 1411 } 1412 1413 @VisibleForTesting onHeadsetConnectionStateChanged(BluetoothDevice device, int fromState, int toState)1414 void onHeadsetConnectionStateChanged(BluetoothDevice device, int fromState, int toState) { 1415 if (device == null) { 1416 Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null"); 1417 return; 1418 } 1419 if (toState == BluetoothProfile.STATE_DISCONNECTED && !hasBatteryService(device)) { 1420 resetBatteryLevel(device, /* isBas= */ false); 1421 } 1422 } 1423 1424 /** Handle indication events from Hands-free. */ handleHfIndicatorValueChanged( BluetoothDevice device, int indicatorId, int indicatorValue)1425 public void handleHfIndicatorValueChanged( 1426 BluetoothDevice device, int indicatorId, int indicatorValue) { 1427 mMainHandler.post(() -> onHfIndicatorValueChanged(device, indicatorId, indicatorValue)); 1428 } 1429 1430 @VisibleForTesting onHfIndicatorValueChanged(BluetoothDevice device, int indicatorId, int indicatorValue)1431 void onHfIndicatorValueChanged(BluetoothDevice device, int indicatorId, int indicatorValue) { 1432 if (device == null) { 1433 Log.e(TAG, "onHfIndicatorValueChanged() remote device is null"); 1434 return; 1435 } 1436 if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) { 1437 updateBatteryLevel(device, indicatorValue, /* isBas= */ false); 1438 } 1439 } 1440 1441 /** Handles Headset specific Bluetooth events */ handleVendorSpecificHeadsetEvent( BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args)1442 public void handleVendorSpecificHeadsetEvent( 1443 BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) { 1444 mMainHandler.post( 1445 () -> onVendorSpecificHeadsetEvent(device, cmd, companyId, cmdType, args)); 1446 } 1447 1448 @VisibleForTesting onVendorSpecificHeadsetEvent( BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args)1449 void onVendorSpecificHeadsetEvent( 1450 BluetoothDevice device, String cmd, int companyId, int cmdType, Object[] args) { 1451 if (device == null) { 1452 Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null"); 1453 return; 1454 } 1455 if (companyId != BluetoothAssignedNumbers.PLANTRONICS 1456 && companyId != BluetoothAssignedNumbers.APPLE) { 1457 Log.i( 1458 TAG, 1459 "onVendorSpecificHeadsetEvent() filtered out non-PLANTRONICS and non-APPLE " 1460 + "vendor commands"); 1461 return; 1462 } 1463 if (cmd == null) { 1464 Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null"); 1465 return; 1466 } 1467 // Only process set command 1468 if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) { 1469 debugLog("onVendorSpecificHeadsetEvent() only SET command is processed"); 1470 return; 1471 } 1472 if (args == null) { 1473 Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null"); 1474 return; 1475 } 1476 int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1477 switch (cmd) { 1478 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT: 1479 batteryPercent = getBatteryLevelFromXEventVsc(args); 1480 break; 1481 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV: 1482 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args); 1483 break; 1484 } 1485 if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 1486 updateBatteryLevel(device, batteryPercent, /* isBas= */ false); 1487 infoLog( 1488 "Updated device " 1489 + device 1490 + " battery level to " 1491 + String.valueOf(batteryPercent) 1492 + "%"); 1493 } 1494 } 1495 1496 /** 1497 * Parse AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue] vendor specific 1498 * event 1499 * 1500 * @param args Array of arguments on the right side of assignment 1501 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 1502 * when there is an error parsing the arguments 1503 */ 1504 @VisibleForTesting getBatteryLevelFromAppleBatteryVsc(Object[] args)1505 static int getBatteryLevelFromAppleBatteryVsc(Object[] args) { 1506 if (args.length == 0) { 1507 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments"); 1508 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1509 } 1510 int numKvPair; 1511 if (args[0] instanceof Integer) { 1512 numKvPair = (Integer) args[0]; 1513 } else { 1514 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments"); 1515 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1516 } 1517 if (args.length != (numKvPair * 2 + 1)) { 1518 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match"); 1519 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1520 } 1521 int indicatorType; 1522 int indicatorValue = -1; 1523 for (int i = 0; i < numKvPair; ++i) { 1524 Object indicatorTypeObj = args[2 * i + 1]; 1525 if (indicatorTypeObj instanceof Integer) { 1526 indicatorType = (Integer) indicatorTypeObj; 1527 } else { 1528 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type"); 1529 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1530 } 1531 if (indicatorType 1532 != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) { 1533 continue; 1534 } 1535 Object indicatorValueObj = args[2 * i + 2]; 1536 if (indicatorValueObj instanceof Integer) { 1537 indicatorValue = (Integer) indicatorValueObj; 1538 } else { 1539 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value"); 1540 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1541 } 1542 break; 1543 } 1544 return (indicatorValue < 0 || indicatorValue > 9) 1545 ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN 1546 : (indicatorValue + 1) * 10; 1547 } 1548 1549 /** 1550 * Parse AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging] vendor specific 1551 * event 1552 * 1553 * @param args Array of arguments on the right side of SET command 1554 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 1555 * when there is an error parsing the arguments 1556 */ 1557 @VisibleForTesting getBatteryLevelFromXEventVsc(Object[] args)1558 static int getBatteryLevelFromXEventVsc(Object[] args) { 1559 if (args.length == 0) { 1560 Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments"); 1561 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1562 } 1563 Object eventNameObj = args[0]; 1564 if (!(eventNameObj instanceof String)) { 1565 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name"); 1566 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1567 } 1568 String eventName = (String) eventNameObj; 1569 if (!eventName.equals( 1570 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) { 1571 infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName); 1572 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1573 } 1574 if (args.length != 5) { 1575 Log.w( 1576 TAG, 1577 "getBatteryLevelFromXEventVsc() wrong battery level event length: " 1578 + String.valueOf(args.length)); 1579 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1580 } 1581 if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) { 1582 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values"); 1583 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1584 } 1585 int batteryLevel = (Integer) args[1]; 1586 int numberOfLevels = (Integer) args[2]; 1587 if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) { 1588 Log.w( 1589 TAG, 1590 "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel=" 1591 + String.valueOf(batteryLevel) 1592 + ", numberOfLevels=" 1593 + String.valueOf(numberOfLevels)); 1594 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 1595 } 1596 return batteryLevel * 100 / (numberOfLevels - 1); 1597 } 1598 1599 @VisibleForTesting hasBatteryService(BluetoothDevice device)1600 boolean hasBatteryService(BluetoothDevice device) { 1601 BatteryService batteryService = BatteryService.getBatteryService(); 1602 return batteryService != null 1603 && batteryService.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED; 1604 } 1605 1606 /** Handles headset client connection state change event. */ handleHeadsetClientConnectionStateChanged( BluetoothDevice device, int fromState, int toState)1607 public void handleHeadsetClientConnectionStateChanged( 1608 BluetoothDevice device, int fromState, int toState) { 1609 mMainHandler.post(() -> onHeadsetClientConnectionStateChanged(device, fromState, toState)); 1610 } 1611 1612 @VisibleForTesting onHeadsetClientConnectionStateChanged(BluetoothDevice device, int fromState, int toState)1613 void onHeadsetClientConnectionStateChanged(BluetoothDevice device, int fromState, int toState) { 1614 if (device == null) { 1615 Log.e(TAG, "onHeadsetClientConnectionStateChanged() remote device is null"); 1616 return; 1617 } 1618 if (toState == BluetoothProfile.STATE_DISCONNECTED && !hasBatteryService(device)) { 1619 resetBatteryLevel(device, /* isBas= */ false); 1620 } 1621 } 1622 1623 /** Handle battery level changes indication events from Audio Gateway. */ handleAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel)1624 public void handleAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) { 1625 mMainHandler.post(() -> onAgBatteryLevelChanged(device, batteryLevel)); 1626 } 1627 1628 @VisibleForTesting onAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel)1629 void onAgBatteryLevelChanged(BluetoothDevice device, int batteryLevel) { 1630 if (device == null) { 1631 Log.e(TAG, "onAgBatteryLevelChanged() remote device is null"); 1632 return; 1633 } 1634 updateBatteryLevel( 1635 device, batteryChargeIndicatorToPercentge(batteryLevel), /* isBas= */ false); 1636 } 1637 errorLog(String msg)1638 private static void errorLog(String msg) { 1639 Log.e(TAG, msg); 1640 } 1641 debugLog(String msg)1642 private static void debugLog(String msg) { 1643 Log.d(TAG, msg); 1644 } 1645 infoLog(String msg)1646 private static void infoLog(String msg) { 1647 Log.i(TAG, msg); 1648 } 1649 warnLog(String msg)1650 private static void warnLog(String msg) { 1651 Log.w(TAG, msg); 1652 } 1653 } 1654