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 import android.util.StatsLog; 35 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 mBondState)314 void setBondState(int mBondState) { 315 synchronized (mObject) { 316 this.mBondState = mBondState; 317 if (mBondState == BluetoothDevice.BOND_NONE) { 318 /* Clearing the Uuids local copy when the device is unpaired. If not cleared, 319 cachedBluetoothDevice issued a connect using the local cached copy of uuids, 320 without waiting for the ACTION_UUID intent. 321 This was resulting in multiple calls to connect().*/ 322 mUuids = null; 323 } 324 } 325 } 326 327 /** 328 * @return the mBondState 329 */ getBondState()330 int getBondState() { 331 synchronized (mObject) { 332 return mBondState; 333 } 334 } 335 336 /** 337 * @param isBondingInitiatedLocally wether bonding is initiated locally 338 */ setBondingInitiatedLocally(boolean isBondingInitiatedLocally)339 void setBondingInitiatedLocally(boolean isBondingInitiatedLocally) { 340 synchronized (mObject) { 341 this.mIsBondingInitiatedLocally = isBondingInitiatedLocally; 342 } 343 } 344 345 /** 346 * @return the isBondingInitiatedLocally 347 */ isBondingInitiatedLocally()348 boolean isBondingInitiatedLocally() { 349 synchronized (mObject) { 350 return mIsBondingInitiatedLocally; 351 } 352 } 353 getBatteryLevel()354 int getBatteryLevel() { 355 synchronized (mObject) { 356 return mBatteryLevel; 357 } 358 } 359 360 /** 361 * @param batteryLevel the mBatteryLevel to set 362 */ setBatteryLevel(int batteryLevel)363 void setBatteryLevel(int batteryLevel) { 364 synchronized (mObject) { 365 this.mBatteryLevel = batteryLevel; 366 } 367 } 368 } 369 sendUuidIntent(BluetoothDevice device, DeviceProperties prop)370 private void sendUuidIntent(BluetoothDevice device, DeviceProperties prop) { 371 Intent intent = new Intent(BluetoothDevice.ACTION_UUID); 372 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 373 intent.putExtra(BluetoothDevice.EXTRA_UUID, prop == null ? null : prop.mUuids); 374 sAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_ADMIN_PERM); 375 376 //Remove the outstanding UUID request 377 sSdpTracker.remove(device); 378 } 379 380 /** 381 * When bonding is initiated to remote device that we have never seen, i.e Out Of Band pairing, 382 * we must add device first before setting it's properties. This is a helper method for doing 383 * that. 384 */ setBondingInitiatedLocally(byte[] address)385 void setBondingInitiatedLocally(byte[] address) { 386 DeviceProperties properties; 387 388 BluetoothDevice device = getDevice(address); 389 if (device == null) { 390 properties = addDeviceProperties(address); 391 } else { 392 properties = getDeviceProperties(device); 393 } 394 395 properties.setBondingInitiatedLocally(true); 396 } 397 398 /** 399 * Update battery level in device properties 400 * @param device The remote device to be updated 401 * @param batteryLevel Battery level Indicator between 0-100, 402 * {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} is error 403 */ 404 @VisibleForTesting updateBatteryLevel(BluetoothDevice device, int batteryLevel)405 void updateBatteryLevel(BluetoothDevice device, int batteryLevel) { 406 if (device == null || batteryLevel < 0 || batteryLevel > 100) { 407 warnLog("Invalid parameters device=" + String.valueOf(device == null) 408 + ", batteryLevel=" + String.valueOf(batteryLevel)); 409 return; 410 } 411 DeviceProperties deviceProperties = getDeviceProperties(device); 412 if (deviceProperties == null) { 413 deviceProperties = addDeviceProperties(Utils.getByteAddress(device)); 414 } 415 synchronized (mObject) { 416 int currentBatteryLevel = deviceProperties.getBatteryLevel(); 417 if (batteryLevel == currentBatteryLevel) { 418 debugLog("Same battery level for device " + device + " received " + String.valueOf( 419 batteryLevel) + "%"); 420 return; 421 } 422 deviceProperties.setBatteryLevel(batteryLevel); 423 } 424 sendBatteryLevelChangedBroadcast(device, batteryLevel); 425 Log.d(TAG, "Updated device " + device + " battery level to " + batteryLevel + "%"); 426 } 427 428 /** 429 * Reset battery level property to {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} for a device 430 * @param device device whose battery level property needs to be reset 431 */ 432 @VisibleForTesting resetBatteryLevel(BluetoothDevice device)433 void resetBatteryLevel(BluetoothDevice device) { 434 if (device == null) { 435 warnLog("Device is null"); 436 return; 437 } 438 DeviceProperties deviceProperties = getDeviceProperties(device); 439 if (deviceProperties == null) { 440 return; 441 } 442 synchronized (mObject) { 443 if (deviceProperties.getBatteryLevel() == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 444 debugLog("Battery level was never set or is already reset, device=" + device); 445 return; 446 } 447 deviceProperties.setBatteryLevel(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 448 } 449 sendBatteryLevelChangedBroadcast(device, BluetoothDevice.BATTERY_LEVEL_UNKNOWN); 450 Log.d(TAG, "Reset battery level, device=" + device); 451 } 452 sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel)453 private void sendBatteryLevelChangedBroadcast(BluetoothDevice device, int batteryLevel) { 454 Intent intent = new Intent(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED); 455 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 456 intent.putExtra(BluetoothDevice.EXTRA_BATTERY_LEVEL, batteryLevel); 457 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 458 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 459 sAdapterService.sendBroadcast(intent, AdapterService.BLUETOOTH_PERM); 460 } 461 areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2)462 private static boolean areUuidsEqual(ParcelUuid[] uuids1, ParcelUuid[] uuids2) { 463 final int length1 = uuids1 == null ? 0 : uuids1.length; 464 final int length2 = uuids2 == null ? 0 : uuids2.length; 465 if (length1 != length2) { 466 return false; 467 } 468 Set<ParcelUuid> set = new HashSet<>(); 469 for (int i = 0; i < length1; ++i) { 470 set.add(uuids1[i]); 471 } 472 for (int i = 0; i < length2; ++i) { 473 set.remove(uuids2[i]); 474 } 475 return set.isEmpty(); 476 } 477 devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values)478 void devicePropertyChangedCallback(byte[] address, int[] types, byte[][] values) { 479 Intent intent; 480 byte[] val; 481 int type; 482 BluetoothDevice bdDevice = getDevice(address); 483 DeviceProperties device; 484 if (bdDevice == null) { 485 debugLog("Added new device property"); 486 device = addDeviceProperties(address); 487 bdDevice = getDevice(address); 488 } else { 489 device = getDeviceProperties(bdDevice); 490 } 491 492 if (types.length <= 0) { 493 errorLog("No properties to update"); 494 return; 495 } 496 497 for (int j = 0; j < types.length; j++) { 498 type = types[j]; 499 val = values[j]; 500 if (val.length > 0) { 501 synchronized (mObject) { 502 debugLog("Property type: " + type); 503 switch (type) { 504 case AbstractionLayer.BT_PROPERTY_BDNAME: 505 final String newName = new String(val); 506 if (newName.equals(device.mName)) { 507 debugLog("Skip name update for " + bdDevice); 508 break; 509 } 510 device.mName = newName; 511 intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED); 512 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 513 intent.putExtra(BluetoothDevice.EXTRA_NAME, device.mName); 514 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 515 sAdapterService.sendBroadcast(intent, sAdapterService.BLUETOOTH_PERM); 516 debugLog("Remote Device name is: " + device.mName); 517 break; 518 case AbstractionLayer.BT_PROPERTY_REMOTE_FRIENDLY_NAME: 519 device.mAlias = new String(val); 520 debugLog("Remote device alias is: " + device.mAlias); 521 break; 522 case AbstractionLayer.BT_PROPERTY_BDADDR: 523 device.mAddress = val; 524 debugLog("Remote Address is:" + Utils.getAddressStringFromByte(val)); 525 break; 526 case AbstractionLayer.BT_PROPERTY_CLASS_OF_DEVICE: 527 final int newClass = Utils.byteArrayToInt(val); 528 if (newClass == device.mBluetoothClass) { 529 debugLog("Skip class update for " + bdDevice); 530 break; 531 } 532 device.mBluetoothClass = Utils.byteArrayToInt(val); 533 intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED); 534 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, bdDevice); 535 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 536 new BluetoothClass(device.mBluetoothClass)); 537 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 538 sAdapterService.sendBroadcast(intent, sAdapterService.BLUETOOTH_PERM); 539 debugLog("Remote class is:" + device.mBluetoothClass); 540 break; 541 case AbstractionLayer.BT_PROPERTY_UUIDS: 542 int numUuids = val.length / AbstractionLayer.BT_UUID_SIZE; 543 final ParcelUuid[] newUuids = Utils.byteArrayToUuid(val); 544 if (areUuidsEqual(newUuids, device.mUuids)) { 545 debugLog( "Skip uuids update for " + bdDevice.getAddress()); 546 break; 547 } 548 device.mUuids = newUuids; 549 if (sAdapterService.getState() == BluetoothAdapter.STATE_ON) { 550 sAdapterService.deviceUuidUpdated(bdDevice); 551 sendUuidIntent(bdDevice, device); 552 } 553 break; 554 case AbstractionLayer.BT_PROPERTY_TYPE_OF_DEVICE: 555 // The device type from hal layer, defined in bluetooth.h, 556 // matches the type defined in BluetoothDevice.java 557 device.mDeviceType = Utils.byteArrayToInt(val); 558 break; 559 case AbstractionLayer.BT_PROPERTY_REMOTE_RSSI: 560 // RSSI from hal is in one byte 561 device.mRssi = val[0]; 562 break; 563 } 564 } 565 } 566 } 567 } 568 deviceFoundCallback(byte[] address)569 void deviceFoundCallback(byte[] address) { 570 // The device properties are already registered - we can send the intent 571 // now 572 BluetoothDevice device = getDevice(address); 573 debugLog("deviceFoundCallback: Remote Address is:" + device); 574 DeviceProperties deviceProp = getDeviceProperties(device); 575 if (deviceProp == null) { 576 errorLog("Device Properties is null for Device:" + device); 577 return; 578 } 579 580 Intent intent = new Intent(BluetoothDevice.ACTION_FOUND); 581 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 582 intent.putExtra(BluetoothDevice.EXTRA_CLASS, 583 new BluetoothClass(deviceProp.mBluetoothClass)); 584 intent.putExtra(BluetoothDevice.EXTRA_RSSI, deviceProp.mRssi); 585 intent.putExtra(BluetoothDevice.EXTRA_NAME, deviceProp.mName); 586 587 final ArrayList<DiscoveringPackage> packages = sAdapterService.getDiscoveringPackages(); 588 synchronized (packages) { 589 for (DiscoveringPackage pkg : packages) { 590 intent.setPackage(pkg.getPackageName()); 591 sAdapterService.sendBroadcastMultiplePermissions(intent, new String[]{ 592 AdapterService.BLUETOOTH_PERM, pkg.getPermission() 593 }); 594 } 595 } 596 } 597 aclStateChangeCallback(int status, byte[] address, int newState)598 void aclStateChangeCallback(int status, byte[] address, int newState) { 599 BluetoothDevice device = getDevice(address); 600 601 if (device == null) { 602 errorLog("aclStateChangeCallback: device is NULL, address=" 603 + Utils.getAddressStringFromByte(address) + ", newState=" + newState); 604 return; 605 } 606 int state = sAdapterService.getState(); 607 608 Intent intent = null; 609 if (newState == AbstractionLayer.BT_ACL_STATE_CONNECTED) { 610 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_ON) { 611 intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); 612 } else if (state == BluetoothAdapter.STATE_BLE_ON 613 || state == BluetoothAdapter.STATE_BLE_TURNING_ON) { 614 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_CONNECTED); 615 } 616 debugLog( 617 "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) 618 + " Connected: " + device); 619 } else { 620 if (device.getBondState() == BluetoothDevice.BOND_BONDING) { 621 // Send PAIRING_CANCEL intent to dismiss any dialog requesting bonding. 622 intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL); 623 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 624 intent.setPackage(sAdapterService.getString(R.string.pairing_ui_package)); 625 sAdapterService.sendBroadcast(intent, sAdapterService.BLUETOOTH_PERM); 626 } 627 if (state == BluetoothAdapter.STATE_ON || state == BluetoothAdapter.STATE_TURNING_OFF) { 628 intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); 629 } else if (state == BluetoothAdapter.STATE_BLE_ON 630 || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) { 631 intent = new Intent(BluetoothAdapter.ACTION_BLE_ACL_DISCONNECTED); 632 } 633 // Reset battery level on complete disconnection 634 if (sAdapterService.getConnectionState(device) == 0) { 635 resetBatteryLevel(device); 636 } 637 debugLog( 638 "aclStateChangeCallback: Adapter State: " + BluetoothAdapter.nameForState(state) 639 + " Disconnected: " + device); 640 } 641 642 int connectionState = newState == AbstractionLayer.BT_ACL_STATE_CONNECTED 643 ? BluetoothAdapter.STATE_CONNECTED : BluetoothAdapter.STATE_DISCONNECTED; 644 StatsLog.write(StatsLog.BLUETOOTH_ACL_CONNECTION_STATE_CHANGED, 645 sAdapterService.obfuscateAddress(device), connectionState); 646 BluetoothClass deviceClass = device.getBluetoothClass(); 647 int classOfDevice = deviceClass == null ? 0 : deviceClass.getClassOfDevice(); 648 StatsLog.write(StatsLog.BLUETOOTH_CLASS_OF_DEVICE_REPORTED, 649 sAdapterService.obfuscateAddress(device), classOfDevice); 650 651 if (intent != null) { 652 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 653 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 654 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 655 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 656 sAdapterService.sendBroadcast(intent, sAdapterService.BLUETOOTH_PERM); 657 } else { 658 Log.e(TAG, "aclStateChangeCallback intent is null. deviceBondState: " 659 + device.getBondState()); 660 } 661 } 662 663 fetchUuids(BluetoothDevice device)664 void fetchUuids(BluetoothDevice device) { 665 if (sSdpTracker.contains(device)) { 666 return; 667 } 668 sSdpTracker.add(device); 669 670 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 671 message.obj = device; 672 mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY); 673 674 sAdapterService.getRemoteServicesNative(Utils.getBytesFromAddress(device.getAddress())); 675 } 676 updateUuids(BluetoothDevice device)677 void updateUuids(BluetoothDevice device) { 678 Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT); 679 message.obj = device; 680 mHandler.sendMessage(message); 681 } 682 683 /** 684 * Handles headset connection state change event 685 * @param intent must be {@link BluetoothHeadset#ACTION_CONNECTION_STATE_CHANGED} intent 686 */ 687 @VisibleForTesting onHeadsetConnectionStateChanged(Intent intent)688 void onHeadsetConnectionStateChanged(Intent intent) { 689 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 690 if (device == null) { 691 Log.e(TAG, "onHeadsetConnectionStateChanged() remote device is null"); 692 return; 693 } 694 if (intent.getIntExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED) 695 == BluetoothProfile.STATE_DISCONNECTED) { 696 // TODO: Rework this when non-HFP sources of battery level indication is added 697 resetBatteryLevel(device); 698 } 699 } 700 701 @VisibleForTesting onHfIndicatorValueChanged(Intent intent)702 void onHfIndicatorValueChanged(Intent intent) { 703 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 704 if (device == null) { 705 Log.e(TAG, "onHfIndicatorValueChanged() remote device is null"); 706 return; 707 } 708 int indicatorId = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_ID, -1); 709 int indicatorValue = intent.getIntExtra(BluetoothHeadset.EXTRA_HF_INDICATORS_IND_VALUE, -1); 710 if (indicatorId == HeadsetHalConstants.HF_INDICATOR_BATTERY_LEVEL_STATUS) { 711 updateBatteryLevel(device, indicatorValue); 712 } 713 } 714 715 /** 716 * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent 717 * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent 718 */ 719 @VisibleForTesting onVendorSpecificHeadsetEvent(Intent intent)720 void onVendorSpecificHeadsetEvent(Intent intent) { 721 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 722 if (device == null) { 723 Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null"); 724 return; 725 } 726 String cmd = 727 intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD); 728 if (cmd == null) { 729 Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null"); 730 return; 731 } 732 int cmdType = 733 intent.getIntExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, 734 -1); 735 // Only process set command 736 if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) { 737 debugLog("onVendorSpecificHeadsetEvent() only SET command is processed"); 738 return; 739 } 740 Object[] args = (Object[]) intent.getExtras() 741 .get(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS); 742 if (args == null) { 743 Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null"); 744 return; 745 } 746 int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 747 switch (cmd) { 748 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT: 749 batteryPercent = getBatteryLevelFromXEventVsc(args); 750 break; 751 case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV: 752 batteryPercent = getBatteryLevelFromAppleBatteryVsc(args); 753 break; 754 } 755 if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { 756 updateBatteryLevel(device, batteryPercent); 757 infoLog("Updated device " + device + " battery level to " + String.valueOf( 758 batteryPercent) + "%"); 759 } 760 } 761 762 /** 763 * Parse 764 * AT+IPHONEACCEV=[NumberOfIndicators],[IndicatorType],[IndicatorValue] 765 * vendor specific event 766 * @param args Array of arguments on the right side of assignment 767 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 768 * when there is an error parsing the arguments 769 */ 770 @VisibleForTesting getBatteryLevelFromAppleBatteryVsc(Object[] args)771 static int getBatteryLevelFromAppleBatteryVsc(Object[] args) { 772 if (args.length == 0) { 773 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() empty arguments"); 774 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 775 } 776 int numKvPair; 777 if (args[0] instanceof Integer) { 778 numKvPair = (Integer) args[0]; 779 } else { 780 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing number of arguments"); 781 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 782 } 783 if (args.length != (numKvPair * 2 + 1)) { 784 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() number of arguments does not match"); 785 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 786 } 787 int indicatorType; 788 int indicatorValue = -1; 789 for (int i = 0; i < numKvPair; ++i) { 790 Object indicatorTypeObj = args[2 * i + 1]; 791 if (indicatorTypeObj instanceof Integer) { 792 indicatorType = (Integer) indicatorTypeObj; 793 } else { 794 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator type"); 795 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 796 } 797 if (indicatorType 798 != BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV_BATTERY_LEVEL) { 799 continue; 800 } 801 Object indicatorValueObj = args[2 * i + 2]; 802 if (indicatorValueObj instanceof Integer) { 803 indicatorValue = (Integer) indicatorValueObj; 804 } else { 805 Log.w(TAG, "getBatteryLevelFromAppleBatteryVsc() error parsing indicator value"); 806 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 807 } 808 break; 809 } 810 return (indicatorValue < 0 || indicatorValue > 9) ? BluetoothDevice.BATTERY_LEVEL_UNKNOWN 811 : (indicatorValue + 1) * 10; 812 } 813 814 /** 815 * Parse 816 * AT+XEVENT=BATTERY,[Level],[NumberOfLevel],[MinutesOfTalk],[IsCharging] 817 * vendor specific event 818 * @param args Array of arguments on the right side of SET command 819 * @return Battery level in percents, [0-100], {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} 820 * when there is an error parsing the arguments 821 */ 822 @VisibleForTesting getBatteryLevelFromXEventVsc(Object[] args)823 static int getBatteryLevelFromXEventVsc(Object[] args) { 824 if (args.length == 0) { 825 Log.w(TAG, "getBatteryLevelFromXEventVsc() empty arguments"); 826 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 827 } 828 Object eventNameObj = args[0]; 829 if (!(eventNameObj instanceof String)) { 830 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event name"); 831 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 832 } 833 String eventName = (String) eventNameObj; 834 if (!eventName.equals( 835 BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT_BATTERY_LEVEL)) { 836 infoLog("getBatteryLevelFromXEventVsc() skip none BATTERY event: " + eventName); 837 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 838 } 839 if (args.length != 5) { 840 Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong battery level event length: " 841 + String.valueOf(args.length)); 842 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 843 } 844 if (!(args[1] instanceof Integer) || !(args[2] instanceof Integer)) { 845 Log.w(TAG, "getBatteryLevelFromXEventVsc() error parsing event values"); 846 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 847 } 848 int batteryLevel = (Integer) args[1]; 849 int numberOfLevels = (Integer) args[2]; 850 if (batteryLevel < 0 || numberOfLevels <= 1 || batteryLevel > numberOfLevels) { 851 Log.w(TAG, "getBatteryLevelFromXEventVsc() wrong event value, batteryLevel=" 852 + String.valueOf(batteryLevel) + ", numberOfLevels=" + String.valueOf( 853 numberOfLevels)); 854 return BluetoothDevice.BATTERY_LEVEL_UNKNOWN; 855 } 856 return batteryLevel * 100 / (numberOfLevels - 1); 857 } 858 errorLog(String msg)859 private static void errorLog(String msg) { 860 Log.e(TAG, msg); 861 } 862 debugLog(String msg)863 private static void debugLog(String msg) { 864 if (DBG) { 865 Log.d(TAG, msg); 866 } 867 } 868 infoLog(String msg)869 private static void infoLog(String msg) { 870 if (DBG) { 871 Log.i(TAG, msg); 872 } 873 } 874 warnLog(String msg)875 private static void warnLog(String msg) { 876 Log.w(TAG, msg); 877 } 878 879 } 880