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