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