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