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