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