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