blob: b6ffcef8fef0f1a9369356a222ae8ea816d30798 [file] [log] [blame]
Jean-Michel Trivi58850372018-09-14 16:01:28 -07001/*
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 */
16package com.android.server.audio;
17
18import android.annotation.NonNull;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080019import android.annotation.Nullable;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070020import android.bluetooth.BluetoothA2dp;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothClass;
23import android.bluetooth.BluetoothCodecConfig;
24import android.bluetooth.BluetoothCodecStatus;
25import android.bluetooth.BluetoothDevice;
26import android.bluetooth.BluetoothHeadset;
27import android.bluetooth.BluetoothHearingAid;
28import android.bluetooth.BluetoothProfile;
29import android.content.Intent;
30import android.media.AudioManager;
31import android.media.AudioSystem;
32import android.os.Binder;
33import android.os.IBinder;
34import android.os.RemoteException;
35import android.os.UserHandle;
36import android.provider.Settings;
37import android.util.Log;
38
Jean-Michel Trivi218da162019-04-05 16:05:04 -070039import com.android.internal.annotations.GuardedBy;
40
Jean-Michel Trivi58850372018-09-14 16:01:28 -070041import java.util.ArrayList;
42import java.util.List;
43import java.util.NoSuchElementException;
44import java.util.Objects;
45
46/**
47 * @hide
48 * Class to encapsulate all communication with Bluetooth services
49 */
50public class BtHelper {
51
52 private static final String TAG = "AS.BtHelper";
53
54 private final @NonNull AudioDeviceBroker mDeviceBroker;
55
56 BtHelper(@NonNull AudioDeviceBroker broker) {
57 mDeviceBroker = broker;
58 }
59
60 // List of clients having issued a SCO start request
Seulki Shinfeb9b892020-03-27 14:05:16 -070061 @GuardedBy("BtHelper.this")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080062 private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
Jean-Michel Trivi58850372018-09-14 16:01:28 -070063
64 // BluetoothHeadset API to control SCO connection
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080065 private @Nullable BluetoothHeadset mBluetoothHeadset;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070066
67 // Bluetooth headset device
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080068 private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
69
70 private @Nullable BluetoothHearingAid mHearingAid;
71
72 // Reference to BluetoothA2dp to query for AbsoluteVolume.
73 private @Nullable BluetoothA2dp mA2dp;
74
75 // If absolute volume is supported in AVRCP device
76 private boolean mAvrcpAbsVolSupported = false;
77
78 // Current connection state indicated by bluetooth headset
79 private int mScoConnectionState;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070080
81 // Indicate if SCO audio connection is currently active and if the initiator is
82 // audio service (internal) or bluetooth headset (external)
83 private int mScoAudioState;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080084
85 // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
86 // originated from an app targeting an API version before JB MR2 and raw audio after that.
87 private int mScoAudioMode;
88
Jean-Michel Trivi58850372018-09-14 16:01:28 -070089 // SCO audio state is not active
90 private static final int SCO_STATE_INACTIVE = 0;
91 // SCO audio activation request waiting for headset service to connect
92 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080093 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
94 // in call audio)
95 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070096 // SCO audio state is active or starting due to a request from AudioManager API
97 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
98 // SCO audio deactivation request waiting for headset service to connect
99 private static final int SCO_STATE_DEACTIVATE_REQ = 4;
100 // SCO audio deactivation in progress, waiting for Bluetooth audio intent
101 private static final int SCO_STATE_DEACTIVATING = 5;
102
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700103 // SCO audio mode is undefined
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800104 /*package*/ static final int SCO_MODE_UNDEFINED = -1;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700105 // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
106 /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
107 // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
108 private static final int SCO_MODE_RAW = 1;
109 // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
110 private static final int SCO_MODE_VR = 2;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800111 // max valid SCO audio mode values
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700112 private static final int SCO_MODE_MAX = 2;
113
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700114 private static final int BT_HEARING_AID_GAIN_MIN = -128;
115
Andy Hung69836952020-03-26 01:00:15 -0700116 /**
117 * Returns a string representation of the scoAudioMode.
118 */
119 public static String scoAudioModeToString(int scoAudioMode) {
120 switch (scoAudioMode) {
121 case SCO_MODE_UNDEFINED:
122 return "SCO_MODE_UNDEFINED";
123 case SCO_MODE_VIRTUAL_CALL:
124 return "SCO_MODE_VIRTUAL_CALL";
125 case SCO_MODE_RAW:
126 return "SCO_MODE_RAW";
127 case SCO_MODE_VR:
128 return "SCO_MODE_VR";
129 default:
130 return "SCO_MODE_(" + scoAudioMode + ")";
131 }
132 }
133
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700134 //----------------------------------------------------------------------
135 /*package*/ static class BluetoothA2dpDeviceInfo {
136 private final @NonNull BluetoothDevice mBtDevice;
137 private final int mVolume;
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700138 private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700139
140 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) {
141 this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT);
142 }
143
144 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) {
145 mBtDevice = btDevice;
146 mVolume = volume;
147 mCodec = codec;
148 }
149
150 public @NonNull BluetoothDevice getBtDevice() {
151 return mBtDevice;
152 }
153
154 public int getVolume() {
155 return mVolume;
156 }
157
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700158 public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700159 return mCodec;
160 }
Jean-Michel Trivib3580a82019-06-24 10:39:19 -0700161
162 // redefine equality op so we can match messages intended for this device
163 @Override
164 public boolean equals(Object o) {
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700165 if (o == null) {
166 return false;
167 }
168 if (this == o) {
169 return true;
170 }
171 if (o instanceof BluetoothA2dpDeviceInfo) {
172 return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice());
173 }
174 return false;
Jean-Michel Trivib3580a82019-06-24 10:39:19 -0700175 }
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700176
177
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700178 }
179
Aniket Kumar Latad7c95982019-02-26 16:23:05 -0800180 // A2DP device events
181 /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0;
182 /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1;
183
184 /*package*/ static String a2dpDeviceEventToString(int event) {
185 switch (event) {
186 case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
187 case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE";
188 default:
189 return new String("invalid event:" + event);
190 }
191 }
192
Jack Hecb499642019-04-03 00:48:49 -0700193 /*package*/ @NonNull static String getName(@NonNull BluetoothDevice device) {
194 final String deviceName = device.getName();
195 if (deviceName == null) {
196 return "";
197 }
198 return deviceName;
199 }
200
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700201 //----------------------------------------------------------------------
202 // Interface for AudioDeviceBroker
203
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700204 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
205 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800206 /*package*/ synchronized void onSystemReady() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700207 mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
208 resetBluetoothSco();
209 getBluetoothHeadset();
210
211 //FIXME: this is to maintain compatibility with deprecated intent
212 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
213 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
214 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
215 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
216 sendStickyBroadcastToAll(newIntent);
217
218 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
219 if (adapter != null) {
220 adapter.getProfileProxy(mDeviceBroker.getContext(),
221 mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
222 adapter.getProfileProxy(mDeviceBroker.getContext(),
223 mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
224 }
225 }
226
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800227 /*package*/ synchronized void onAudioServerDiedRestoreA2dp() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700228 final int forMed = mDeviceBroker.getBluetoothA2dpEnabled()
229 ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP;
230 mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()");
231 }
232
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800233 /*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700234 return (mA2dp != null && mAvrcpAbsVolSupported);
235 }
236
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800237 /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700238 mAvrcpAbsVolSupported = supported;
Jean-Michel Trivif7345252019-05-29 14:25:02 -0700239 Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700240 }
241
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800242 /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
243 if (mA2dp == null) {
244 if (AudioService.DEBUG_VOL) {
Jean-Michel Trivif7345252019-05-29 14:25:02 -0700245 AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
246 "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG));
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700247 return;
248 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700249 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800250 if (!mAvrcpAbsVolSupported) {
Jean-Michel Trivif7345252019-05-29 14:25:02 -0700251 AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent(
252 "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG));
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800253 return;
254 }
255 if (AudioService.DEBUG_VOL) {
256 Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
257 }
258 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
Jean-Michel Trivi734819a2019-02-04 15:44:28 -0800259 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
260 mA2dp.setAvrcpAbsoluteVolume(index);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700261 }
262
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700263 /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec(
264 @NonNull BluetoothDevice device) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700265 if (mA2dp == null) {
266 return AudioSystem.AUDIO_FORMAT_DEFAULT;
267 }
268 final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
269 if (btCodecStatus == null) {
270 return AudioSystem.AUDIO_FORMAT_DEFAULT;
271 }
272 final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
273 if (btCodecConfig == null) {
274 return AudioSystem.AUDIO_FORMAT_DEFAULT;
275 }
Jean-Michel Trivie9e4a3d2020-04-22 21:38:07 -0700276 return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700277 }
278
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700279 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
280 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800281 /*package*/ synchronized void receiveBtEvent(Intent intent) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700282 final String action = intent.getAction();
283 if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
284 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
285 setBtScoActiveDevice(btDevice);
286 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
287 boolean broadcast = false;
288 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800289 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
290 // broadcast intent if the connection was initated by AudioService
291 if (!mScoClients.isEmpty()
292 && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
293 || mScoAudioState == SCO_STATE_ACTIVATE_REQ
294 || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
295 || mScoAudioState == SCO_STATE_DEACTIVATING)) {
296 broadcast = true;
297 }
298 switch (btState) {
299 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
300 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
301 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
302 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
303 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
304 }
305 mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
306 break;
307 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
308 mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
309 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
Eric Laurent4394d3b2020-05-10 17:10:06 -0700310 // There are two cases where we want to immediately reconnect audio:
311 // 1) If a new start request was received while disconnecting: this was
312 // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
313 // 2) If audio was connected then disconnected via Bluetooth APIs and
314 // we still have pending activation requests by apps: this is indicated by
315 // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty.
316 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
317 || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
318 && !mScoClients.isEmpty())) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800319 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
320 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
321 mBluetoothHeadsetDevice, mScoAudioMode)) {
322 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
323 broadcast = false;
324 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700325 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800326 }
327 // Tear down SCO if disconnected from external
Eric Laurent4394d3b2020-05-10 17:10:06 -0700328 if (mScoAudioState == SCO_STATE_DEACTIVATING) {
329 clearAllScoClients(0, false);
330 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800331 mScoAudioState = SCO_STATE_INACTIVE;
332 break;
333 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
334 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
335 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
336 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
337 }
Myles Watson866b1bb2019-12-03 11:18:27 -0800338 broadcast = false;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800339 break;
340 default:
341 // do not broadcast CONNECTING or invalid state
342 broadcast = false;
343 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700344 }
345 if (broadcast) {
346 broadcastScoConnectionState(scoAudioState);
347 //FIXME: this is to maintain compatibility with deprecated intent
348 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
349 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
350 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
351 sendStickyBroadcastToAll(newIntent);
352 }
353 }
354 }
355
356 /**
357 *
358 * @return false if SCO isn't connected
359 */
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800360 /*package*/ synchronized boolean isBluetoothScoOn() {
Eric Laurent1dd060f2020-05-13 17:43:24 -0700361 if (mBluetoothHeadset == null) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800362 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700363 }
Eric Laurent1dd060f2020-05-13 17:43:24 -0700364 return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
365 == BluetoothHeadset.STATE_AUDIO_CONNECTED;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700366 }
367
368 /**
369 * Disconnect all SCO connections started by {@link AudioManager} except those started by
370 * {@param exceptPid}
371 *
372 * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
373 */
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700374 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
375 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800376 /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
377 checkScoAudioState();
378 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
379 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700380 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800381 clearAllScoClients(exceptPid, true);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700382 }
383
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700384 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
385 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800386 /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700387 @NonNull String eventSource) {
388 ScoClient client = getScoClient(cb, true);
389 // The calling identity must be cleared before calling ScoClient.incCount().
390 // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
391 // and this must be done on behalf of system server to make sure permissions are granted.
392 // The caller identity must be cleared after getScoClient() because it is needed if a new
393 // client is created.
394 final long ident = Binder.clearCallingIdentity();
395 try {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700396 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
Seulki Shinfeb9b892020-03-27 14:05:16 -0700397 client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700398 } catch (NullPointerException e) {
399 Log.e(TAG, "Null ScoClient", e);
400 }
401 Binder.restoreCallingIdentity(ident);
402 }
403
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700404 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
405 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800406 /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
407 @NonNull String eventSource) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700408 ScoClient client = getScoClient(cb, false);
409 // The calling identity must be cleared before calling ScoClient.decCount().
410 // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
411 // and this must be done on behalf of system server to make sure permissions are granted.
412 final long ident = Binder.clearCallingIdentity();
413 if (client != null) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700414 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
Seulki Shinfeb9b892020-03-27 14:05:16 -0700415 client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
416 SCO_MODE_VIRTUAL_CALL);
417 // If a disconnection is pending, the client will be removed whne clearAllScoClients()
418 // is called form receiveBtEvent()
419 if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
420 && mScoAudioState != SCO_STATE_DEACTIVATING) {
421 client.remove(false /*stop */, true /*unregister*/);
422 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700423 }
424 Binder.restoreCallingIdentity(ident);
425 }
426
427
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800428 /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
429 if (mHearingAid == null) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700430 if (AudioService.DEBUG_VOL) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800431 Log.i(TAG, "setHearingAidVolume: null mHearingAid");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700432 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800433 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700434 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800435 //hearing aid expect volume value in range -128dB to 0dB
436 int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
437 AudioSystem.DEVICE_OUT_HEARING_AID);
438 if (gainDB < BT_HEARING_AID_GAIN_MIN) {
439 gainDB = BT_HEARING_AID_GAIN_MIN;
440 }
441 if (AudioService.DEBUG_VOL) {
442 Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
443 + index + " gain=" + gainDB);
444 }
445 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
446 AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
447 mHearingAid.setVolume(gainDB);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700448 }
449
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800450 /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700451 if (state == mScoConnectionState) {
452 return;
453 }
454 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
455 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
456 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
457 mScoConnectionState);
458 sendStickyBroadcastToAll(newIntent);
459 mScoConnectionState = state;
460 }
461
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800462 /*package*/ synchronized void disconnectAllBluetoothProfiles() {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800463 mDeviceBroker.postDisconnectA2dp();
464 mDeviceBroker.postDisconnectA2dpSink();
465 mDeviceBroker.postDisconnectHeadset();
466 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800467 }
468
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700469 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
470 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800471 /*package*/ synchronized void resetBluetoothSco() {
472 clearAllScoClients(0, false);
473 mScoAudioState = SCO_STATE_INACTIVE;
474 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
475 AudioSystem.setParameters("A2dpSuspended=false");
476 mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
477 }
478
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700479 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
480 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800481 /*package*/ synchronized void disconnectHeadset() {
482 setBtScoActiveDevice(null);
483 mBluetoothHeadset = null;
484 }
485
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800486 /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
487 mA2dp = a2dp;
488 final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
489 if (deviceList.isEmpty()) {
490 return;
491 }
492 final BluetoothDevice btDevice = deviceList.get(0);
Jean-Michel Trivib3580a82019-06-24 10:39:19 -0700493 // the device is guaranteed CONNECTED
494 mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice,
495 BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1);
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800496 }
497
498 /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
499 final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
500 if (deviceList.isEmpty()) {
501 return;
502 }
503 final BluetoothDevice btDevice = deviceList.get(0);
504 final @BluetoothProfile.BtProfileState int state =
505 profile.getConnectionState(btDevice);
506 mDeviceBroker.postSetA2dpSourceConnectionState(
507 state, new BluetoothA2dpDeviceInfo(btDevice));
508 }
509
510 /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
511 mHearingAid = hearingAid;
512 final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
513 if (deviceList.isEmpty()) {
514 return;
515 }
516 final BluetoothDevice btDevice = deviceList.get(0);
517 final @BluetoothProfile.BtProfileState int state =
518 mHearingAid.getConnectionState(btDevice);
Jean-Michel Trivifc86cfa2019-03-01 10:15:47 -0800519 mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800520 btDevice, state,
521 /*suppressNoisyIntent*/ false,
522 /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
523 /*eventSource*/ "mBluetoothProfileServiceListener");
524 }
525
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700526 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
527 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800528 /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
529 // Discard timeout message
530 mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
531 mBluetoothHeadset = headset;
532 setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
533 // Refresh SCO audio state
534 checkScoAudioState();
535 if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
536 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
537 return;
538 }
539 boolean status = false;
540 if (mBluetoothHeadsetDevice != null) {
541 switch (mScoAudioState) {
542 case SCO_STATE_ACTIVATE_REQ:
543 status = connectBluetoothScoAudioHelper(
544 mBluetoothHeadset,
545 mBluetoothHeadsetDevice, mScoAudioMode);
546 if (status) {
547 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
548 }
549 break;
550 case SCO_STATE_DEACTIVATE_REQ:
551 status = disconnectBluetoothScoAudioHelper(
552 mBluetoothHeadset,
553 mBluetoothHeadsetDevice, mScoAudioMode);
554 if (status) {
555 mScoAudioState = SCO_STATE_DEACTIVATING;
556 }
557 break;
558 }
559 }
560 if (!status) {
561 mScoAudioState = SCO_STATE_INACTIVE;
562 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
563 }
564 }
565
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800566 //----------------------------------------------------------------------
567 private void broadcastScoConnectionState(int state) {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800568 mDeviceBroker.postBroadcastScoConnectionState(state);
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800569 }
570
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700571 private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
572 if (btDevice == null) {
573 return true;
574 }
575 String address = btDevice.getAddress();
576 BluetoothClass btClass = btDevice.getBluetoothClass();
577 int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
578 int[] outDeviceTypes = {
579 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
580 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
581 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
582 };
583 if (btClass != null) {
584 switch (btClass.getDeviceClass()) {
585 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
586 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
587 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
588 break;
589 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
590 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
591 break;
592 }
593 }
594 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
595 address = "";
596 }
Jack Hecb499642019-04-03 00:48:49 -0700597 String btDeviceName = getName(btDevice);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700598 boolean result = false;
599 if (isActive) {
600 result |= mDeviceBroker.handleDeviceConnection(
601 isActive, outDeviceTypes[0], address, btDeviceName);
602 } else {
603 for (int outDeviceType : outDeviceTypes) {
604 result |= mDeviceBroker.handleDeviceConnection(
605 isActive, outDeviceType, address, btDeviceName);
606 }
607 }
608 // handleDeviceConnection() && result to make sure the method get executed
609 result = mDeviceBroker.handleDeviceConnection(
610 isActive, inDevice, address, btDeviceName) && result;
611 return result;
612 }
613
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700614 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
615 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
616 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700617 private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800618 Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
619 final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
620 if (Objects.equals(btDevice, previousActiveDevice)) {
621 return;
622 }
623 if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
624 Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
625 + previousActiveDevice);
626 }
627 if (!handleBtScoActiveDeviceChange(btDevice, true)) {
628 Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
629 // set mBluetoothHeadsetDevice to null when failing to add new device
630 btDevice = null;
631 }
632 mBluetoothHeadsetDevice = btDevice;
633 if (mBluetoothHeadsetDevice == null) {
634 resetBluetoothSco();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700635 }
636 }
637
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800638 // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async
639 // methods inside listener.
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700640 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
641 new BluetoothProfile.ServiceListener() {
642 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700643 switch(profile) {
644 case BluetoothProfile.A2DP:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800645 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
646 "BT profile service: connecting A2DP profile"));
647 mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700648 break;
649
650 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800651 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
652 "BT profile service: connecting A2DP_SINK profile"));
653 mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700654 break;
655
656 case BluetoothProfile.HEADSET:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800657 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
658 "BT profile service: connecting HEADSET profile"));
659 mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700660 break;
661
662 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800663 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
664 "BT profile service: connecting HEARING_AID profile"));
665 mDeviceBroker.postBtHearingAidProfileConnected(
666 (BluetoothHearingAid) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700667 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700668 default:
669 break;
670 }
671 }
672 public void onServiceDisconnected(int profile) {
673
674 switch (profile) {
675 case BluetoothProfile.A2DP:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800676 mDeviceBroker.postDisconnectA2dp();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700677 break;
678
679 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800680 mDeviceBroker.postDisconnectA2dpSink();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700681 break;
682
683 case BluetoothProfile.HEADSET:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800684 mDeviceBroker.postDisconnectHeadset();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700685 break;
686
687 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800688 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700689 break;
690
691 default:
692 break;
693 }
694 }
695 };
696
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700697 //----------------------------------------------------------------------
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700698 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
699 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
700 /*package*/ synchronized void scoClientDied(Object obj) {
701 final ScoClient client = (ScoClient) obj;
Seulki Shinfeb9b892020-03-27 14:05:16 -0700702 client.remove(true /*stop*/, false /*unregister*/);
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700703 Log.w(TAG, "SCO client died");
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700704 }
705
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700706 private class ScoClient implements IBinder.DeathRecipient {
707 private IBinder mCb; // To be notified of client's death
708 private int mCreatorPid;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700709
710 ScoClient(IBinder cb) {
711 mCb = cb;
712 mCreatorPid = Binder.getCallingPid();
Seulki Shinfeb9b892020-03-27 14:05:16 -0700713 }
714
715 public void registerDeathRecipient() {
716 try {
717 mCb.linkToDeath(this, 0);
718 } catch (RemoteException e) {
719 Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
720 }
721 }
722
723 public void unregisterDeathRecipient() {
724 try {
725 mCb.unlinkToDeath(this, 0);
726 } catch (NoSuchElementException e) {
727 Log.w(TAG, "ScoClient could not not unregistered to binder");
728 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700729 }
730
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800731 @Override
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700732 public void binderDied() {
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700733 // process this from DeviceBroker's message queue to take the right locks since
734 // this event can impact SCO mode and requires querying audio mode stack
735 mDeviceBroker.postScoClientDied(this);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700736 }
737
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800738 IBinder getBinder() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700739 return mCb;
740 }
741
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800742 int getPid() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700743 return mCreatorPid;
744 }
745
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700746 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
747 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
748 @GuardedBy("BtHelper.this")
jiabincbf33b22019-04-25 13:35:56 -0700749 private boolean requestScoState(int state, int scoAudioMode) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700750 checkScoAudioState();
Seulki Shinfeb9b892020-03-27 14:05:16 -0700751 if (mScoClients.size() != 1) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700752 Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
Seulki Shinfeb9b892020-03-27 14:05:16 -0700753 + ", num SCO clients=" + mScoClients.size());
jiabincbf33b22019-04-25 13:35:56 -0700754 return true;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700755 }
756 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
757 // Make sure that the state transitions to CONNECTING even if we cannot initiate
758 // the connection.
759 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
760 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
761 // currently controlled by the same client process.
Jean-Michel Trivi5bbcd442019-04-18 12:07:34 -0700762 final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700763 if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
764 Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
765 + modeOwnerPid + " != creatorPid " + mCreatorPid);
766 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700767 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700768 }
769 switch (mScoAudioState) {
770 case SCO_STATE_INACTIVE:
771 mScoAudioMode = scoAudioMode;
772 if (scoAudioMode == SCO_MODE_UNDEFINED) {
773 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
774 if (mBluetoothHeadsetDevice != null) {
775 mScoAudioMode = Settings.Global.getInt(
776 mDeviceBroker.getContentResolver(),
777 "bluetooth_sco_channel_"
778 + mBluetoothHeadsetDevice.getAddress(),
779 SCO_MODE_VIRTUAL_CALL);
780 if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
781 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700782 }
783 }
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700784 }
785 if (mBluetoothHeadset == null) {
786 if (getBluetoothHeadset()) {
787 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700788 } else {
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700789 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
790 + " connection, mScoAudioMode=" + mScoAudioMode);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700791 broadcastScoConnectionState(
792 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700793 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700794 }
795 break;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700796 }
797 if (mBluetoothHeadsetDevice == null) {
798 Log.w(TAG, "requestScoState: no active device while connecting,"
799 + " mScoAudioMode=" + mScoAudioMode);
800 broadcastScoConnectionState(
801 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700802 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700803 }
804 if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
805 mBluetoothHeadsetDevice, mScoAudioMode)) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700806 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700807 } else {
808 Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
809 + " failed, mScoAudioMode=" + mScoAudioMode);
810 broadcastScoConnectionState(
811 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700812 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700813 }
814 break;
815 case SCO_STATE_DEACTIVATING:
816 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
817 break;
818 case SCO_STATE_DEACTIVATE_REQ:
819 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
820 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
821 break;
Seulki Shinfeb9b892020-03-27 14:05:16 -0700822 case SCO_STATE_ACTIVE_INTERNAL:
823 Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
824 break;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700825 default:
826 Log.w(TAG, "requestScoState: failed to connect in state "
827 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
828 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700829 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700830 }
831 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
832 switch (mScoAudioState) {
833 case SCO_STATE_ACTIVE_INTERNAL:
834 if (mBluetoothHeadset == null) {
835 if (getBluetoothHeadset()) {
836 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
837 } else {
838 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
839 + " disconnection, mScoAudioMode=" + mScoAudioMode);
840 mScoAudioState = SCO_STATE_INACTIVE;
841 broadcastScoConnectionState(
842 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700843 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700844 }
845 break;
846 }
847 if (mBluetoothHeadsetDevice == null) {
848 mScoAudioState = SCO_STATE_INACTIVE;
849 broadcastScoConnectionState(
850 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
851 break;
852 }
853 if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
854 mBluetoothHeadsetDevice, mScoAudioMode)) {
855 mScoAudioState = SCO_STATE_DEACTIVATING;
856 } else {
857 mScoAudioState = SCO_STATE_INACTIVE;
858 broadcastScoConnectionState(
859 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
860 }
861 break;
862 case SCO_STATE_ACTIVATE_REQ:
863 mScoAudioState = SCO_STATE_INACTIVE;
864 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
865 break;
866 default:
867 Log.w(TAG, "requestScoState: failed to disconnect in state "
868 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
869 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700870 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700871 }
872 }
jiabincbf33b22019-04-25 13:35:56 -0700873 return true;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700874 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700875
876 @GuardedBy("BtHelper.this")
877 void remove(boolean stop, boolean unregister) {
878 if (unregister) {
879 unregisterDeathRecipient();
880 }
881 if (stop) {
882 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
883 SCO_MODE_VIRTUAL_CALL);
884 }
885 mScoClients.remove(this);
886 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700887 }
888
889 //-----------------------------------------------------
890 // Utilities
891 private void sendStickyBroadcastToAll(Intent intent) {
892 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
893 final long ident = Binder.clearCallingIdentity();
894 try {
895 mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
896 } finally {
897 Binder.restoreCallingIdentity(ident);
898 }
899 }
900
901 private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
902 BluetoothDevice device, int scoAudioMode) {
903 switch (scoAudioMode) {
904 case SCO_MODE_RAW:
905 return bluetoothHeadset.disconnectAudio();
906 case SCO_MODE_VIRTUAL_CALL:
907 return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
908 case SCO_MODE_VR:
909 return bluetoothHeadset.stopVoiceRecognition(device);
910 default:
911 return false;
912 }
913 }
914
915 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
916 BluetoothDevice device, int scoAudioMode) {
917 switch (scoAudioMode) {
918 case SCO_MODE_RAW:
919 return bluetoothHeadset.connectAudio();
920 case SCO_MODE_VIRTUAL_CALL:
921 return bluetoothHeadset.startScoUsingVirtualVoiceCall();
922 case SCO_MODE_VR:
923 return bluetoothHeadset.startVoiceRecognition(device);
924 default:
925 return false;
926 }
927 }
928
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700929 private void checkScoAudioState() {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800930 if (mBluetoothHeadset != null
931 && mBluetoothHeadsetDevice != null
932 && mScoAudioState == SCO_STATE_INACTIVE
933 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
934 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
935 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700936 }
937 }
938
939
Seulki Shinfeb9b892020-03-27 14:05:16 -0700940 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700941 private ScoClient getScoClient(IBinder cb, boolean create) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800942 for (ScoClient existingClient : mScoClients) {
943 if (existingClient.getBinder() == cb) {
944 return existingClient;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700945 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700946 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800947 if (create) {
948 ScoClient newClient = new ScoClient(cb);
Seulki Shinfeb9b892020-03-27 14:05:16 -0700949 newClient.registerDeathRecipient();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800950 mScoClients.add(newClient);
951 return newClient;
952 }
953 return null;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700954 }
955
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700956 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
957 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
958 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700959 private void clearAllScoClients(int exceptPid, boolean stopSco) {
Seulki Shinfeb9b892020-03-27 14:05:16 -0700960 final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800961 for (ScoClient cl : mScoClients) {
962 if (cl.getPid() != exceptPid) {
Seulki Shinfeb9b892020-03-27 14:05:16 -0700963 clients.add(cl);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700964 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800965 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700966 for (ScoClient cl : clients) {
967 cl.remove(stopSco, true /*unregister*/);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700968 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700969
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700970 }
971
972 private boolean getBluetoothHeadset() {
973 boolean result = false;
974 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
975 if (adapter != null) {
976 result = adapter.getProfileProxy(mDeviceBroker.getContext(),
977 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
978 }
979 // If we could not get a bluetooth headset proxy, send a failure message
980 // without delay to reset the SCO audio state and clear SCO clients.
981 // If we could get a proxy, send a delayed failure message that will reset our state
982 // in case we don't receive onServiceConnected().
983 mDeviceBroker.handleFailureToConnectToBtHeadsetService(
984 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0);
985 return result;
986 }
987
Andy Hung69836952020-03-26 01:00:15 -0700988 /**
989 * Returns the String equivalent of the btCodecType.
990 *
991 * This uses an "ENCODING_" prefix for consistency with Audio;
992 * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth.
993 */
994 public static String bluetoothCodecToEncodingString(int btCodecType) {
995 switch (btCodecType) {
996 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
997 return "ENCODING_SBC";
998 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
999 return "ENCODING_AAC";
1000 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
1001 return "ENCODING_APTX";
1002 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
1003 return "ENCODING_APTX_HD";
1004 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
1005 return "ENCODING_LDAC";
1006 default:
1007 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
1008 }
1009 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -07001010}