blob: 9e7b428d2cca08f0dc187f23e7b3765d610308ca [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() {
361 if ((mBluetoothHeadset != null)
362 && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
363 != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
364 Log.w(TAG, "isBluetoothScoOn(true) returning false because "
365 + mBluetoothHeadsetDevice + " is not in audio connected mode");
366 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700367 }
368 return true;
369 }
370
371 /**
372 * Disconnect all SCO connections started by {@link AudioManager} except those started by
373 * {@param exceptPid}
374 *
375 * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
376 */
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700377 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
378 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800379 /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
380 checkScoAudioState();
381 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
382 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700383 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800384 clearAllScoClients(exceptPid, true);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700385 }
386
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700387 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
388 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800389 /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700390 @NonNull String eventSource) {
391 ScoClient client = getScoClient(cb, true);
392 // The calling identity must be cleared before calling ScoClient.incCount().
393 // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
394 // and this must be done on behalf of system server to make sure permissions are granted.
395 // The caller identity must be cleared after getScoClient() because it is needed if a new
396 // client is created.
397 final long ident = Binder.clearCallingIdentity();
398 try {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700399 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
Seulki Shinfeb9b892020-03-27 14:05:16 -0700400 client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700401 } catch (NullPointerException e) {
402 Log.e(TAG, "Null ScoClient", e);
403 }
404 Binder.restoreCallingIdentity(ident);
405 }
406
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700407 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
408 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800409 /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
410 @NonNull String eventSource) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700411 ScoClient client = getScoClient(cb, false);
412 // The calling identity must be cleared before calling ScoClient.decCount().
413 // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
414 // and this must be done on behalf of system server to make sure permissions are granted.
415 final long ident = Binder.clearCallingIdentity();
416 if (client != null) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700417 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
Seulki Shinfeb9b892020-03-27 14:05:16 -0700418 client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
419 SCO_MODE_VIRTUAL_CALL);
420 // If a disconnection is pending, the client will be removed whne clearAllScoClients()
421 // is called form receiveBtEvent()
422 if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
423 && mScoAudioState != SCO_STATE_DEACTIVATING) {
424 client.remove(false /*stop */, true /*unregister*/);
425 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700426 }
427 Binder.restoreCallingIdentity(ident);
428 }
429
430
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800431 /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
432 if (mHearingAid == null) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700433 if (AudioService.DEBUG_VOL) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800434 Log.i(TAG, "setHearingAidVolume: null mHearingAid");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700435 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800436 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700437 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800438 //hearing aid expect volume value in range -128dB to 0dB
439 int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
440 AudioSystem.DEVICE_OUT_HEARING_AID);
441 if (gainDB < BT_HEARING_AID_GAIN_MIN) {
442 gainDB = BT_HEARING_AID_GAIN_MIN;
443 }
444 if (AudioService.DEBUG_VOL) {
445 Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
446 + index + " gain=" + gainDB);
447 }
448 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
449 AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
450 mHearingAid.setVolume(gainDB);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700451 }
452
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800453 /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700454 if (state == mScoConnectionState) {
455 return;
456 }
457 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
458 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
459 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
460 mScoConnectionState);
461 sendStickyBroadcastToAll(newIntent);
462 mScoConnectionState = state;
463 }
464
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800465 /*package*/ synchronized void disconnectAllBluetoothProfiles() {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800466 mDeviceBroker.postDisconnectA2dp();
467 mDeviceBroker.postDisconnectA2dpSink();
468 mDeviceBroker.postDisconnectHeadset();
469 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800470 }
471
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700472 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
473 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800474 /*package*/ synchronized void resetBluetoothSco() {
475 clearAllScoClients(0, false);
476 mScoAudioState = SCO_STATE_INACTIVE;
477 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
478 AudioSystem.setParameters("A2dpSuspended=false");
479 mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
480 }
481
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700482 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
483 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800484 /*package*/ synchronized void disconnectHeadset() {
485 setBtScoActiveDevice(null);
486 mBluetoothHeadset = null;
487 }
488
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800489 /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
490 mA2dp = a2dp;
491 final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
492 if (deviceList.isEmpty()) {
493 return;
494 }
495 final BluetoothDevice btDevice = deviceList.get(0);
Jean-Michel Trivib3580a82019-06-24 10:39:19 -0700496 // the device is guaranteed CONNECTED
497 mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice,
498 BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1);
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800499 }
500
501 /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
502 final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
503 if (deviceList.isEmpty()) {
504 return;
505 }
506 final BluetoothDevice btDevice = deviceList.get(0);
507 final @BluetoothProfile.BtProfileState int state =
508 profile.getConnectionState(btDevice);
509 mDeviceBroker.postSetA2dpSourceConnectionState(
510 state, new BluetoothA2dpDeviceInfo(btDevice));
511 }
512
513 /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
514 mHearingAid = hearingAid;
515 final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
516 if (deviceList.isEmpty()) {
517 return;
518 }
519 final BluetoothDevice btDevice = deviceList.get(0);
520 final @BluetoothProfile.BtProfileState int state =
521 mHearingAid.getConnectionState(btDevice);
Jean-Michel Trivifc86cfa2019-03-01 10:15:47 -0800522 mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800523 btDevice, state,
524 /*suppressNoisyIntent*/ false,
525 /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
526 /*eventSource*/ "mBluetoothProfileServiceListener");
527 }
528
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700529 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
530 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800531 /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
532 // Discard timeout message
533 mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
534 mBluetoothHeadset = headset;
535 setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
536 // Refresh SCO audio state
537 checkScoAudioState();
538 if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
539 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
540 return;
541 }
542 boolean status = false;
543 if (mBluetoothHeadsetDevice != null) {
544 switch (mScoAudioState) {
545 case SCO_STATE_ACTIVATE_REQ:
546 status = connectBluetoothScoAudioHelper(
547 mBluetoothHeadset,
548 mBluetoothHeadsetDevice, mScoAudioMode);
549 if (status) {
550 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
551 }
552 break;
553 case SCO_STATE_DEACTIVATE_REQ:
554 status = disconnectBluetoothScoAudioHelper(
555 mBluetoothHeadset,
556 mBluetoothHeadsetDevice, mScoAudioMode);
557 if (status) {
558 mScoAudioState = SCO_STATE_DEACTIVATING;
559 }
560 break;
561 }
562 }
563 if (!status) {
564 mScoAudioState = SCO_STATE_INACTIVE;
565 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
566 }
567 }
568
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800569 //----------------------------------------------------------------------
570 private void broadcastScoConnectionState(int state) {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800571 mDeviceBroker.postBroadcastScoConnectionState(state);
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800572 }
573
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700574 private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
575 if (btDevice == null) {
576 return true;
577 }
578 String address = btDevice.getAddress();
579 BluetoothClass btClass = btDevice.getBluetoothClass();
580 int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
581 int[] outDeviceTypes = {
582 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
583 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
584 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
585 };
586 if (btClass != null) {
587 switch (btClass.getDeviceClass()) {
588 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
589 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
590 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
591 break;
592 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
593 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
594 break;
595 }
596 }
597 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
598 address = "";
599 }
Jack Hecb499642019-04-03 00:48:49 -0700600 String btDeviceName = getName(btDevice);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700601 boolean result = false;
602 if (isActive) {
603 result |= mDeviceBroker.handleDeviceConnection(
604 isActive, outDeviceTypes[0], address, btDeviceName);
605 } else {
606 for (int outDeviceType : outDeviceTypes) {
607 result |= mDeviceBroker.handleDeviceConnection(
608 isActive, outDeviceType, address, btDeviceName);
609 }
610 }
611 // handleDeviceConnection() && result to make sure the method get executed
612 result = mDeviceBroker.handleDeviceConnection(
613 isActive, inDevice, address, btDeviceName) && result;
614 return result;
615 }
616
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700617 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
618 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
619 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700620 private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800621 Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
622 final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
623 if (Objects.equals(btDevice, previousActiveDevice)) {
624 return;
625 }
626 if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
627 Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
628 + previousActiveDevice);
629 }
630 if (!handleBtScoActiveDeviceChange(btDevice, true)) {
631 Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
632 // set mBluetoothHeadsetDevice to null when failing to add new device
633 btDevice = null;
634 }
635 mBluetoothHeadsetDevice = btDevice;
636 if (mBluetoothHeadsetDevice == null) {
637 resetBluetoothSco();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700638 }
639 }
640
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800641 // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async
642 // methods inside listener.
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700643 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
644 new BluetoothProfile.ServiceListener() {
645 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700646 switch(profile) {
647 case BluetoothProfile.A2DP:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800648 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
649 "BT profile service: connecting A2DP profile"));
650 mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700651 break;
652
653 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800654 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
655 "BT profile service: connecting A2DP_SINK profile"));
656 mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700657 break;
658
659 case BluetoothProfile.HEADSET:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800660 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
661 "BT profile service: connecting HEADSET profile"));
662 mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700663 break;
664
665 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800666 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
667 "BT profile service: connecting HEARING_AID profile"));
668 mDeviceBroker.postBtHearingAidProfileConnected(
669 (BluetoothHearingAid) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700670 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700671 default:
672 break;
673 }
674 }
675 public void onServiceDisconnected(int profile) {
676
677 switch (profile) {
678 case BluetoothProfile.A2DP:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800679 mDeviceBroker.postDisconnectA2dp();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700680 break;
681
682 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800683 mDeviceBroker.postDisconnectA2dpSink();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700684 break;
685
686 case BluetoothProfile.HEADSET:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800687 mDeviceBroker.postDisconnectHeadset();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700688 break;
689
690 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800691 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700692 break;
693
694 default:
695 break;
696 }
697 }
698 };
699
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700700 //----------------------------------------------------------------------
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700701 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
702 @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
703 /*package*/ synchronized void scoClientDied(Object obj) {
704 final ScoClient client = (ScoClient) obj;
Seulki Shinfeb9b892020-03-27 14:05:16 -0700705 client.remove(true /*stop*/, false /*unregister*/);
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700706 Log.w(TAG, "SCO client died");
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700707 }
708
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700709 private class ScoClient implements IBinder.DeathRecipient {
710 private IBinder mCb; // To be notified of client's death
711 private int mCreatorPid;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700712
713 ScoClient(IBinder cb) {
714 mCb = cb;
715 mCreatorPid = Binder.getCallingPid();
Seulki Shinfeb9b892020-03-27 14:05:16 -0700716 }
717
718 public void registerDeathRecipient() {
719 try {
720 mCb.linkToDeath(this, 0);
721 } catch (RemoteException e) {
722 Log.w(TAG, "ScoClient could not link to " + mCb + " binder death");
723 }
724 }
725
726 public void unregisterDeathRecipient() {
727 try {
728 mCb.unlinkToDeath(this, 0);
729 } catch (NoSuchElementException e) {
730 Log.w(TAG, "ScoClient could not not unregistered to binder");
731 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700732 }
733
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800734 @Override
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700735 public void binderDied() {
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700736 // process this from DeviceBroker's message queue to take the right locks since
737 // this event can impact SCO mode and requires querying audio mode stack
738 mDeviceBroker.postScoClientDied(this);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700739 }
740
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800741 IBinder getBinder() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700742 return mCb;
743 }
744
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800745 int getPid() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700746 return mCreatorPid;
747 }
748
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700749 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
750 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
751 @GuardedBy("BtHelper.this")
jiabincbf33b22019-04-25 13:35:56 -0700752 private boolean requestScoState(int state, int scoAudioMode) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700753 checkScoAudioState();
Seulki Shinfeb9b892020-03-27 14:05:16 -0700754 if (mScoClients.size() != 1) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700755 Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
Seulki Shinfeb9b892020-03-27 14:05:16 -0700756 + ", num SCO clients=" + mScoClients.size());
jiabincbf33b22019-04-25 13:35:56 -0700757 return true;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700758 }
759 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
760 // Make sure that the state transitions to CONNECTING even if we cannot initiate
761 // the connection.
762 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
763 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
764 // currently controlled by the same client process.
Jean-Michel Trivi5bbcd442019-04-18 12:07:34 -0700765 final int modeOwnerPid = mDeviceBroker.getModeOwnerPid();
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700766 if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
767 Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
768 + modeOwnerPid + " != creatorPid " + mCreatorPid);
769 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700770 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700771 }
772 switch (mScoAudioState) {
773 case SCO_STATE_INACTIVE:
774 mScoAudioMode = scoAudioMode;
775 if (scoAudioMode == SCO_MODE_UNDEFINED) {
776 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
777 if (mBluetoothHeadsetDevice != null) {
778 mScoAudioMode = Settings.Global.getInt(
779 mDeviceBroker.getContentResolver(),
780 "bluetooth_sco_channel_"
781 + mBluetoothHeadsetDevice.getAddress(),
782 SCO_MODE_VIRTUAL_CALL);
783 if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
784 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700785 }
786 }
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700787 }
788 if (mBluetoothHeadset == null) {
789 if (getBluetoothHeadset()) {
790 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700791 } else {
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700792 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
793 + " connection, mScoAudioMode=" + mScoAudioMode);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700794 broadcastScoConnectionState(
795 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700796 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700797 }
798 break;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700799 }
800 if (mBluetoothHeadsetDevice == null) {
801 Log.w(TAG, "requestScoState: no active device while connecting,"
802 + " mScoAudioMode=" + mScoAudioMode);
803 broadcastScoConnectionState(
804 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700805 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700806 }
807 if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
808 mBluetoothHeadsetDevice, mScoAudioMode)) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700809 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700810 } else {
811 Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
812 + " failed, mScoAudioMode=" + mScoAudioMode);
813 broadcastScoConnectionState(
814 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700815 return false;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700816 }
817 break;
818 case SCO_STATE_DEACTIVATING:
819 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
820 break;
821 case SCO_STATE_DEACTIVATE_REQ:
822 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
823 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
824 break;
Seulki Shinfeb9b892020-03-27 14:05:16 -0700825 case SCO_STATE_ACTIVE_INTERNAL:
826 Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
827 break;
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700828 default:
829 Log.w(TAG, "requestScoState: failed to connect in state "
830 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
831 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700832 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700833 }
834 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
835 switch (mScoAudioState) {
836 case SCO_STATE_ACTIVE_INTERNAL:
837 if (mBluetoothHeadset == null) {
838 if (getBluetoothHeadset()) {
839 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
840 } else {
841 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
842 + " disconnection, mScoAudioMode=" + mScoAudioMode);
843 mScoAudioState = SCO_STATE_INACTIVE;
844 broadcastScoConnectionState(
845 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700846 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700847 }
848 break;
849 }
850 if (mBluetoothHeadsetDevice == null) {
851 mScoAudioState = SCO_STATE_INACTIVE;
852 broadcastScoConnectionState(
853 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
854 break;
855 }
856 if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
857 mBluetoothHeadsetDevice, mScoAudioMode)) {
858 mScoAudioState = SCO_STATE_DEACTIVATING;
859 } else {
860 mScoAudioState = SCO_STATE_INACTIVE;
861 broadcastScoConnectionState(
862 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
863 }
864 break;
865 case SCO_STATE_ACTIVATE_REQ:
866 mScoAudioState = SCO_STATE_INACTIVE;
867 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
868 break;
869 default:
870 Log.w(TAG, "requestScoState: failed to disconnect in state "
871 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
872 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
jiabincbf33b22019-04-25 13:35:56 -0700873 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700874 }
875 }
jiabincbf33b22019-04-25 13:35:56 -0700876 return true;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700877 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700878
879 @GuardedBy("BtHelper.this")
880 void remove(boolean stop, boolean unregister) {
881 if (unregister) {
882 unregisterDeathRecipient();
883 }
884 if (stop) {
885 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
886 SCO_MODE_VIRTUAL_CALL);
887 }
888 mScoClients.remove(this);
889 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700890 }
891
892 //-----------------------------------------------------
893 // Utilities
894 private void sendStickyBroadcastToAll(Intent intent) {
895 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
896 final long ident = Binder.clearCallingIdentity();
897 try {
898 mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
899 } finally {
900 Binder.restoreCallingIdentity(ident);
901 }
902 }
903
904 private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
905 BluetoothDevice device, int scoAudioMode) {
906 switch (scoAudioMode) {
907 case SCO_MODE_RAW:
908 return bluetoothHeadset.disconnectAudio();
909 case SCO_MODE_VIRTUAL_CALL:
910 return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
911 case SCO_MODE_VR:
912 return bluetoothHeadset.stopVoiceRecognition(device);
913 default:
914 return false;
915 }
916 }
917
918 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
919 BluetoothDevice device, int scoAudioMode) {
920 switch (scoAudioMode) {
921 case SCO_MODE_RAW:
922 return bluetoothHeadset.connectAudio();
923 case SCO_MODE_VIRTUAL_CALL:
924 return bluetoothHeadset.startScoUsingVirtualVoiceCall();
925 case SCO_MODE_VR:
926 return bluetoothHeadset.startVoiceRecognition(device);
927 default:
928 return false;
929 }
930 }
931
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700932 private void checkScoAudioState() {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800933 if (mBluetoothHeadset != null
934 && mBluetoothHeadsetDevice != null
935 && mScoAudioState == SCO_STATE_INACTIVE
936 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
937 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
938 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700939 }
940 }
941
942
Seulki Shinfeb9b892020-03-27 14:05:16 -0700943 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700944 private ScoClient getScoClient(IBinder cb, boolean create) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800945 for (ScoClient existingClient : mScoClients) {
946 if (existingClient.getBinder() == cb) {
947 return existingClient;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700948 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700949 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800950 if (create) {
951 ScoClient newClient = new ScoClient(cb);
Seulki Shinfeb9b892020-03-27 14:05:16 -0700952 newClient.registerDeathRecipient();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800953 mScoClients.add(newClient);
954 return newClient;
955 }
956 return null;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700957 }
958
Jean-Michel Trivi218da162019-04-05 16:05:04 -0700959 // @GuardedBy("AudioDeviceBroker.mSetModeLock")
960 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
961 @GuardedBy("BtHelper.this")
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700962 private void clearAllScoClients(int exceptPid, boolean stopSco) {
Seulki Shinfeb9b892020-03-27 14:05:16 -0700963 final ArrayList<ScoClient> clients = new ArrayList<ScoClient>();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800964 for (ScoClient cl : mScoClients) {
965 if (cl.getPid() != exceptPid) {
Seulki Shinfeb9b892020-03-27 14:05:16 -0700966 clients.add(cl);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700967 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800968 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700969 for (ScoClient cl : clients) {
970 cl.remove(stopSco, true /*unregister*/);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700971 }
Seulki Shinfeb9b892020-03-27 14:05:16 -0700972
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700973 }
974
975 private boolean getBluetoothHeadset() {
976 boolean result = false;
977 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
978 if (adapter != null) {
979 result = adapter.getProfileProxy(mDeviceBroker.getContext(),
980 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
981 }
982 // If we could not get a bluetooth headset proxy, send a failure message
983 // without delay to reset the SCO audio state and clear SCO clients.
984 // If we could get a proxy, send a delayed failure message that will reset our state
985 // in case we don't receive onServiceConnected().
986 mDeviceBroker.handleFailureToConnectToBtHeadsetService(
987 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0);
988 return result;
989 }
990
Andy Hung69836952020-03-26 01:00:15 -0700991 /**
992 * Returns the String equivalent of the btCodecType.
993 *
994 * This uses an "ENCODING_" prefix for consistency with Audio;
995 * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth.
996 */
997 public static String bluetoothCodecToEncodingString(int btCodecType) {
998 switch (btCodecType) {
999 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
1000 return "ENCODING_SBC";
1001 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
1002 return "ENCODING_AAC";
1003 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
1004 return "ENCODING_APTX";
1005 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
1006 return "ENCODING_APTX_HD";
1007 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
1008 return "ENCODING_LDAC";
1009 default:
1010 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
1011 }
1012 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -07001013}