1 /*
2  * Copyright 2019 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 package com.android.server.audio;
17 
18 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_DEFAULT;
19 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET;
20 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_WATCH;
21 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_CARKIT;
22 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES;
23 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID;
24 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_RECEIVER;
25 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER;
26 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN;
27 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH;
28 import static android.media.audio.Flags.automaticBtDeviceType;
29 
30 import android.annotation.NonNull;
31 import android.annotation.Nullable;
32 import android.bluetooth.BluetoothA2dp;
33 import android.bluetooth.BluetoothAdapter;
34 import android.bluetooth.BluetoothClass;
35 import android.bluetooth.BluetoothCodecConfig;
36 import android.bluetooth.BluetoothCodecStatus;
37 import android.bluetooth.BluetoothDevice;
38 import android.bluetooth.BluetoothHeadset;
39 import android.bluetooth.BluetoothHearingAid;
40 import android.bluetooth.BluetoothLeAudio;
41 import android.bluetooth.BluetoothLeAudioCodecConfig;
42 import android.bluetooth.BluetoothLeAudioCodecStatus;
43 import android.bluetooth.BluetoothProfile;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.media.AudioDeviceAttributes;
47 import android.media.AudioManager;
48 import android.media.AudioManager.AudioDeviceCategory;
49 import android.media.AudioSystem;
50 import android.media.BluetoothProfileConnectionInfo;
51 import android.os.Binder;
52 import android.os.Bundle;
53 import android.os.UserHandle;
54 import android.provider.Settings;
55 import android.text.TextUtils;
56 import android.util.Log;
57 import android.util.Pair;
58 
59 import com.android.internal.annotations.GuardedBy;
60 import com.android.server.utils.EventLogger;
61 
62 import java.io.PrintWriter;
63 import java.util.ArrayList;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.Map;
67 import java.util.Objects;
68 
69 /**
70  * @hide
71  * Class to encapsulate all communication with Bluetooth services
72  */
73 public class BtHelper {
74 
75     private static final String TAG = "AS.BtHelper";
76 
77     private final @NonNull AudioDeviceBroker mDeviceBroker;
78     private final @NonNull Context mContext;
79 
BtHelper(@onNull AudioDeviceBroker broker, Context context)80     BtHelper(@NonNull AudioDeviceBroker broker, Context context) {
81         mDeviceBroker = broker;
82         mContext = context;
83     }
84 
85     // BluetoothHeadset API to control SCO connection
86     private @Nullable BluetoothHeadset mBluetoothHeadset;
87 
88     // Bluetooth headset device
89     private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
90     private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices =
91             new HashMap<>();
92 
93     private @Nullable BluetoothHearingAid mHearingAid = null;
94 
95     private @Nullable BluetoothLeAudio mLeAudio = null;
96 
97     private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig;
98 
99     // Reference to BluetoothA2dp to query for AbsoluteVolume.
100     private @Nullable BluetoothA2dp mA2dp = null;
101 
102     private @Nullable BluetoothCodecConfig mA2dpCodecConfig;
103 
104     private @AudioSystem.AudioFormatNativeEnumForBtCodec
105             int mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
106 
107     // If absolute volume is supported in AVRCP device
108     private boolean mAvrcpAbsVolSupported = false;
109 
110     // Current connection state indicated by bluetooth headset
111     private int mScoConnectionState;
112 
113     // Indicate if SCO audio connection is currently active and if the initiator is
114     // audio service (internal) or bluetooth headset (external)
115     private int mScoAudioState;
116 
117     // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
118     // originated from an app targeting an API version before JB MR2 and raw audio after that.
119     private int mScoAudioMode;
120 
121     // SCO audio state is not active
122     private static final int SCO_STATE_INACTIVE = 0;
123     // SCO audio activation request waiting for headset service to connect
124     private static final int SCO_STATE_ACTIVATE_REQ = 1;
125     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
126     // in call audio)
127     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
128     // SCO audio state is active or starting due to a request from AudioManager API
129     private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
130     // SCO audio deactivation request waiting for headset service to connect
131     private static final int SCO_STATE_DEACTIVATE_REQ = 4;
132     // SCO audio deactivation in progress, waiting for Bluetooth audio intent
133     private static final int SCO_STATE_DEACTIVATING = 5;
134 
135     // SCO audio mode is undefined
136     /*package*/  static final int SCO_MODE_UNDEFINED = -1;
137     // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
138     /*package*/  static final int SCO_MODE_VIRTUAL_CALL = 0;
139     // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
140     private static final int SCO_MODE_VR = 2;
141     // max valid SCO audio mode values
142     private static final int SCO_MODE_MAX = 2;
143 
144     private static final int BT_HEARING_AID_GAIN_MIN = -128;
145     private static final int BT_LE_AUDIO_MIN_VOL = 0;
146     private static final int BT_LE_AUDIO_MAX_VOL = 255;
147 
148     // BtDevice constants currently rolling out under flag protection. Use own
149     // constants instead to avoid mainline dependency from flag library import
150     // TODO(b/335936458): remove once the BtDevice flag is rolled out
151     private static final String DEVICE_TYPE_SPEAKER = "Speaker";
152     private static final String DEVICE_TYPE_HEADSET = "Headset";
153     private static final String DEVICE_TYPE_CARKIT = "Carkit";
154     private static final String DEVICE_TYPE_HEARING_AID = "HearingAid";
155 
156     /**
157      * Returns a string representation of the scoAudioMode.
158      */
scoAudioModeToString(int scoAudioMode)159     public static String scoAudioModeToString(int scoAudioMode) {
160         switch (scoAudioMode) {
161             case SCO_MODE_UNDEFINED:
162                 return "SCO_MODE_UNDEFINED";
163             case SCO_MODE_VIRTUAL_CALL:
164                 return "SCO_MODE_VIRTUAL_CALL";
165             case SCO_MODE_VR:
166                 return "SCO_MODE_VR";
167             default:
168                 return "SCO_MODE_(" + scoAudioMode + ")";
169         }
170     }
171 
172     /**
173      * Returns a string representation of the scoAudioState.
174      */
scoAudioStateToString(int scoAudioState)175     public static String scoAudioStateToString(int scoAudioState) {
176         switch (scoAudioState) {
177             case SCO_STATE_INACTIVE:
178                 return "SCO_STATE_INACTIVE";
179             case SCO_STATE_ACTIVATE_REQ:
180                 return "SCO_STATE_ACTIVATE_REQ";
181             case SCO_STATE_ACTIVE_EXTERNAL:
182                 return "SCO_STATE_ACTIVE_EXTERNAL";
183             case SCO_STATE_ACTIVE_INTERNAL:
184                 return "SCO_STATE_ACTIVE_INTERNAL";
185             case SCO_STATE_DEACTIVATING:
186                 return "SCO_STATE_DEACTIVATING";
187             default:
188                 return "SCO_STATE_(" + scoAudioState + ")";
189         }
190     }
191 
192     // A2DP device events
193     /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0;
194 
deviceEventToString(int event)195     /*package*/ static String deviceEventToString(int event) {
196         switch (event) {
197             case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
198             default:
199                 return new String("invalid event:" + event);
200         }
201     }
202 
getName(@onNull BluetoothDevice device)203     /*package*/ @NonNull static String getName(@NonNull BluetoothDevice device) {
204         final String deviceName = device.getName();
205         if (deviceName == null) {
206             return "";
207         }
208         return deviceName;
209     }
210 
211     //----------------------------------------------------------------------
212     // Interface for AudioDeviceBroker
213 
214     // @GuardedBy("mDeviceBroker.mSetModeLock")
215     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onSystemReady()216     /*package*/ synchronized void onSystemReady() {
217         mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
218         resetBluetoothSco();
219         getBluetoothHeadset();
220 
221         //FIXME: this is to maintain compatibility with deprecated intent
222         // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
223         Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
224         newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
225                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
226         sendStickyBroadcastToAll(newIntent);
227 
228         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
229         if (adapter != null) {
230             adapter.getProfileProxy(mDeviceBroker.getContext(),
231                     mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
232             adapter.getProfileProxy(mDeviceBroker.getContext(),
233                     mBluetoothProfileServiceListener, BluetoothProfile.A2DP_SINK);
234             adapter.getProfileProxy(mDeviceBroker.getContext(),
235                     mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
236             adapter.getProfileProxy(mDeviceBroker.getContext(),
237                     mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO);
238             adapter.getProfileProxy(mDeviceBroker.getContext(),
239                     mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
240         }
241     }
242 
onAudioServerDiedRestoreA2dp()243     /*package*/ synchronized void onAudioServerDiedRestoreA2dp() {
244         final int forMed = mDeviceBroker.getBluetoothA2dpEnabled()
245                 ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP;
246         mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()");
247     }
248 
setAvrcpAbsoluteVolumeSupported(boolean supported)249     /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) {
250         mAvrcpAbsVolSupported = supported;
251         Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported);
252     }
253 
setAvrcpAbsoluteVolumeIndex(int index)254     /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
255         if (mA2dp == null) {
256             if (AudioService.DEBUG_VOL) {
257                 AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent(
258                         "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG));
259             }
260             return;
261         }
262         if (!mAvrcpAbsVolSupported) {
263             AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent(
264                     "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG));
265             return;
266         }
267         if (AudioService.DEBUG_VOL) {
268             Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
269         }
270         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
271                 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
272         try {
273             mA2dp.setAvrcpAbsoluteVolume(index);
274         } catch (Exception e) {
275             Log.e(TAG, "Exception while changing abs volume", e);
276         }
277     }
278 
getCodec( @onNull BluetoothDevice device, @AudioService.BtProfile int profile)279     private synchronized Pair<Integer, Boolean> getCodec(
280             @NonNull BluetoothDevice device, @AudioService.BtProfile int profile) {
281 
282         switch (profile) {
283             case BluetoothProfile.A2DP: {
284                 boolean changed = mA2dpCodecConfig != null;
285                 if (mA2dp == null) {
286                     mA2dpCodecConfig = null;
287                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
288                 }
289                 BluetoothCodecStatus btCodecStatus = null;
290                 try {
291                     btCodecStatus = mA2dp.getCodecStatus(device);
292                 } catch (Exception e) {
293                     Log.e(TAG, "Exception while getting status of " + device, e);
294                 }
295                 if (btCodecStatus == null) {
296                     Log.e(TAG, "getCodec, null A2DP codec status for device: " + device);
297                     mA2dpCodecConfig = null;
298                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
299                 }
300                 final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
301                 if (btCodecConfig == null) {
302                     mA2dpCodecConfig = null;
303                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
304                 }
305                 changed = !btCodecConfig.equals(mA2dpCodecConfig);
306                 mA2dpCodecConfig = btCodecConfig;
307                 return new Pair<>(AudioSystem.bluetoothA2dpCodecToAudioFormat(
308                         btCodecConfig.getCodecType()), changed);
309             }
310             case BluetoothProfile.LE_AUDIO: {
311                 boolean changed = mLeAudioCodecConfig != null;
312                 if (mLeAudio == null) {
313                     mLeAudioCodecConfig = null;
314                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
315                 }
316                 BluetoothLeAudioCodecStatus btLeCodecStatus = null;
317                 int groupId = mLeAudio.getGroupId(device);
318                 try {
319                     btLeCodecStatus = mLeAudio.getCodecStatus(groupId);
320                 } catch (Exception e) {
321                     Log.e(TAG, "Exception while getting status of " + device, e);
322                 }
323                 if (btLeCodecStatus == null) {
324                     Log.e(TAG, "getCodec, null LE codec status for device: " + device);
325                     mLeAudioCodecConfig = null;
326                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
327                 }
328                 BluetoothLeAudioCodecConfig btLeCodecConfig =
329                         btLeCodecStatus.getOutputCodecConfig();
330                 if (btLeCodecConfig == null) {
331                     mLeAudioCodecConfig = null;
332                     return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed);
333                 }
334                 changed = !btLeCodecConfig.equals(mLeAudioCodecConfig);
335                 mLeAudioCodecConfig = btLeCodecConfig;
336                 return new Pair<>(AudioSystem.bluetoothLeCodecToAudioFormat(
337                         btLeCodecConfig.getCodecType()), changed);
338             }
339             case BluetoothProfile.LE_AUDIO_BROADCAST: {
340                 // We assume LC3 for LE Audio broadcast codec as there is no API to get the codec
341                 // config on LE Broadcast profile proxy.
342                 boolean changed = mLeAudioBroadcastCodec != AudioSystem.AUDIO_FORMAT_LC3;
343                 mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_LC3;
344                 return new Pair<>(mLeAudioBroadcastCodec, changed);
345             }
346             default:
347                 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false);
348         }
349     }
350 
351     /*package*/ synchronized Pair<Integer, Boolean>
getCodecWithFallback(@onNull BluetoothDevice device, @AudioService.BtProfile int profile, boolean isLeOutput, @NonNull String source)352                     getCodecWithFallback(@NonNull BluetoothDevice device,
353                                          @AudioService.BtProfile int profile,
354                                          boolean isLeOutput, @NonNull String source) {
355         // For profiles other than A2DP and LE Audio output, the audio codec format must be
356         // AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format
357         // only if audio HW module selection based on format is supported for the device type.
358         if (!(profile == BluetoothProfile.A2DP
359                 || (isLeOutput && ((profile == BluetoothProfile.LE_AUDIO)
360                         || (profile == BluetoothProfile.LE_AUDIO_BROADCAST))))) {
361             return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false);
362         }
363         Pair<Integer, Boolean> codecAndChanged =
364                 getCodec(device, profile);
365         if (codecAndChanged.first == AudioSystem.AUDIO_FORMAT_DEFAULT) {
366             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
367                     "getCodec DEFAULT from " + source + " fallback to "
368                             + (profile == BluetoothProfile.A2DP ? "SBC" : "LC3")));
369             return new Pair<>(profile == BluetoothProfile.A2DP
370                     ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3, true);
371         }
372 
373         return codecAndChanged;
374     }
375 
376     // @GuardedBy("mDeviceBroker.mSetModeLock")
377     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onReceiveBtEvent(Intent intent)378     /*package*/ synchronized void onReceiveBtEvent(Intent intent) {
379         final String action = intent.getAction();
380 
381         Log.i(TAG, "onReceiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState);
382         if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
383             BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE,
384                     android.bluetooth.BluetoothDevice.class);
385             if (btDevice != null && !isProfilePoxyConnected(BluetoothProfile.HEADSET)) {
386                 AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
387                         "onReceiveBtEvent ACTION_ACTIVE_DEVICE_CHANGED "
388                                 + "received with null profile proxy for device: "
389                                 + btDevice)).printLog(TAG));
390                 return;
391             }
392             onSetBtScoActiveDevice(btDevice);
393         } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
394             int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
395             onScoAudioStateChanged(btState);
396         }
397     }
398 
399     /**
400      * Exclusively called from AudioDeviceBroker when handling MSG_L_RECEIVED_BT_EVENT
401      * as part of the serialization of the communication route selection
402      */
403     // @GuardedBy("mDeviceBroker.mSetModeLock")
404     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onScoAudioStateChanged(int state)405     private void onScoAudioStateChanged(int state) {
406         boolean broadcast = false;
407         int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
408         if (mDeviceBroker.isScoManagedByAudio()) {
409             switch (state) {
410                 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
411                     mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
412                     scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
413                     broadcast = true;
414                     break;
415                 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
416                     mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
417                     scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
418                     broadcast = true;
419                     break;
420                 default:
421                     break;
422             }
423         } else {
424             switch (state) {
425                 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
426                     scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
427                     if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
428                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
429                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
430                     } else if (mDeviceBroker.isBluetoothScoRequested()) {
431                         // broadcast intent if the connection was initated by AudioService
432                         broadcast = true;
433                     }
434                     mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
435                     break;
436                 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
437                     mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
438                     scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
439                     // There are two cases where we want to immediately reconnect audio:
440                     // 1) If a new start request was received while disconnecting: this was
441                     // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
442                     // 2) If audio was connected then disconnected via Bluetooth APIs and
443                     // we still have pending activation requests by apps: this is indicated by
444                     // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
445                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
446                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
447                                 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
448                                 mBluetoothHeadsetDevice, mScoAudioMode)) {
449                             mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
450                             scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
451                             broadcast = true;
452                             break;
453                         }
454                     }
455                     if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
456                         broadcast = true;
457                     }
458                     mScoAudioState = SCO_STATE_INACTIVE;
459                     break;
460                 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
461                     if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
462                             && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
463                         mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
464                     }
465                     break;
466                 default:
467                     break;
468             }
469         }
470         if (broadcast) {
471             broadcastScoConnectionState(scoAudioState);
472             //FIXME: this is to maintain compatibility with deprecated intent
473             // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
474             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
475             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
476             sendStickyBroadcastToAll(newIntent);
477         }
478     }
479     /**
480      *
481      * @return false if SCO isn't connected
482      */
isBluetoothScoOn()483     /*package*/ synchronized boolean isBluetoothScoOn() {
484         if (mBluetoothHeadset == null || mBluetoothHeadsetDevice == null) {
485             return false;
486         }
487         return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
488                 == BluetoothHeadset.STATE_AUDIO_CONNECTED;
489     }
490 
491     // @GuardedBy("mDeviceBroker.mSetModeLock")
492     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
startBluetoothSco(int scoAudioMode, @NonNull String eventSource)493     /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode,
494                 @NonNull String eventSource) {
495         AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
496         return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
497     }
498 
499     // @GuardedBy("mDeviceBroker.mSetModeLock")
500     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
stopBluetoothSco(@onNull String eventSource)501     /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) {
502         AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource));
503         return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL);
504     }
505 
setLeAudioVolume(int index, int maxIndex, int streamType)506     /*package*/ synchronized void setLeAudioVolume(int index, int maxIndex, int streamType) {
507         if (mLeAudio == null) {
508             if (AudioService.DEBUG_VOL) {
509                 Log.i(TAG, "setLeAudioVolume: null mLeAudio");
510             }
511             return;
512         }
513         /* leaudio expect volume value in range 0 to 255 */
514         int volume = (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex);
515 
516         if (AudioService.DEBUG_VOL) {
517             Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx="
518                     + index + " volume=" + volume);
519         }
520         AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
521                 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, streamType, index,
522                 maxIndex, /*caller=*/null));
523         try {
524             mLeAudio.setVolume(volume);
525         } catch (Exception e) {
526             Log.e(TAG, "Exception while setting LE volume", e);
527         }
528     }
529 
setHearingAidVolume(int index, int streamType, boolean isHeadAidConnected)530     /*package*/ synchronized void setHearingAidVolume(int index, int streamType,
531             boolean isHeadAidConnected) {
532         if (mHearingAid == null) {
533             if (AudioService.DEBUG_VOL) {
534                 Log.i(TAG, "setHearingAidVolume: null mHearingAid");
535             }
536             return;
537         }
538         //hearing aid expect volume value in range -128dB to 0dB
539         int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
540                 AudioSystem.DEVICE_OUT_HEARING_AID);
541         if (gainDB < BT_HEARING_AID_GAIN_MIN) {
542             gainDB = BT_HEARING_AID_GAIN_MIN;
543         }
544         if (AudioService.DEBUG_VOL) {
545             Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
546                     + index + " gain=" + gainDB);
547         }
548         // do not log when hearing aid is not connected to avoid confusion when reading dumpsys
549         if (isHeadAidConnected) {
550             AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent(
551                     AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
552         }
553         try {
554             mHearingAid.setVolume(gainDB);
555         } catch (Exception e) {
556             Log.i(TAG, "Exception while setting hearing aid volume", e);
557         }
558     }
559 
onBroadcastScoConnectionState(int state)560     /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
561         if (state == mScoConnectionState) {
562             return;
563         }
564         Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
565         newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
566         newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
567                 mScoConnectionState);
568         sendStickyBroadcastToAll(newIntent);
569         mScoConnectionState = state;
570     }
571 
572     // @GuardedBy("mDeviceBroker.mSetModeLock")
573     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
resetBluetoothSco()574     /*package*/ synchronized void resetBluetoothSco() {
575         mScoAudioState = SCO_STATE_INACTIVE;
576         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
577         mDeviceBroker.clearA2dpSuspended(false /* internalOnly */);
578         mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */);
579         mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
580     }
581 
582     // @GuardedBy("mDeviceBroker.mSetModeLock")
583     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onBtProfileDisconnected(int profile)584     /*package*/ synchronized void onBtProfileDisconnected(int profile) {
585         AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
586                 "BT profile " + BluetoothProfile.getProfileName(profile)
587                 + " disconnected").printLog(TAG));
588         switch (profile) {
589             case BluetoothProfile.HEADSET:
590                 mBluetoothHeadset = null;
591                 break;
592             case BluetoothProfile.A2DP:
593                 mA2dp = null;
594                 mA2dpCodecConfig = null;
595                 break;
596             case BluetoothProfile.HEARING_AID:
597                 mHearingAid = null;
598                 break;
599             case BluetoothProfile.LE_AUDIO:
600                 if (mLeAudio != null && mLeAudioCallback != null) {
601                     mLeAudio.unregisterCallback(mLeAudioCallback);
602                 }
603                 mLeAudio = null;
604                 mLeAudioCallback = null;
605                 mLeAudioCodecConfig = null;
606                 break;
607             case BluetoothProfile.LE_AUDIO_BROADCAST:
608                 mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT;
609                 break;
610             case BluetoothProfile.A2DP_SINK:
611                 // nothing to do in BtHelper
612                 break;
613             default:
614                 // Not a valid profile to disconnect
615                 Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect "
616                         + BluetoothProfile.getProfileName(profile));
617                 break;
618         }
619     }
620 
621     // BluetoothLeAudio callback used to update the list of addresses in the same group as a
622     // connected LE Audio device
623     class MyLeAudioCallback implements BluetoothLeAudio.Callback {
624         @Override
onCodecConfigChanged(int groupId, @NonNull BluetoothLeAudioCodecStatus status)625         public void onCodecConfigChanged(int groupId,
626                                   @NonNull BluetoothLeAudioCodecStatus status) {
627             // Do nothing
628         }
629 
630         @Override
onGroupNodeAdded(@onNull BluetoothDevice device, int groupId)631         public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) {
632             mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
633         }
634 
635         @Override
onGroupNodeRemoved(@onNull BluetoothDevice device, int groupId)636         public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) {
637             mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
638         }
639         @Override
onGroupStatusChanged(int groupId, int groupStatus)640         public void onGroupStatusChanged(int groupId, int groupStatus) {
641             mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
642         }
643     }
644 
645     MyLeAudioCallback mLeAudioCallback = null;
646 
647     // @GuardedBy("mDeviceBroker.mSetModeLock")
648     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onBtProfileConnected(int profile, BluetoothProfile proxy)649     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
650         AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
651                 "BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy "
652                 + proxy).printLog(TAG));
653         if (proxy == null) {
654             Log.e(TAG, "onBtProfileConnected: null proxy for profile: " + profile);
655             return;
656         }
657         switch (profile) {
658             case BluetoothProfile.HEADSET:
659                 onHeadsetProfileConnected((BluetoothHeadset) proxy);
660                 return;
661             case BluetoothProfile.A2DP:
662                 if (((BluetoothA2dp) proxy).equals(mA2dp)) {
663                     return;
664                 }
665                 mA2dp = (BluetoothA2dp) proxy;
666                 break;
667             case BluetoothProfile.HEARING_AID:
668                 if (((BluetoothHearingAid) proxy).equals(mHearingAid)) {
669                     return;
670                 }
671                 mHearingAid = (BluetoothHearingAid) proxy;
672                 break;
673             case BluetoothProfile.LE_AUDIO:
674                 if (((BluetoothLeAudio) proxy).equals(mLeAudio)) {
675                     return;
676                 }
677                 if (mLeAudio != null && mLeAudioCallback != null) {
678                     mLeAudio.unregisterCallback(mLeAudioCallback);
679                 }
680                 mLeAudio = (BluetoothLeAudio) proxy;
681                 mLeAudioCallback = new MyLeAudioCallback();
682                 mLeAudio.registerCallback(
683                             mContext.getMainExecutor(), mLeAudioCallback);
684                 break;
685             case BluetoothProfile.A2DP_SINK:
686             case BluetoothProfile.LE_AUDIO_BROADCAST:
687                 // nothing to do in BtHelper
688                 return;
689             default:
690                 // Not a valid profile to connect
691                 Log.e(TAG, "onBtProfileConnected: Not a valid profile to connect "
692                         + BluetoothProfile.getProfileName(profile));
693                 return;
694         }
695 
696         // this part is only for A2DP, LE Audio unicast and Hearing aid
697         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
698         if (adapter == null) {
699             Log.e(TAG, "onBtProfileConnected: Null BluetoothAdapter when connecting profile: "
700                     + BluetoothProfile.getProfileName(profile));
701             return;
702         }
703         List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile);
704         if (activeDevices.isEmpty() || activeDevices.get(0) == null) {
705             return;
706         }
707         BluetoothDevice device = activeDevices.get(0);
708         switch (profile) {
709             case BluetoothProfile.A2DP: {
710                 BluetoothProfileConnectionInfo bpci =
711                         BluetoothProfileConnectionInfo.createA2dpInfo(false, -1);
712                 postBluetoothActiveDevice(device, bpci);
713             } break;
714             case BluetoothProfile.HEARING_AID: {
715                 BluetoothProfileConnectionInfo bpci =
716                         BluetoothProfileConnectionInfo.createHearingAidInfo(false);
717                 postBluetoothActiveDevice(device, bpci);
718             } break;
719             case BluetoothProfile.LE_AUDIO: {
720                 int groupId = mLeAudio.getGroupId(device);
721                 BluetoothLeAudioCodecStatus btLeCodecStatus = null;
722                 try {
723                     btLeCodecStatus = mLeAudio.getCodecStatus(groupId);
724                 } catch (Exception e) {
725                     Log.e(TAG, "Exception while getting status of " + device, e);
726                 }
727                 if (btLeCodecStatus == null) {
728                     Log.i(TAG, "onBtProfileConnected null LE codec status for groupId: "
729                             + groupId + ", device: " + device);
730                     break;
731                 }
732                 List<BluetoothLeAudioCodecConfig> outputCodecConfigs =
733                         btLeCodecStatus.getOutputCodecSelectableCapabilities();
734                 if (!outputCodecConfigs.isEmpty()) {
735                     BluetoothProfileConnectionInfo bpci =
736                             BluetoothProfileConnectionInfo.createLeAudioInfo(
737                                     false /*suppressNoisyIntent*/, true /*isLeOutput*/);
738                     postBluetoothActiveDevice(device, bpci);
739                 }
740                 List<BluetoothLeAudioCodecConfig> inputCodecConfigs =
741                         btLeCodecStatus.getInputCodecSelectableCapabilities();
742                 if (!inputCodecConfigs.isEmpty()) {
743                     BluetoothProfileConnectionInfo bpci =
744                             BluetoothProfileConnectionInfo.createLeAudioInfo(
745                                     false /*suppressNoisyIntent*/, false /*isLeOutput*/);
746                     postBluetoothActiveDevice(device, bpci);
747                 }
748             } break;
749             default:
750                 // Not a valid profile to connect
751                 Log.wtf(TAG, "Invalid profile! onBtProfileConnected");
752                 break;
753         }
754     }
755 
postBluetoothActiveDevice( BluetoothDevice device, BluetoothProfileConnectionInfo bpci)756     private void postBluetoothActiveDevice(
757             BluetoothDevice device, BluetoothProfileConnectionInfo bpci) {
758         AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData(
759                 device, null, bpci, "mBluetoothProfileServiceListener");
760         AudioDeviceBroker.BtDeviceInfo info = mDeviceBroker.createBtDeviceInfo(
761                 data, device, BluetoothProfile.STATE_CONNECTED);
762         mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */);
763     }
764 
isProfilePoxyConnected(int profile)765     /*package*/ synchronized boolean isProfilePoxyConnected(int profile) {
766         switch (profile) {
767             case BluetoothProfile.HEADSET:
768                 return mBluetoothHeadset != null;
769             case BluetoothProfile.A2DP:
770                 return mA2dp != null;
771             case BluetoothProfile.HEARING_AID:
772                 return mHearingAid != null;
773             case BluetoothProfile.LE_AUDIO:
774                 return mLeAudio != null;
775             case BluetoothProfile.A2DP_SINK:
776             case BluetoothProfile.LE_AUDIO_BROADCAST:
777             default:
778                 // return true for profiles that are not managed by the BtHelper because
779                 // the fact that the profile proxy is not connected does not affect
780                 // the device connection handling.
781                 return true;
782         }
783     }
784 
785     // @GuardedBy("mDeviceBroker.mSetModeLock")
786     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onHeadsetProfileConnected(@onNull BluetoothHeadset headset)787     private void onHeadsetProfileConnected(@NonNull BluetoothHeadset headset) {
788         // Discard timeout message
789         mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
790         mBluetoothHeadset = headset;
791         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
792         if (adapter != null) {
793             List<BluetoothDevice> activeDevices =
794                     adapter.getActiveDevices(BluetoothProfile.HEADSET);
795             for (BluetoothDevice device : activeDevices) {
796                 if (device == null) {
797                     continue;
798                 }
799                 onSetBtScoActiveDevice(device);
800             }
801         } else {
802             Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter");
803         }
804 
805         // Refresh SCO audio state
806         checkScoAudioState();
807         if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
808                 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
809             return;
810         }
811         boolean status = false;
812         if (mBluetoothHeadsetDevice != null) {
813             switch (mScoAudioState) {
814                 case SCO_STATE_ACTIVATE_REQ:
815                     status = connectBluetoothScoAudioHelper(
816                             mBluetoothHeadset,
817                             mBluetoothHeadsetDevice, mScoAudioMode);
818                     if (status) {
819                         mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
820                     }
821                     break;
822                 case SCO_STATE_DEACTIVATE_REQ:
823                     status = disconnectBluetoothScoAudioHelper(
824                             mBluetoothHeadset,
825                             mBluetoothHeadsetDevice, mScoAudioMode);
826                     if (status) {
827                         mScoAudioState = SCO_STATE_DEACTIVATING;
828                     }
829                     break;
830             }
831         }
832         if (!status) {
833             mScoAudioState = SCO_STATE_INACTIVE;
834             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
835         }
836     }
837 
838     //----------------------------------------------------------------------
broadcastScoConnectionState(int state)839     private void broadcastScoConnectionState(int state) {
840         mDeviceBroker.postBroadcastScoConnectionState(state);
841     }
842 
getHeadsetAudioDevice()843     @Nullable AudioDeviceAttributes getHeadsetAudioDevice() {
844         if (mBluetoothHeadsetDevice == null) {
845             return null;
846         }
847         return getHeadsetAudioDevice(mBluetoothHeadsetDevice);
848     }
849 
getHeadsetAudioDevice(BluetoothDevice btDevice)850     private @NonNull AudioDeviceAttributes getHeadsetAudioDevice(BluetoothDevice btDevice) {
851         AudioDeviceAttributes deviceAttr = mResolvedScoAudioDevices.get(btDevice);
852         if (deviceAttr != null) {
853             // Returns the cached device attributes so that it is consistent as the previous one.
854             return deviceAttr;
855         }
856         return btHeadsetDeviceToAudioDevice(btDevice);
857     }
858 
btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice)859     private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
860         if (btDevice == null) {
861             return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
862         }
863         String address = btDevice.getAddress();
864         String name = getName(btDevice);
865         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
866             address = "";
867         }
868         BluetoothClass btClass = btDevice.getBluetoothClass();
869         int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
870         if (btClass != null) {
871             switch (btClass.getDeviceClass()) {
872                 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
873                 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
874                     nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
875                     break;
876                 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
877                     nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
878                     break;
879             }
880         }
881         if (AudioService.DEBUG_DEVICES) {
882             Log.i(TAG, "btHeadsetDeviceToAudioDevice btDevice: " + btDevice
883                     + " btClass: " + (btClass == null ? "Unknown" : btClass)
884                     + " nativeType: " + nativeType + " address: " + address);
885         }
886         return new AudioDeviceAttributes(nativeType, address, name);
887     }
888 
handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive)889     private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
890         if (btDevice == null) {
891             return true;
892         }
893         int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
894         AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
895         boolean result = false;
896         if (isActive) {
897             result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
898         } else {
899             int[] outDeviceTypes = {
900                     AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
901                     AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
902                     AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
903             };
904             for (int outDeviceType : outDeviceTypes) {
905                 result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
906                         outDeviceType, audioDevice.getAddress(), audioDevice.getName()),
907                         isActive, btDevice);
908             }
909         }
910         // handleDeviceConnection() && result to make sure the method get executed
911         result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
912                         inDevice, audioDevice.getAddress(), audioDevice.getName()),
913                 isActive, btDevice) && result;
914         if (result) {
915             if (isActive) {
916                 mResolvedScoAudioDevices.put(btDevice, audioDevice);
917             } else {
918                 mResolvedScoAudioDevices.remove(btDevice);
919             }
920         }
921         return result;
922     }
923 
924     // Return `(null)` if given BluetoothDevice is null. Otherwise, return the anonymized address.
getAnonymizedAddress(BluetoothDevice btDevice)925     private String getAnonymizedAddress(BluetoothDevice btDevice) {
926         return btDevice == null ? "(null)" : btDevice.getAnonymizedAddress();
927     }
928 
929     // @GuardedBy("mDeviceBroker.mSetModeLock")
930     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
onSetBtScoActiveDevice(BluetoothDevice btDevice)931     /*package */ synchronized void onSetBtScoActiveDevice(BluetoothDevice btDevice) {
932         Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice)
933                 + " -> " + getAnonymizedAddress(btDevice));
934         final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
935         if (Objects.equals(btDevice, previousActiveDevice)) {
936             return;
937         }
938         if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
939             Log.w(TAG, "onSetBtScoActiveDevice() failed to remove previous device "
940                     + getAnonymizedAddress(previousActiveDevice));
941         }
942         if (!handleBtScoActiveDeviceChange(btDevice, true)) {
943             Log.e(TAG, "onSetBtScoActiveDevice() failed to add new device "
944                     + getAnonymizedAddress(btDevice));
945             // set mBluetoothHeadsetDevice to null when failing to add new device
946             btDevice = null;
947         }
948         mBluetoothHeadsetDevice = btDevice;
949         if (mBluetoothHeadsetDevice == null) {
950             resetBluetoothSco();
951         }
952     }
953 
954     // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async
955     //      methods inside listener.
956     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
957             new BluetoothProfile.ServiceListener() {
958                 public void onServiceConnected(int profile, BluetoothProfile proxy) {
959                     switch(profile) {
960                         case BluetoothProfile.A2DP:
961                         case BluetoothProfile.HEADSET:
962                         case BluetoothProfile.HEARING_AID:
963                         case BluetoothProfile.LE_AUDIO:
964                         case BluetoothProfile.A2DP_SINK:
965                         case BluetoothProfile.LE_AUDIO_BROADCAST:
966                             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
967                                     "BT profile service: connecting "
968                                     + BluetoothProfile.getProfileName(profile)
969                                     + " profile").printLog(TAG));
970                             mDeviceBroker.postBtProfileConnected(profile, proxy);
971                             break;
972 
973                         default:
974                             break;
975                     }
976                 }
977                 public void onServiceDisconnected(int profile) {
978 
979                     switch (profile) {
980                         case BluetoothProfile.A2DP:
981                         case BluetoothProfile.HEADSET:
982                         case BluetoothProfile.HEARING_AID:
983                         case BluetoothProfile.LE_AUDIO:
984                         case BluetoothProfile.A2DP_SINK:
985                         case BluetoothProfile.LE_AUDIO_BROADCAST:
986                             AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
987                                     "BT profile service: disconnecting "
988                                         + BluetoothProfile.getProfileName(profile)
989                                         + " profile").printLog(TAG));
990                             mDeviceBroker.postBtProfileDisconnected(profile);
991                             break;
992 
993                         default:
994                             break;
995                     }
996                 }
997             };
998 
999     //----------------------------------------------------------------------
1000 
1001     // @GuardedBy("mDeviceBroker.mSetModeLock")
1002     // @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
1003     @GuardedBy("BtHelper.this")
requestScoState(int state, int scoAudioMode)1004     private boolean requestScoState(int state, int scoAudioMode) {
1005         checkScoAudioState();
1006         if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1007             // Make sure that the state transitions to CONNECTING even if we cannot initiate
1008             // the connection except if already connected internally
1009             if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
1010                 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1011             }
1012             switch (mScoAudioState) {
1013                 case SCO_STATE_INACTIVE:
1014                     mScoAudioMode = scoAudioMode;
1015                     if (scoAudioMode == SCO_MODE_UNDEFINED) {
1016                         mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
1017                         if (mBluetoothHeadsetDevice != null) {
1018                             mScoAudioMode = Settings.Global.getInt(
1019                                     mDeviceBroker.getContentResolver(),
1020                                     "bluetooth_sco_channel_"
1021                                             + mBluetoothHeadsetDevice.getAddress(),
1022                                     SCO_MODE_VIRTUAL_CALL);
1023                             if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
1024                                 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
1025                             }
1026                         }
1027                     }
1028                     if (mBluetoothHeadset == null) {
1029                         if (getBluetoothHeadset()) {
1030                             mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1031                         } else {
1032                             Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
1033                                     + " connection, mScoAudioMode=" + mScoAudioMode);
1034                             broadcastScoConnectionState(
1035                                     AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1036                             return false;
1037                         }
1038                         break;
1039                     }
1040                     if (mBluetoothHeadsetDevice == null) {
1041                         Log.w(TAG, "requestScoState: no active device while connecting,"
1042                                 + " mScoAudioMode=" + mScoAudioMode);
1043                         broadcastScoConnectionState(
1044                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1045                         return false;
1046                     }
1047                     if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
1048                             mBluetoothHeadsetDevice, mScoAudioMode)) {
1049                         mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1050                     } else {
1051                         Log.w(TAG, "requestScoState: connect to "
1052                                 + getAnonymizedAddress(mBluetoothHeadsetDevice)
1053                                 + " failed, mScoAudioMode=" + mScoAudioMode);
1054                         broadcastScoConnectionState(
1055                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1056                         return false;
1057                     }
1058                     break;
1059                 case SCO_STATE_DEACTIVATING:
1060                     mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1061                     break;
1062                 case SCO_STATE_DEACTIVATE_REQ:
1063                     mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1064                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1065                     break;
1066                 case SCO_STATE_ACTIVE_INTERNAL:
1067                     // Already in ACTIVE mode, simply return
1068                     break;
1069                 case SCO_STATE_ACTIVE_EXTERNAL:
1070                     /* Confirm SCO Audio connection to requesting app as it is already connected
1071                      * externally (i.e. through SCO APIs by Telecom service).
1072                      * Once SCO Audio is disconnected by the external owner, we will reconnect it
1073                      * automatically on behalf of the requesting app and the state will move to
1074                      * SCO_STATE_ACTIVE_INTERNAL.
1075                      */
1076                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1077                     break;
1078                 default:
1079                     Log.w(TAG, "requestScoState: failed to connect in state "
1080                             + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
1081                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1082                     return false;
1083             }
1084         } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1085             switch (mScoAudioState) {
1086                 case SCO_STATE_ACTIVE_INTERNAL:
1087                     if (mBluetoothHeadset == null) {
1088                         if (getBluetoothHeadset()) {
1089                             mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1090                         } else {
1091                             Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
1092                                     + " disconnection, mScoAudioMode=" + mScoAudioMode);
1093                             mScoAudioState = SCO_STATE_INACTIVE;
1094                             broadcastScoConnectionState(
1095                                     AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1096                             return false;
1097                         }
1098                         break;
1099                     }
1100                     if (mBluetoothHeadsetDevice == null) {
1101                         mScoAudioState = SCO_STATE_INACTIVE;
1102                         broadcastScoConnectionState(
1103                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1104                         break;
1105                     }
1106                     if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
1107                             mBluetoothHeadsetDevice, mScoAudioMode)) {
1108                         mScoAudioState = SCO_STATE_DEACTIVATING;
1109                     } else {
1110                         mScoAudioState = SCO_STATE_INACTIVE;
1111                         broadcastScoConnectionState(
1112                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1113                     }
1114                     break;
1115                 case SCO_STATE_ACTIVATE_REQ:
1116                     mScoAudioState = SCO_STATE_INACTIVE;
1117                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1118                     break;
1119                 default:
1120                     Log.w(TAG, "requestScoState: failed to disconnect in state "
1121                             + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
1122                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1123                     return false;
1124             }
1125         }
1126         return true;
1127     }
1128 
1129     //-----------------------------------------------------
1130     // Utilities
sendStickyBroadcastToAll(Intent intent)1131     private void sendStickyBroadcastToAll(Intent intent) {
1132         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
1133         final long ident = Binder.clearCallingIdentity();
1134         try {
1135             mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1136         } finally {
1137             Binder.restoreCallingIdentity(ident);
1138         }
1139     }
1140 
disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)1141     private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
1142             BluetoothDevice device, int scoAudioMode) {
1143         switch (scoAudioMode) {
1144             case SCO_MODE_VIRTUAL_CALL:
1145                 return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
1146             case SCO_MODE_VR:
1147                 return bluetoothHeadset.stopVoiceRecognition(device);
1148             default:
1149                 return false;
1150         }
1151     }
1152 
connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)1153     private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
1154             BluetoothDevice device, int scoAudioMode) {
1155         switch (scoAudioMode) {
1156             case SCO_MODE_VIRTUAL_CALL:
1157                 return bluetoothHeadset.startScoUsingVirtualVoiceCall();
1158             case SCO_MODE_VR:
1159                 return bluetoothHeadset.startVoiceRecognition(device);
1160             default:
1161                 return false;
1162         }
1163     }
1164 
checkScoAudioState()1165     private void checkScoAudioState() {
1166         if (mBluetoothHeadset != null
1167                 && mBluetoothHeadsetDevice != null
1168                 && mScoAudioState == SCO_STATE_INACTIVE
1169                 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1170                 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1171             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1172         }
1173     }
1174 
getBluetoothHeadset()1175     private boolean getBluetoothHeadset() {
1176         boolean result = false;
1177         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1178         if (adapter != null) {
1179             result = adapter.getProfileProxy(mDeviceBroker.getContext(),
1180                     mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
1181         }
1182         // If we could not get a bluetooth headset proxy, send a failure message
1183         // without delay to reset the SCO audio state and clear SCO clients.
1184         // If we could get a proxy, send a delayed failure message that will reset our state
1185         // in case we don't receive onServiceConnected().
1186         mDeviceBroker.handleFailureToConnectToBtHeadsetService(
1187                 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1188         return result;
1189     }
1190 
getLeAudioDeviceGroupId(BluetoothDevice device)1191     /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) {
1192         if (mLeAudio == null || device == null) {
1193             return BluetoothLeAudio.GROUP_ID_INVALID;
1194         }
1195         return mLeAudio.getGroupId(device);
1196     }
1197 
1198     /**
1199      * Returns all addresses and identity addresses for LE Audio devices a group.
1200      * @param groupId The ID of the group from which to get addresses.
1201      * @return A List of Pair(String main_address, String identity_address). Note that the
1202      * addresses returned by BluetoothDevice can be null.
1203      */
getLeAudioGroupAddresses(int groupId)1204     /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) {
1205         List<Pair<String, String>> addresses = new ArrayList<>();
1206         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1207         if (adapter == null || mLeAudio == null) {
1208             return addresses;
1209         }
1210         List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
1211         for (BluetoothDevice device : activeDevices) {
1212             if (device != null && mLeAudio.getGroupId(device) == groupId) {
1213                 addresses.add(new Pair(device.getAddress(), device.getIdentityAddress()));
1214             }
1215         }
1216         return addresses;
1217     }
1218 
1219     /**
1220      * Returns the String equivalent of the btCodecType.
1221      *
1222      * This uses an "ENCODING_" prefix for consistency with Audio;
1223      * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth.
1224      */
bluetoothCodecToEncodingString(int btCodecType)1225     public static String bluetoothCodecToEncodingString(int btCodecType) {
1226         switch (btCodecType) {
1227             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
1228                 return "ENCODING_SBC";
1229             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
1230                 return "ENCODING_AAC";
1231             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
1232                 return "ENCODING_APTX";
1233             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
1234                 return "ENCODING_APTX_HD";
1235             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
1236                 return "ENCODING_LDAC";
1237             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
1238                 return "ENCODING_OPUS";
1239             default:
1240                 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
1241         }
1242     }
1243 
getProfileFromType(int deviceType)1244     /*package */ static int getProfileFromType(int deviceType) {
1245         if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) {
1246             return BluetoothProfile.A2DP;
1247         } else if (AudioSystem.isBluetoothScoDevice(deviceType)) {
1248             return BluetoothProfile.HEADSET;
1249         } else if (AudioSystem.isBluetoothLeDevice(deviceType)) {
1250             return BluetoothProfile.LE_AUDIO;
1251         }
1252         return 0; // 0 is not a valid profile
1253     }
1254 
getPreferredAudioProfiles(String address)1255     /*package */ static Bundle getPreferredAudioProfiles(String address) {
1256         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1257         return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address));
1258     }
1259 
1260     @Nullable
getBluetoothDevice(String address)1261     /*package */ static BluetoothDevice getBluetoothDevice(String address) {
1262         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1263         if (adapter == null || !BluetoothAdapter.checkBluetoothAddress(address)) {
1264             return null;
1265         }
1266 
1267         return adapter.getRemoteDevice(address);
1268     }
1269 
1270     @AudioDeviceCategory
getBtDeviceCategory(String address)1271     /*package*/ static int getBtDeviceCategory(String address) {
1272         if (!automaticBtDeviceType()) {
1273             return AUDIO_DEVICE_CATEGORY_UNKNOWN;
1274         }
1275 
1276         BluetoothDevice device = BtHelper.getBluetoothDevice(address);
1277         if (device == null) {
1278             return AUDIO_DEVICE_CATEGORY_UNKNOWN;
1279         }
1280 
1281         byte[] deviceType = device.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE);
1282         if (deviceType == null) {
1283             return AUDIO_DEVICE_CATEGORY_UNKNOWN;
1284         }
1285         String deviceCategory = new String(deviceType);
1286         switch (deviceCategory) {
1287             case DEVICE_TYPE_HEARING_AID:
1288                 return AUDIO_DEVICE_CATEGORY_HEARING_AID;
1289             case DEVICE_TYPE_CARKIT:
1290                 return AUDIO_DEVICE_CATEGORY_CARKIT;
1291             case DEVICE_TYPE_HEADSET:
1292             case DEVICE_TYPE_UNTETHERED_HEADSET:
1293                 return AUDIO_DEVICE_CATEGORY_HEADPHONES;
1294             case DEVICE_TYPE_SPEAKER:
1295                 return AUDIO_DEVICE_CATEGORY_SPEAKER;
1296             case DEVICE_TYPE_WATCH:
1297                 return AUDIO_DEVICE_CATEGORY_WATCH;
1298             case DEVICE_TYPE_DEFAULT:
1299             default:
1300                 // fall through
1301         }
1302 
1303         BluetoothClass deviceClass = device.getBluetoothClass();
1304         if (deviceClass == null) {
1305             return AUDIO_DEVICE_CATEGORY_UNKNOWN;
1306         }
1307 
1308         switch (deviceClass.getDeviceClass()) {
1309             case BluetoothClass.Device.WEARABLE_WRIST_WATCH:
1310                 return AUDIO_DEVICE_CATEGORY_WATCH;
1311             case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
1312             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
1313             case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
1314                 return AUDIO_DEVICE_CATEGORY_SPEAKER;
1315             case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1316             case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
1317                 return AUDIO_DEVICE_CATEGORY_HEADPHONES;
1318             case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
1319                 return AUDIO_DEVICE_CATEGORY_RECEIVER;
1320             default:
1321                 return AUDIO_DEVICE_CATEGORY_UNKNOWN;
1322         }
1323     }
1324 
1325     /**
1326      * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices
1327      * have been applied.
1328      */
onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice)1329     public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) {
1330         BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice);
1331     }
1332 
1333     /**
1334      * Returns the string equivalent for the btDeviceClass class.
1335      */
btDeviceClassToString(int btDeviceClass)1336     public static String btDeviceClassToString(int btDeviceClass) {
1337         switch (btDeviceClass) {
1338             case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED:
1339                 return "AUDIO_VIDEO_UNCATEGORIZED";
1340             case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1341                 return "AUDIO_VIDEO_WEARABLE_HEADSET";
1342             case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1343                 return "AUDIO_VIDEO_HANDSFREE";
1344             case 0x040C:
1345                 return "AUDIO_VIDEO_RESERVED_0x040C"; // uncommon
1346             case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE:
1347                 return "AUDIO_VIDEO_MICROPHONE";
1348             case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
1349                 return "AUDIO_VIDEO_LOUDSPEAKER";
1350             case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
1351                 return "AUDIO_VIDEO_HEADPHONES";
1352             case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO:
1353                 return "AUDIO_VIDEO_PORTABLE_AUDIO";
1354             case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1355                 return "AUDIO_VIDEO_CAR_AUDIO";
1356             case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX:
1357                 return "AUDIO_VIDEO_SET_TOP_BOX";
1358             case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
1359                 return "AUDIO_VIDEO_HIFI_AUDIO";
1360             case BluetoothClass.Device.AUDIO_VIDEO_VCR:
1361                 return "AUDIO_VIDEO_VCR";
1362             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA:
1363                 return "AUDIO_VIDEO_VIDEO_CAMERA";
1364             case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER:
1365                 return "AUDIO_VIDEO_CAMCORDER";
1366             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR:
1367                 return "AUDIO_VIDEO_VIDEO_MONITOR";
1368             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER:
1369                 return "AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER";
1370             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING:
1371                 return "AUDIO_VIDEO_VIDEO_CONFERENCING";
1372             case 0x0444:
1373                 return "AUDIO_VIDEO_RESERVED_0x0444"; // uncommon
1374             case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY:
1375                 return "AUDIO_VIDEO_VIDEO_GAMING_TOY";
1376             default: // other device classes printed as a hex string.
1377                 return TextUtils.formatSimple("0x%04x", btDeviceClass);
1378         }
1379     }
1380 
1381     //------------------------------------------------------------
dump(PrintWriter pw, String prefix)1382     /*package*/ void dump(PrintWriter pw, String prefix) {
1383         pw.println("\n" + prefix + "mBluetoothHeadset: " + mBluetoothHeadset);
1384         pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
1385         if (mBluetoothHeadsetDevice != null) {
1386             final BluetoothClass bluetoothClass = mBluetoothHeadsetDevice.getBluetoothClass();
1387             if (bluetoothClass != null) {
1388                 pw.println(prefix + "mBluetoothHeadsetDevice.DeviceClass: "
1389                         + btDeviceClassToString(bluetoothClass.getDeviceClass()));
1390             }
1391         }
1392         pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
1393         pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
1394         pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
1395         pw.println("\n" + prefix + "mLeAudio: " + mLeAudio);
1396         pw.println(prefix + "mA2dp: " + mA2dp);
1397         pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
1398     }
1399 
1400 }
1401