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 } 254 for (BluetoothCallback callback : mCallbacks) { 255 callback.onActiveDeviceChanged(activeDevice, bluetoothProfile); 256 } 257 } 258 dispatchAclStateChanged(@onNull CachedBluetoothDevice activeDevice, int state)259 private void dispatchAclStateChanged(@NonNull CachedBluetoothDevice activeDevice, int state) { 260 for (BluetoothCallback callback : mCallbacks) { 261 callback.onAclConnectionStateChanged(activeDevice, state); 262 } 263 } 264 265 @VisibleForTesting addHandler(String action, Handler handler)266 void addHandler(String action, Handler handler) { 267 mHandlerMap.put(action, handler); 268 mAdapterIntentFilter.addAction(action); 269 } 270 271 private class BluetoothBroadcastReceiver extends BroadcastReceiver { 272 @Override onReceive(Context context, Intent intent)273 public void onReceive(Context context, Intent intent) { 274 String action = intent.getAction(); 275 BluetoothDevice device = intent 276 .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 277 278 Handler handler = mHandlerMap.get(action); 279 if (handler != null) { 280 handler.onReceive(context, intent, device); 281 } 282 } 283 } 284 285 private class AdapterStateChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)286 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 287 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 288 BluetoothAdapter.ERROR); 289 // update local profiles and get paired devices 290 mLocalAdapter.setBluetoothStateInt(state); 291 // send callback to update UI and possibly start scanning 292 for (BluetoothCallback callback : mCallbacks) { 293 callback.onBluetoothStateChanged(state); 294 } 295 // Inform CachedDeviceManager that the adapter state has changed 296 mDeviceManager.onBluetoothStateChanged(state); 297 } 298 } 299 300 private class ScanningStateChangedHandler implements Handler { 301 private final boolean mStarted; 302 ScanningStateChangedHandler(boolean started)303 ScanningStateChangedHandler(boolean started) { 304 mStarted = started; 305 } 306 onReceive(Context context, Intent intent, BluetoothDevice device)307 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 308 for (BluetoothCallback callback : mCallbacks) { 309 callback.onScanningStateChanged(mStarted); 310 } 311 mDeviceManager.onScanningStateChanged(mStarted); 312 } 313 } 314 315 private class DeviceFoundHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)316 public void onReceive(Context context, Intent intent, 317 BluetoothDevice device) { 318 short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); 319 String name = intent.getStringExtra(BluetoothDevice.EXTRA_NAME); 320 final boolean isCoordinatedSetMember = 321 intent.getBooleanExtra(BluetoothDevice.EXTRA_IS_COORDINATED_SET_MEMBER, false); 322 // TODO Pick up UUID. They should be available for 2.1 devices. 323 // Skip for now, there's a bluez problem and we are not getting uuids even for 2.1. 324 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 325 if (cachedDevice == null) { 326 cachedDevice = mDeviceManager.addDevice(device); 327 Log.d(TAG, "DeviceFoundHandler created new CachedBluetoothDevice " 328 + cachedDevice.getDevice().getAnonymizedAddress()); 329 } else if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED 330 && !cachedDevice.getDevice().isConnected()) { 331 // Dispatch device add callback to show bonded but 332 // not connected devices in discovery mode 333 dispatchDeviceAdded(cachedDevice); 334 } 335 cachedDevice.setRssi(rssi); 336 cachedDevice.setJustDiscovered(true); 337 cachedDevice.setIsCoordinatedSetMember(isCoordinatedSetMember); 338 } 339 } 340 341 private class ConnectionStateChangedHandler implements Handler { 342 @Override onReceive(Context context, Intent intent, BluetoothDevice device)343 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 344 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 345 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 346 BluetoothAdapter.ERROR); 347 dispatchConnectionStateChanged(cachedDevice, state); 348 } 349 } 350 351 private class NameChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)352 public void onReceive(Context context, Intent intent, 353 BluetoothDevice device) { 354 mDeviceManager.onDeviceNameUpdated(device); 355 } 356 } 357 358 private class BondStateChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)359 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 360 if (device == null) { 361 Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 362 return; 363 } 364 int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 365 BluetoothDevice.ERROR); 366 367 if (mDeviceManager.onBondStateChangedIfProcess(device, bondState)) { 368 Log.d(TAG, "Should not update UI for the set member"); 369 return; 370 } 371 372 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 373 if (cachedDevice == null) { 374 Log.w(TAG, "Got bonding state changed for " + device + 375 ", but we have no record of that device."); 376 cachedDevice = mDeviceManager.addDevice(device); 377 } 378 379 for (BluetoothCallback callback : mCallbacks) { 380 callback.onDeviceBondStateChanged(cachedDevice, bondState); 381 } 382 cachedDevice.onBondingStateChanged(bondState); 383 384 if (bondState == BluetoothDevice.BOND_NONE) { 385 // Check if we need to remove other Coordinated set member devices / Hearing Aid 386 // devices 387 if (DEBUG) { 388 Log.d(TAG, "BondStateChangedHandler: cachedDevice.getGroupId() = " 389 + cachedDevice.getGroupId() + ", cachedDevice.getHiSyncId()= " 390 + cachedDevice.getHiSyncId()); 391 } 392 if (cachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID 393 || cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) { 394 Log.d(TAG, "BondStateChangedHandler: Start onDeviceUnpaired"); 395 mDeviceManager.onDeviceUnpaired(cachedDevice); 396 } 397 int reason = intent.getIntExtra(BluetoothDevice.EXTRA_UNBOND_REASON, 398 BluetoothDevice.ERROR); 399 400 showUnbondMessage(context, cachedDevice.getName(), reason); 401 } 402 } 403 404 /** 405 * Called when we have reached the unbonded state. 406 * 407 * @param reason one of the error reasons from 408 * BluetoothDevice.UNBOND_REASON_* 409 */ showUnbondMessage(Context context, String name, int reason)410 private void showUnbondMessage(Context context, String name, int reason) { 411 if (DEBUG) { 412 Log.d(TAG, "showUnbondMessage() name : " + name + ", reason : " + reason); 413 } 414 int errorMsg; 415 416 switch (reason) { 417 case BluetoothDevice.UNBOND_REASON_AUTH_FAILED: 418 errorMsg = R.string.bluetooth_pairing_pin_error_message; 419 break; 420 case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED: 421 errorMsg = R.string.bluetooth_pairing_rejected_error_message; 422 break; 423 case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN: 424 errorMsg = R.string.bluetooth_pairing_device_down_error_message; 425 break; 426 case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS: 427 case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT: 428 case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS: 429 case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED: 430 errorMsg = R.string.bluetooth_pairing_error_message; 431 break; 432 default: 433 Log.w(TAG, 434 "showUnbondMessage: Not displaying any message for reason: " + reason); 435 return; 436 } 437 BluetoothUtils.showError(context, name, errorMsg); 438 } 439 } 440 441 private class ClassChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)442 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 443 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 444 if (cachedDevice != null) { 445 cachedDevice.refresh(); 446 } 447 } 448 } 449 450 private class UuidChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)451 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 452 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 453 if (cachedDevice != null) { 454 cachedDevice.onUuidChanged(); 455 } 456 } 457 } 458 459 private class BatteryLevelChangedHandler implements Handler { onReceive(Context context, Intent intent, BluetoothDevice device)460 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 461 CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device); 462 if (cachedDevice != null) { 463 cachedDevice.refresh(); 464 } 465 } 466 } 467 468 private class ActiveDeviceChangedHandler implements Handler { 469 @Override onReceive(Context context, Intent intent, BluetoothDevice device)470 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 471 String action = intent.getAction(); 472 if (action == null) { 473 Log.w(TAG, "ActiveDeviceChangedHandler: action is null"); 474 return; 475 } 476 @Nullable 477 CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); 478 int bluetoothProfile = 0; 479 if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) { 480 bluetoothProfile = BluetoothProfile.A2DP; 481 } else if (Objects.equals(action, BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { 482 bluetoothProfile = BluetoothProfile.HEADSET; 483 } else if (Objects.equals(action, BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED)) { 484 bluetoothProfile = BluetoothProfile.HEARING_AID; 485 } else if (Objects.equals(action, 486 BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED)) { 487 bluetoothProfile = BluetoothProfile.LE_AUDIO; 488 } else { 489 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); 490 return; 491 } 492 dispatchActiveDeviceChanged(activeDevice, bluetoothProfile); 493 } 494 } 495 496 private class AclStateChangedHandler implements Handler { 497 @Override onReceive(Context context, Intent intent, BluetoothDevice device)498 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 499 if (device == null) { 500 Log.w(TAG, "AclStateChangedHandler: device is null"); 501 return; 502 } 503 504 // Avoid to notify Settings UI for Hearing Aid sub device. 505 if (mDeviceManager.isSubDevice(device)) { 506 return; 507 } 508 509 final String action = intent.getAction(); 510 if (action == null) { 511 Log.w(TAG, "AclStateChangedHandler: action is null"); 512 return; 513 } 514 final CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device); 515 if (activeDevice == null) { 516 Log.w(TAG, "AclStateChangedHandler: activeDevice is null"); 517 return; 518 } 519 final int state; 520 switch (action) { 521 case BluetoothDevice.ACTION_ACL_CONNECTED: 522 state = BluetoothAdapter.STATE_CONNECTED; 523 break; 524 case BluetoothDevice.ACTION_ACL_DISCONNECTED: 525 state = BluetoothAdapter.STATE_DISCONNECTED; 526 break; 527 default: 528 Log.w(TAG, "ActiveDeviceChangedHandler: unknown action " + action); 529 return; 530 531 } 532 dispatchAclStateChanged(activeDevice, state); 533 } 534 } 535 536 private class AudioModeChangedHandler implements Handler { 537 538 @Override onReceive(Context context, Intent intent, BluetoothDevice device)539 public void onReceive(Context context, Intent intent, BluetoothDevice device) { 540 final String action = intent.getAction(); 541 if (action == null) { 542 Log.w(TAG, "AudioModeChangedHandler() action is null"); 543 return; 544 } 545 dispatchAudioModeChanged(); 546 } 547 } 548 } 549