1 /* 2 * Copyright (C) 2011 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.settingslib.bluetooth; 18 19 import android.bluetooth.BluetoothA2dp; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothCsipSetCoordinator; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothHeadset; 24 import android.bluetooth.BluetoothHearingAid; 25 import android.bluetooth.BluetoothLeAudio; 26 import android.bluetooth.BluetoothProfile; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.os.UserHandle; 32 import android.os.UserManager; 33 import android.telephony.TelephonyManager; 34 import android.util.Log; 35 36 import androidx.annotation.NonNull; 37 import androidx.annotation.Nullable; 38 import androidx.annotation.VisibleForTesting; 39 import androidx.collection.ArraySet; 40 41 import com.android.settingslib.R; 42 import com.android.settingslib.flags.Flags; 43 import com.android.settingslib.utils.ThreadUtils; 44 45 import java.util.Collection; 46 import java.util.HashMap; 47 import java.util.Map; 48 import java.util.Objects; 49 import java.util.Set; 50 import java.util.concurrent.CopyOnWriteArrayList; 51 52 /** 53 * BluetoothEventManager receives broadcasts and callbacks from the Bluetooth 54 * API and dispatches the event on the UI thread to the right class in the 55 * Settings. 56 */ 57 public class BluetoothEventManager { 58 private static final String TAG = "BluetoothEventManager"; 59 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 60 61 private final LocalBluetoothAdapter mLocalAdapter; 62 private final LocalBluetoothManager mBtManager; 63 private final CachedBluetoothDeviceManager mDeviceManager; 64 private final IntentFilter mAdapterIntentFilter, mProfileIntentFilter; 65 private final Map<String, Handler> mHandlerMap; 66 private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); 67 private final BroadcastReceiver mProfileBroadcastReceiver = new BluetoothBroadcastReceiver(); 68 private final Collection<BluetoothCallback> mCallbacks = new CopyOnWriteArrayList<>(); 69 private final android.os.Handler mReceiverHandler; 70 private final UserHandle mUserHandle; 71 private final Context mContext; 72 private boolean mIsWorkProfile = false; 73 74 interface Handler { onReceive(Context context, Intent intent, BluetoothDevice device)75 void onReceive(Context context, Intent intent, BluetoothDevice device); 76 } 77 78 /** 79 * Creates BluetoothEventManager with the ability to pass in {@link UserHandle} that tells it to 80 * listen for bluetooth events for that particular userHandle. 81 * 82 * <p> If passing in userHandle that's different from the user running the process, 83 * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission is required. If 84 * userHandle passed in is {@code null}, we register event receiver for the 85 * {@code context.getUser()} handle. 86 */ BluetoothEventManager( LocalBluetoothAdapter adapter, LocalBluetoothManager btManager, CachedBluetoothDeviceManager deviceManager, Context context, android.os.Handler handler, @Nullable UserHandle userHandle)87 BluetoothEventManager( 88 LocalBluetoothAdapter adapter, 89 LocalBluetoothManager btManager, 90 CachedBluetoothDeviceManager deviceManager, 91 Context context, 92 android.os.Handler handler, 93 @Nullable UserHandle userHandle) { 94 mLocalAdapter = adapter; 95 mBtManager = btManager; 96 mDeviceManager = deviceManager; 97 mAdapterIntentFilter = new IntentFilter(); 98 mProfileIntentFilter = new IntentFilter(); 99 mHandlerMap = new HashMap<>(); 100 mContext = context; 101 mUserHandle = userHandle; 102 mReceiverHandler = handler; 103 104 // Bluetooth on/off broadcasts 105 addHandler(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedHandler()); 106 // Generic connected/not broadcast 107 addHandler(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED, 108 new ConnectionStateChangedHandler()); 109 110 // Discovery broadcasts 111 addHandler(BluetoothAdapter.ACTION_DISCOVERY_STARTED, 112 new ScanningStateChangedHandler(true)); 113 addHandler(BluetoothAdapter.ACTION_DISCOVERY_FINISHED, 114 new ScanningStateChangedHandler(false)); 115 addHandler(BluetoothDevice.ACTION_FOUND, new DeviceFoundHandler()); 116 addHandler(BluetoothDevice.ACTION_NAME_CHANGED, new NameChangedHandler()); 117 addHandler(BluetoothDevice.ACTION_ALIAS_CHANGED, new NameChangedHandler()); 118 119 // Pairing broadcasts 120 addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler()); 121 122 // Fine-grained state broadcasts 123 addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler()); 124 addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler()); 125 addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler()); 126 127 // Active device broadcasts 128 addHandler(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler()); 129 addHandler(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED, new ActiveDeviceChangedHandler()); 130 addHandler(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED, 131 new ActiveDeviceChangedHandler()); 132 addHandler(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED, 133 new ActiveDeviceChangedHandler()); 134 135 // Headset state changed broadcasts 136 addHandler(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED, 137 new AudioModeChangedHandler()); 138 addHandler(TelephonyManager.ACTION_PHONE_STATE_CHANGED, 139 new AudioModeChangedHandler()); 140 141 // ACL connection changed broadcasts 142 addHandler(BluetoothDevice.ACTION_ACL_CONNECTED, new AclStateChangedHandler()); 143 addHandler(BluetoothDevice.ACTION_ACL_DISCONNECTED, new AclStateChangedHandler()); 144 145 addHandler(BluetoothAdapter.ACTION_AUTO_ON_STATE_CHANGED, new AutoOnStateChangedHandler()); 146 147 registerAdapterIntentReceiver(); 148 149 UserManager userManager = context.getSystemService(UserManager.class); 150 mIsWorkProfile = userManager != null && userManager.isManagedProfile(); 151 } 152 153 /** Register to start receiving callbacks for Bluetooth events. */ registerCallback(BluetoothCallback callback)154 public void registerCallback(BluetoothCallback callback) { 155 mCallbacks.add(callback); 156 } 157 158 /** Unregister to stop receiving callbacks for Bluetooth events. */ unregisterCallback(BluetoothCallback callback)159 public void unregisterCallback(BluetoothCallback callback) { 160 mCallbacks.remove(callback); 161 } 162 163 @VisibleForTesting registerProfileIntentReceiver()164 void registerProfileIntentReceiver() { 165 registerIntentReceiver(mProfileBroadcastReceiver, mProfileIntentFilter); 166 } 167 168 @VisibleForTesting registerAdapterIntentReceiver()169 void registerAdapterIntentReceiver() { 170 registerIntentReceiver(mBroadcastReceiver, mAdapterIntentFilter); 171 } 172 173 /** 174 * Registers the provided receiver to receive the broadcasts that correspond to the 175 * passed intent filter, in the context of the provided handler. 176 */ registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter)177 private void registerIntentReceiver(BroadcastReceiver receiver, IntentFilter filter) { 178 if (mUserHandle == null) { 179 // If userHandle has not been provided, simply call registerReceiver. 180 mContext.registerReceiver(receiver, filter, null, mReceiverHandler, 181 Context.RECEIVER_EXPORTED); 182 } else { 183 // userHandle was explicitly specified, so need to call multi-user aware API. 184 mContext.registerReceiverAsUser(receiver, mUserHandle, filter, null, mReceiverHandler, 185 Context.RECEIVER_EXPORTED); 186 } 187 } 188 189 @VisibleForTesting addProfileHandler(String action, Handler handler)190 void addProfileHandler(String action, Handler handler) { 191 mHandlerMap.put(action, handler); 192 mProfileIntentFilter.addAction(action); 193 } 194 readPairedDevices()195 boolean readPairedDevices() { 196 Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices(); 197 if (bondedDevices == null) { 198 return false; 199 } 200 201 boolean deviceAdded = false; 202 for (BluetoothDevice device : bondedDevices) { 203 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 204 if (cachedDevice == null) { 205 mDeviceManager.addDevice(device); 206 deviceAdded = true; 207 } 208 } 209 210 return deviceAdded; 211 } 212 dispatchDeviceAdded(@onNull CachedBluetoothDevice cachedDevice)213 void dispatchDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) { 214 for (BluetoothCallback callback : mCallbacks) { 215 callback.onDeviceAdded(cachedDevice); 216 } 217 } 218 dispatchDeviceRemoved(@onNull CachedBluetoothDevice cachedDevice)219 void dispatchDeviceRemoved(@NonNull CachedBluetoothDevice cachedDevice) { 220 for (BluetoothCallback callback : mCallbacks) { 221 callback.onDeviceDeleted(cachedDevice); 222 } 223 } 224 dispatchProfileConnectionStateChanged( @onNull CachedBluetoothDevice device, int state, int bluetoothProfile)225 void dispatchProfileConnectionStateChanged( 226 @NonNull CachedBluetoothDevice device, int state, int bluetoothProfile) { 227 for (BluetoothCallback callback : mCallbacks) { 228 callback.onProfileConnectionStateChanged(device, state, bluetoothProfile); 229 } 230 231 if (mIsWorkProfile) { 232 Log.d(TAG, "Skip profileConnectionStateChanged for audio sharing, work profile"); 233 return; 234 } 235 236 LocalBluetoothLeBroadcast broadcast = mBtManager == null ? null 237 : mBtManager.getProfileManager().getLeAudioBroadcastProfile(); 238 LocalBluetoothLeBroadcastAssistant assistant = mBtManager == null ? null 239 : mBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); 240 // Trigger updateFallbackActiveDeviceIfNeeded when ASSISTANT profile disconnected when 241 // audio sharing is enabled. 242 if (bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT 243 && state == BluetoothAdapter.STATE_DISCONNECTED 244 && BluetoothUtils.isAudioSharingUIAvailable(mContext) 245 && broadcast != null && assistant != null && broadcast.isProfileReady() 246 && assistant.isProfileReady()) { 247 Log.d(TAG, "updateFallbackActiveDeviceIfNeeded, ASSISTANT profile disconnected"); 248 broadcast.updateFallbackActiveDeviceIfNeeded(); 249 } 250 // Dispatch handleOnProfileStateChanged to local broadcast profile 251 if (Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice() 252 && broadcast != null 253 && state == BluetoothAdapter.STATE_CONNECTED) { 254 Log.d(TAG, "dispatchProfileConnectionStateChanged to local broadcast profile"); 255 var unused = ThreadUtils.postOnBackgroundThread( 256 () -> broadcast.handleProfileConnected(device, bluetoothProfile, mBtManager)); 257 } 258 } 259 dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state)260 private void dispatchConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { 261 for (BluetoothCallback callback : mCallbacks) { 262 callback.onConnectionStateChanged(cachedDevice, state); 263 } 264 } 265 dispatchAudioModeChanged()266 private void dispatchAudioModeChanged() { 267 for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) { 268 cachedDevice.onAudioModeChanged(); 269 } 270 for (BluetoothCallback callback : mCallbacks) { 271 callback.onAudioModeChanged(); 272 } 273 } 274 275 @VisibleForTesting dispatchActiveDeviceChanged( @ullable CachedBluetoothDevice activeDevice, int bluetoothProfile)276 void dispatchActiveDeviceChanged( 277 @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) { 278 CachedBluetoothDevice mainActiveDevice = activeDevice; 279 for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) { 280 CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); 281 Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice(); 282 final Set<CachedBluetoothDevice> cachedDevices = new ArraySet<>(); 283 cachedDevices.add(cachedDevice); 284 if (!memberDevices.isEmpty()) { 285 cachedDevices.addAll(memberDevices); 286 } else if (subDevice != null) { 287 cachedDevices.add(subDevice); 288 } 289 290 // should report isActive from main device or it will cause trouble to other callers. 291 if (activeDevice != null 292 && (cachedDevices.stream().anyMatch( 293 device -> device.equals(activeDevice)))) { 294 Log.d(TAG, "The active device is in the set, report main device as active device:" 295 + cachedDevice.getDevice() + ", active device:" + activeDevice.getDevice()); 296 mainActiveDevice = cachedDevice; 297 } 298 boolean isActiveDevice = cachedDevice.equals(mainActiveDevice); 299 cachedDevices.forEach( 300 device -> device.onActiveDeviceChanged(isActiveDevice, bluetoothProfile)); 301 //TODO: b/400440223 - Check if we can call DeviceManager.onActiveDeviceChanged & 302 // Callback.onActiveDeviceChanged for cachedDevices Set also, so we don't need to report 303 // isActive from main device. 304 mDeviceManager.onActiveDeviceChanged(cachedDevice); 305 } 306 307 for (BluetoothCallback callback : mCallbacks) { 308 callback.onActiveDeviceChanged(mainActiveDevice, bluetoothProfile); 309 } 310 } 311 dispatchAclStateChanged(@onNull CachedBluetoothDevice activeDevice, int state)312 private void dispatchAclStateChanged(@NonNull CachedBluetoothDevice activeDevice, int state) { 313 for (BluetoothCallback callback : mCallbacks) { 314 callback.onAclConnectionStateChanged(activeDevice, state); 315 } 316 } 317 318 @VisibleForTesting addHandler(String action, Handler handler)319 void addHandler(String action, Handler handler) { 320 mHandlerMap.put(action, handler); 321 mAdapterIntentFilter.addAction(action); 322 } 323 324 private class BluetoothBroadcastReceiver extends BroadcastReceiver { 325 @Override onReceive(Context context, Intent intent)326 public void onReceive(Context context, Intent intent) { 327 String action = intent.getAction(); 328 BluetoothDevice device = intent 329 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 330 331 Handler handler = mHandlerMap.get(action); 332 if (handler != null) { 333 handler.onReceive(context, intent, device); 334 } 335 } 336 } 337 338 private class AdapterStateChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)339 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 340 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 341 BluetoothAdapter.ERROR); 342 // update local profiles and get paired devices 343 mLocalAdapter.setBluetoothStateInt(state); 344 // send callback to update UI and possibly start scanning 345 for (BluetoothCallback callback : mCallbacks) { 346 callback.onBluetoothStateChanged(state); 347 } 348 // Inform CachedDeviceManager that the adapter state has changed 349 mDeviceManager.onBluetoothStateChanged(state); 350 } 351 } 352 353 private class ScanningStateChangedHandler implements Handler { 354 private final boolean mStarted; 355 ScanningStateChangedHandler(boolean started)356 ScanningStateChangedHandler(boolean started) { 357 mStarted = started; 358 } 359 onReceive(Context context, Intent intent, BluetoothDevice device)360 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 361 for (BluetoothCallback callback : mCallbacks) { 362 callback.onScanningStateChanged(mStarted); 363 } 364 mDeviceManager.onScanningStateChanged(mStarted); 365 } 366 } 367 368 private class DeviceFoundHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)369 public void onReceive(Context context, Intent intent, 370 BluetoothDevice device) { 371 short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); 372 String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); 373 final boolean isCoordinatedSetMember = 374 intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, false); 375 // TODO Pick up UUID. They should be available for 2.1 devices. 376 // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1. 377 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 378 if (cachedDevice == null) { 379 cachedDevice = mDeviceManager.addDevice(device); 380 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice " 381 + cachedDevice.getDevice().getAnonymizedAddress()); 382 } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED 383 && !cachedDevice.getDevice().isConnected()) { 384 // Dispatch device add callback to show bonded but 385 // not connected devices in discovery mode 386 dispatchDeviceAdded(cachedDevice); 387 } 388 cachedDevice.setRssi(rssi); 389 cachedDevice.setJustDiscovered(true); 390 cachedDevice.setIsCoordinatedSetMember(isCoordinatedSetMember); 391 } 392 } 393 394 private class ConnectionStateChangedHandler implements Handler { 395 @Override onReceive(Context context, Intent intent, BluetoothDevice device)396 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 397 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 398 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 399 BluetoothAdapter.ERROR); 400 dispatchConnectionStateChanged(cachedDevice, state); 401 } 402 } 403 404 private class NameChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)405 public void onReceive(Context context, Intent intent, 406 BluetoothDevice device) { 407 mDeviceManager.onDeviceNameUpdated(device); 408 } 409 } 410 411 private class BondStateChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)412 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 413 if (device == null) { 414 Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 415 return; 416 } 417 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 418 BluetoothDevice.ERROR); 419 420 if (mDeviceManager.onBondStateChangedIfProcess(device, bondState)) { 421 Log.d(TAG, "Should not update UI for the set member"); 422 return; 423 } 424 425 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 426 if (cachedDevice == null) { 427 Log.w(TAG, "Got bonding state changed for " + device + 428 ", but we have no record of that device."); 429 cachedDevice = mDeviceManager.addDevice(device); 430 } 431 432 if (bondState == BluetoothDevice.BOND_BONDED) { 433 mDeviceManager.removeDuplicateInstanceForIdentityAddress(device); 434 } 435 436 for (BluetoothCallback callback : mCallbacks) { 437 callback.onDeviceBondStateChanged(cachedDevice, bondState); 438 } 439 cachedDevice.onBondingStateChanged(bondState); 440 441 if (bondState == BluetoothDevice.BOND_NONE) { 442 // Check if we need to remove other Coordinated set member devices / Hearing Aid 443 // devices 444 if (DEBUG) { 445 Log.d(TAG, "BondStateChangedHandler: cachedDevice.getGroupId() = " 446 + cachedDevice.getGroupId() + ", cachedDevice.getHiSyncId()= " 447 + cachedDevice.getHiSyncId()); 448 } 449 if (cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID 450 || cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) { 451 Log.d(TAG, "BondStateChangedHandler: Start onDeviceUnpaired"); 452 mDeviceManager.onDeviceUnpaired(cachedDevice); 453 } 454 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, 455 BluetoothDevice.ERROR); 456 457 showUnbondMessage(context, cachedDevice.getName(), reason); 458 } 459 } 460 461 /** 462 * Called when we have reached the unbonded state. 463 * 464 * @param reason one of the error reasons from 465 * BluetoothDevice.UNBOND_REASON_* 466 */ showUnbondMessage(Context context, String name, int reason)467 private void showUnbondMessage(Context context, String name, int reason) { 468 if (DEBUG) { 469 Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason); 470 } 471 int errorMsg; 472 473 switch (reason) { 474 case BluetoothDevice.UNBOND_REASON_AUTH_FAILED: 475 errorMsg = R.string.bluetooth_pairing_pin_error_message; 476 break; 477 case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED: 478 errorMsg = R.string.bluetooth_pairing_rejected_error_message; 479 break; 480 case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN: 481 errorMsg = R.string.bluetooth_pairing_device_down_error_message; 482 break; 483 case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS: 484 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: 485 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: 486 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: 487 errorMsg = R.string.bluetooth_pairing_error_message; 488 break; 489 default: 490 Log.w(TAG, 491 "showUnbondMessage: Not displaying any message for reason: " + reason); 492 return; 493 } 494 BluetoothUtils.showError(context, name, errorMsg); 495 } 496 } 497 498 private class ClassChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)499 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 500 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 501 if (cachedDevice != null) { 502 cachedDevice.refresh(); 503 } 504 } 505 } 506 507 private class UuidChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)508 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 509 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 510 if (cachedDevice != null) { 511 cachedDevice.onUuidChanged(); 512 } 513 } 514 } 515 516 private class BatteryLevelChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)517 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 518 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 519 if (cachedDevice != null) { 520 cachedDevice.refresh(); 521 } 522 } 523 } 524 525 private class ActiveDeviceChangedHandler implements Handler { 526 @Override onReceive(Context context, Intent intent, BluetoothDevice device)527 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 528 String action = intent.getAction(); 529 if (action == null) { 530 Log.w(TAG, "ActiveDeviceChangedHandler: action is null"); 531 return; 532 } 533 @Nullable 534 CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); 535 int bluetoothProfile = 0; 536 if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) { 537 bluetoothProfile = BluetoothProfile.A2DP; 538 } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { 539 bluetoothProfile = BluetoothProfile.HEADSET; 540 } else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) { 541 bluetoothProfile = BluetoothProfile.HEARING_AID; 542 } else if (Objects.equals(action, 543 BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED)) { 544 bluetoothProfile = BluetoothProfile.LE_AUDIO; 545 } else { 546 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); 547 return; 548 } 549 dispatchActiveDeviceChanged(activeDevice, bluetoothProfile); 550 } 551 } 552 553 private class AclStateChangedHandler implements Handler { 554 @Override onReceive(Context context, Intent intent, BluetoothDevice device)555 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 556 if (device == null) { 557 Log.w(TAG, "AclStateChangedHandler: device is null"); 558 return; 559 } 560 561 // Avoid to notify Settings UI for Hearing Aid sub device. 562 if (mDeviceManager.isSubDevice(device)) { 563 return; 564 } 565 566 final String action = intent.getAction(); 567 if (action == null) { 568 Log.w(TAG, "AclStateChangedHandler: action is null"); 569 return; 570 } 571 final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); 572 if (activeDevice == null) { 573 Log.w(TAG, "AclStateChangedHandler: activeDevice is null"); 574 return; 575 } 576 final int state; 577 switch (action) { 578 case BluetoothDevice.ACTION_ACL_CONNECTED: 579 state = BluetoothAdapter.STATE_CONNECTED; 580 break; 581 case BluetoothDevice.ACTION_ACL_DISCONNECTED: 582 state = BluetoothAdapter.STATE_DISCONNECTED; 583 break; 584 default: 585 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); 586 return; 587 } 588 dispatchAclStateChanged(activeDevice, state); 589 } 590 } 591 592 private class AudioModeChangedHandler implements Handler { 593 594 @Override onReceive(Context context, Intent intent, BluetoothDevice device)595 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 596 final String action = intent.getAction(); 597 if (action == null) { 598 Log.w(TAG, "AudioModeChangedHandler() action is null"); 599 return; 600 } 601 dispatchAudioModeChanged(); 602 } 603 } 604 605 private class AutoOnStateChangedHandler implements Handler { 606 607 @Override onReceive(Context context, Intent intent, BluetoothDevice device)608 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 609 String action = intent.getAction(); 610 if (action == null) { 611 Log.w(TAG, "AutoOnStateChangedHandler() action is null"); 612 return; 613 } 614 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_AUTO_ON_STATE, 615 BluetoothAdapter.ERROR); 616 for (BluetoothCallback callback : mCallbacks) { 617 callback.onAutoOnStateChanged(state); 618 } 619 } 620 } 621 } 622