• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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