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