blob: 04073cb5f45c3b2ab86162b2fe3ea794334b333d [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 Trivi58850372018-09-14 16:01:28 -070039import java.util.ArrayList;
40import java.util.List;
41import java.util.NoSuchElementException;
42import java.util.Objects;
43
44/**
45 * @hide
46 * Class to encapsulate all communication with Bluetooth services
47 */
48public class BtHelper {
49
50 private static final String TAG = "AS.BtHelper";
51
52 private final @NonNull AudioDeviceBroker mDeviceBroker;
53
54 BtHelper(@NonNull AudioDeviceBroker broker) {
55 mDeviceBroker = broker;
56 }
57
58 // List of clients having issued a SCO start request
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080059 private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
Jean-Michel Trivi58850372018-09-14 16:01:28 -070060
61 // BluetoothHeadset API to control SCO connection
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080062 private @Nullable BluetoothHeadset mBluetoothHeadset;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070063
64 // Bluetooth headset device
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080065 private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
66
67 private @Nullable BluetoothHearingAid mHearingAid;
68
69 // Reference to BluetoothA2dp to query for AbsoluteVolume.
70 private @Nullable BluetoothA2dp mA2dp;
71
72 // If absolute volume is supported in AVRCP device
73 private boolean mAvrcpAbsVolSupported = false;
74
75 // Current connection state indicated by bluetooth headset
76 private int mScoConnectionState;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070077
78 // Indicate if SCO audio connection is currently active and if the initiator is
79 // audio service (internal) or bluetooth headset (external)
80 private int mScoAudioState;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080081
82 // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
83 // originated from an app targeting an API version before JB MR2 and raw audio after that.
84 private int mScoAudioMode;
85
Jean-Michel Trivi58850372018-09-14 16:01:28 -070086 // SCO audio state is not active
87 private static final int SCO_STATE_INACTIVE = 0;
88 // SCO audio activation request waiting for headset service to connect
89 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -080090 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
91 // in call audio)
92 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Jean-Michel Trivi58850372018-09-14 16:01:28 -070093 // SCO audio state is active or starting due to a request from AudioManager API
94 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
95 // SCO audio deactivation request waiting for headset service to connect
96 private static final int SCO_STATE_DEACTIVATE_REQ = 4;
97 // SCO audio deactivation in progress, waiting for Bluetooth audio intent
98 private static final int SCO_STATE_DEACTIVATING = 5;
99
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700100 // SCO audio mode is undefined
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800101 /*package*/ static final int SCO_MODE_UNDEFINED = -1;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700102 // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
103 /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
104 // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
105 private static final int SCO_MODE_RAW = 1;
106 // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
107 private static final int SCO_MODE_VR = 2;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800108 // max valid SCO audio mode values
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700109 private static final int SCO_MODE_MAX = 2;
110
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700111 private static final int BT_HEARING_AID_GAIN_MIN = -128;
112
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700113 //----------------------------------------------------------------------
114 /*package*/ static class BluetoothA2dpDeviceInfo {
115 private final @NonNull BluetoothDevice mBtDevice;
116 private final int mVolume;
117 private final int mCodec;
118
119 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) {
120 this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT);
121 }
122
123 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) {
124 mBtDevice = btDevice;
125 mVolume = volume;
126 mCodec = codec;
127 }
128
129 public @NonNull BluetoothDevice getBtDevice() {
130 return mBtDevice;
131 }
132
133 public int getVolume() {
134 return mVolume;
135 }
136
137 public int getCodec() {
138 return mCodec;
139 }
140 }
141
142 //----------------------------------------------------------------------
143 // Interface for AudioDeviceBroker
144
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800145 /*package*/ synchronized void onSystemReady() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700146 mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
147 resetBluetoothSco();
148 getBluetoothHeadset();
149
150 //FIXME: this is to maintain compatibility with deprecated intent
151 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
152 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
153 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
154 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
155 sendStickyBroadcastToAll(newIntent);
156
157 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
158 if (adapter != null) {
159 adapter.getProfileProxy(mDeviceBroker.getContext(),
160 mBluetoothProfileServiceListener, BluetoothProfile.A2DP);
161 adapter.getProfileProxy(mDeviceBroker.getContext(),
162 mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID);
163 }
164 }
165
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800166 /*package*/ synchronized void onAudioServerDiedRestoreA2dp() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700167 final int forMed = mDeviceBroker.getBluetoothA2dpEnabled()
168 ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP;
169 mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()");
170 }
171
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800172 /*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700173 return (mA2dp != null && mAvrcpAbsVolSupported);
174 }
175
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800176 /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700177 mAvrcpAbsVolSupported = supported;
178 }
179
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800180 /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
181 if (mA2dp == null) {
182 if (AudioService.DEBUG_VOL) {
183 Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700184 return;
185 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700186 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800187 if (!mAvrcpAbsVolSupported) {
188 return;
189 }
190 if (AudioService.DEBUG_VOL) {
191 Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
192 }
193 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
Jean-Michel Trivi734819a2019-02-04 15:44:28 -0800194 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index));
195 mA2dp.setAvrcpAbsoluteVolume(index);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700196 }
197
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800198 /*package*/ synchronized int getA2dpCodec(@NonNull BluetoothDevice device) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700199 if (mA2dp == null) {
200 return AudioSystem.AUDIO_FORMAT_DEFAULT;
201 }
202 final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device);
203 if (btCodecStatus == null) {
204 return AudioSystem.AUDIO_FORMAT_DEFAULT;
205 }
206 final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig();
207 if (btCodecConfig == null) {
208 return AudioSystem.AUDIO_FORMAT_DEFAULT;
209 }
210 return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
211 }
212
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800213 /*package*/ synchronized void receiveBtEvent(Intent intent) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700214 final String action = intent.getAction();
215 if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
216 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
217 setBtScoActiveDevice(btDevice);
218 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
219 boolean broadcast = false;
220 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800221 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
222 // broadcast intent if the connection was initated by AudioService
223 if (!mScoClients.isEmpty()
224 && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
225 || mScoAudioState == SCO_STATE_ACTIVATE_REQ
226 || mScoAudioState == SCO_STATE_DEACTIVATE_REQ
227 || mScoAudioState == SCO_STATE_DEACTIVATING)) {
228 broadcast = true;
229 }
230 switch (btState) {
231 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
232 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
233 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
234 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
235 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
236 }
237 mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
238 break;
239 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
240 mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
241 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
242 // startBluetoothSco called after stopBluetoothSco
243 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
244 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
245 && connectBluetoothScoAudioHelper(mBluetoothHeadset,
246 mBluetoothHeadsetDevice, mScoAudioMode)) {
247 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
248 broadcast = false;
249 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700250 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800251 }
252 // Tear down SCO if disconnected from external
253 clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL);
254 mScoAudioState = SCO_STATE_INACTIVE;
255 break;
256 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
257 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
258 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
259 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
260 }
261 break;
262 default:
263 // do not broadcast CONNECTING or invalid state
264 broadcast = false;
265 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700266 }
267 if (broadcast) {
268 broadcastScoConnectionState(scoAudioState);
269 //FIXME: this is to maintain compatibility with deprecated intent
270 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
271 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
272 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
273 sendStickyBroadcastToAll(newIntent);
274 }
275 }
276 }
277
278 /**
279 *
280 * @return false if SCO isn't connected
281 */
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800282 /*package*/ synchronized boolean isBluetoothScoOn() {
283 if ((mBluetoothHeadset != null)
284 && (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
285 != BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
286 Log.w(TAG, "isBluetoothScoOn(true) returning false because "
287 + mBluetoothHeadsetDevice + " is not in audio connected mode");
288 return false;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700289 }
290 return true;
291 }
292
293 /**
294 * Disconnect all SCO connections started by {@link AudioManager} except those started by
295 * {@param exceptPid}
296 *
297 * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
298 */
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800299 /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
300 checkScoAudioState();
301 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
302 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700303 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800304 clearAllScoClients(exceptPid, true);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700305 }
306
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800307 /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700308 @NonNull String eventSource) {
309 ScoClient client = getScoClient(cb, true);
310 // The calling identity must be cleared before calling ScoClient.incCount().
311 // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
312 // and this must be done on behalf of system server to make sure permissions are granted.
313 // The caller identity must be cleared after getScoClient() because it is needed if a new
314 // client is created.
315 final long ident = Binder.clearCallingIdentity();
316 try {
317 eventSource += " client count before=" + client.getCount();
318 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
319 client.incCount(scoAudioMode);
320 } catch (NullPointerException e) {
321 Log.e(TAG, "Null ScoClient", e);
322 }
323 Binder.restoreCallingIdentity(ident);
324 }
325
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800326 /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
327 @NonNull String eventSource) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700328 ScoClient client = getScoClient(cb, false);
329 // The calling identity must be cleared before calling ScoClient.decCount().
330 // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
331 // and this must be done on behalf of system server to make sure permissions are granted.
332 final long ident = Binder.clearCallingIdentity();
333 if (client != null) {
334 eventSource += " client count before=" + client.getCount();
335 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
336 client.decCount();
337 }
338 Binder.restoreCallingIdentity(ident);
339 }
340
341
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800342 /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
343 if (mHearingAid == null) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700344 if (AudioService.DEBUG_VOL) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800345 Log.i(TAG, "setHearingAidVolume: null mHearingAid");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700346 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800347 return;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700348 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800349 //hearing aid expect volume value in range -128dB to 0dB
350 int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
351 AudioSystem.DEVICE_OUT_HEARING_AID);
352 if (gainDB < BT_HEARING_AID_GAIN_MIN) {
353 gainDB = BT_HEARING_AID_GAIN_MIN;
354 }
355 if (AudioService.DEBUG_VOL) {
356 Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
357 + index + " gain=" + gainDB);
358 }
359 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
360 AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
361 mHearingAid.setVolume(gainDB);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700362 }
363
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800364 /*package*/ synchronized void onBroadcastScoConnectionState(int state) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700365 if (state == mScoConnectionState) {
366 return;
367 }
368 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
369 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
370 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
371 mScoConnectionState);
372 sendStickyBroadcastToAll(newIntent);
373 mScoConnectionState = state;
374 }
375
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800376 /*package*/ synchronized void disconnectAllBluetoothProfiles() {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800377 mDeviceBroker.postDisconnectA2dp();
378 mDeviceBroker.postDisconnectA2dpSink();
379 mDeviceBroker.postDisconnectHeadset();
380 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800381 }
382
383 /*package*/ synchronized void resetBluetoothSco() {
384 clearAllScoClients(0, false);
385 mScoAudioState = SCO_STATE_INACTIVE;
386 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
387 AudioSystem.setParameters("A2dpSuspended=false");
388 mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
389 }
390
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800391 /*package*/ synchronized void disconnectHeadset() {
392 setBtScoActiveDevice(null);
393 mBluetoothHeadset = null;
394 }
395
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800396 /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
397 mA2dp = a2dp;
398 final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
399 if (deviceList.isEmpty()) {
400 return;
401 }
402 final BluetoothDevice btDevice = deviceList.get(0);
403 final @BluetoothProfile.BtProfileState int state = mA2dp.getConnectionState(btDevice);
404 mDeviceBroker.handleSetA2dpSinkConnectionState(
405 state, new BluetoothA2dpDeviceInfo(btDevice));
406 }
407
408 /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
409 final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
410 if (deviceList.isEmpty()) {
411 return;
412 }
413 final BluetoothDevice btDevice = deviceList.get(0);
414 final @BluetoothProfile.BtProfileState int state =
415 profile.getConnectionState(btDevice);
416 mDeviceBroker.postSetA2dpSourceConnectionState(
417 state, new BluetoothA2dpDeviceInfo(btDevice));
418 }
419
420 /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
421 mHearingAid = hearingAid;
422 final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
423 if (deviceList.isEmpty()) {
424 return;
425 }
426 final BluetoothDevice btDevice = deviceList.get(0);
427 final @BluetoothProfile.BtProfileState int state =
428 mHearingAid.getConnectionState(btDevice);
Jean-Michel Trivifc86cfa2019-03-01 10:15:47 -0800429 mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800430 btDevice, state,
431 /*suppressNoisyIntent*/ false,
432 /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
433 /*eventSource*/ "mBluetoothProfileServiceListener");
434 }
435
436 /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
437 // Discard timeout message
438 mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
439 mBluetoothHeadset = headset;
440 setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
441 // Refresh SCO audio state
442 checkScoAudioState();
443 if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
444 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
445 return;
446 }
447 boolean status = false;
448 if (mBluetoothHeadsetDevice != null) {
449 switch (mScoAudioState) {
450 case SCO_STATE_ACTIVATE_REQ:
451 status = connectBluetoothScoAudioHelper(
452 mBluetoothHeadset,
453 mBluetoothHeadsetDevice, mScoAudioMode);
454 if (status) {
455 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
456 }
457 break;
458 case SCO_STATE_DEACTIVATE_REQ:
459 status = disconnectBluetoothScoAudioHelper(
460 mBluetoothHeadset,
461 mBluetoothHeadsetDevice, mScoAudioMode);
462 if (status) {
463 mScoAudioState = SCO_STATE_DEACTIVATING;
464 }
465 break;
466 }
467 }
468 if (!status) {
469 mScoAudioState = SCO_STATE_INACTIVE;
470 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
471 }
472 }
473
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800474 //----------------------------------------------------------------------
475 private void broadcastScoConnectionState(int state) {
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800476 mDeviceBroker.postBroadcastScoConnectionState(state);
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800477 }
478
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700479 private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
480 if (btDevice == null) {
481 return true;
482 }
483 String address = btDevice.getAddress();
484 BluetoothClass btClass = btDevice.getBluetoothClass();
485 int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
486 int[] outDeviceTypes = {
487 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
488 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
489 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
490 };
491 if (btClass != null) {
492 switch (btClass.getDeviceClass()) {
493 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
494 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
495 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET };
496 break;
497 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
498 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT };
499 break;
500 }
501 }
502 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
503 address = "";
504 }
505 String btDeviceName = btDevice.getName();
506 boolean result = false;
507 if (isActive) {
508 result |= mDeviceBroker.handleDeviceConnection(
509 isActive, outDeviceTypes[0], address, btDeviceName);
510 } else {
511 for (int outDeviceType : outDeviceTypes) {
512 result |= mDeviceBroker.handleDeviceConnection(
513 isActive, outDeviceType, address, btDeviceName);
514 }
515 }
516 // handleDeviceConnection() && result to make sure the method get executed
517 result = mDeviceBroker.handleDeviceConnection(
518 isActive, inDevice, address, btDeviceName) && result;
519 return result;
520 }
521
522 private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800523 Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
524 final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
525 if (Objects.equals(btDevice, previousActiveDevice)) {
526 return;
527 }
528 if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
529 Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
530 + previousActiveDevice);
531 }
532 if (!handleBtScoActiveDeviceChange(btDevice, true)) {
533 Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
534 // set mBluetoothHeadsetDevice to null when failing to add new device
535 btDevice = null;
536 }
537 mBluetoothHeadsetDevice = btDevice;
538 if (mBluetoothHeadsetDevice == null) {
539 resetBluetoothSco();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700540 }
541 }
542
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800543 // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async
544 // methods inside listener.
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700545 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
546 new BluetoothProfile.ServiceListener() {
547 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700548 switch(profile) {
549 case BluetoothProfile.A2DP:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800550 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
551 "BT profile service: connecting A2DP profile"));
552 mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700553 break;
554
555 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800556 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
557 "BT profile service: connecting A2DP_SINK profile"));
558 mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700559 break;
560
561 case BluetoothProfile.HEADSET:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800562 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
563 "BT profile service: connecting HEADSET profile"));
564 mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700565 break;
566
567 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivi43ff4f32019-02-07 12:08:39 -0800568 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
569 "BT profile service: connecting HEARING_AID profile"));
570 mDeviceBroker.postBtHearingAidProfileConnected(
571 (BluetoothHearingAid) proxy);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700572 break;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700573 default:
574 break;
575 }
576 }
577 public void onServiceDisconnected(int profile) {
578
579 switch (profile) {
580 case BluetoothProfile.A2DP:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800581 mDeviceBroker.postDisconnectA2dp();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700582 break;
583
584 case BluetoothProfile.A2DP_SINK:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800585 mDeviceBroker.postDisconnectA2dpSink();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700586 break;
587
588 case BluetoothProfile.HEADSET:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800589 mDeviceBroker.postDisconnectHeadset();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700590 break;
591
592 case BluetoothProfile.HEARING_AID:
Jean-Michel Trivib9465152019-02-06 15:20:19 -0800593 mDeviceBroker.postDisconnectHearingAid();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700594 break;
595
596 default:
597 break;
598 }
599 }
600 };
601
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700602 //----------------------------------------------------------------------
603 private class ScoClient implements IBinder.DeathRecipient {
604 private IBinder mCb; // To be notified of client's death
605 private int mCreatorPid;
606 private int mStartcount; // number of SCO connections started by this client
607
608 ScoClient(IBinder cb) {
609 mCb = cb;
610 mCreatorPid = Binder.getCallingPid();
611 mStartcount = 0;
612 }
613
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800614 @Override
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700615 public void binderDied() {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800616 // this is the only place the implementation of ScoClient needs to be synchronized
617 // on the instance, as all other methods are directly or indirectly called from
618 // package-private methods, which are synchronized
619 synchronized (BtHelper.this) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700620 Log.w(TAG, "SCO client died");
621 int index = mScoClients.indexOf(this);
622 if (index < 0) {
623 Log.w(TAG, "unregistered SCO client died");
624 } else {
625 clearCount(true);
626 mScoClients.remove(this);
627 }
628 }
629 }
630
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800631 void incCount(int scoAudioMode) {
632 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
633 if (mStartcount == 0) {
634 try {
635 mCb.linkToDeath(this, 0);
636 } catch (RemoteException e) {
637 // client has already died!
638 Log.w(TAG, "ScoClient incCount() could not link to "
639 + mCb + " binder death");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700640 }
641 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800642 mStartcount++;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700643 }
644
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800645 void decCount() {
646 if (mStartcount == 0) {
647 Log.w(TAG, "ScoClient.decCount() already 0");
648 } else {
649 mStartcount--;
650 if (mStartcount == 0) {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700651 try {
652 mCb.unlinkToDeath(this, 0);
653 } catch (NoSuchElementException e) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800654 Log.w(TAG, "decCount() going to 0 but not registered to binder");
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700655 }
656 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800657 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700658 }
659 }
660
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800661 void clearCount(boolean stopSco) {
662 if (mStartcount != 0) {
663 try {
664 mCb.unlinkToDeath(this, 0);
665 } catch (NoSuchElementException e) {
666 Log.w(TAG, "clearCount() mStartcount: "
667 + mStartcount + " != 0 but not registered to binder");
668 }
669 }
670 mStartcount = 0;
671 if (stopSco) {
672 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
673 }
674 }
675
676 int getCount() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700677 return mStartcount;
678 }
679
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800680 IBinder getBinder() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700681 return mCb;
682 }
683
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800684 int getPid() {
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700685 return mCreatorPid;
686 }
687
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800688 private int totalCount() {
689 int count = 0;
690 for (ScoClient mScoClient : mScoClients) {
691 count += mScoClient.getCount();
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700692 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800693 return count;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700694 }
695
696 private void requestScoState(int state, int scoAudioMode) {
697 checkScoAudioState();
698 int clientCount = totalCount();
699 if (clientCount != 0) {
700 Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode
701 + ", clientCount=" + clientCount);
702 return;
703 }
704 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
705 // Make sure that the state transitions to CONNECTING even if we cannot initiate
706 // the connection.
707 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
708 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
709 // currently controlled by the same client process.
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800710 // TODO do not sync that way, see b/123769055
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700711 synchronized (mDeviceBroker.mSetModeLock) {
712 int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
713 ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
714 if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
715 Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
716 + modeOwnerPid + " != creatorPid " + mCreatorPid);
717 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
718 return;
719 }
720 switch (mScoAudioState) {
721 case SCO_STATE_INACTIVE:
722 mScoAudioMode = scoAudioMode;
723 if (scoAudioMode == SCO_MODE_UNDEFINED) {
724 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
725 if (mBluetoothHeadsetDevice != null) {
726 mScoAudioMode = Settings.Global.getInt(
727 mDeviceBroker.getContentResolver(),
728 "bluetooth_sco_channel_"
729 + mBluetoothHeadsetDevice.getAddress(),
730 SCO_MODE_VIRTUAL_CALL);
731 if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
732 mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
733 }
734 }
735 }
736 if (mBluetoothHeadset == null) {
737 if (getBluetoothHeadset()) {
738 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
739 } else {
740 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
741 + " connection, mScoAudioMode=" + mScoAudioMode);
742 broadcastScoConnectionState(
743 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
744 }
745 break;
746 }
747 if (mBluetoothHeadsetDevice == null) {
748 Log.w(TAG, "requestScoState: no active device while connecting,"
749 + " mScoAudioMode=" + mScoAudioMode);
750 broadcastScoConnectionState(
751 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
752 break;
753 }
754 if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
755 mBluetoothHeadsetDevice, mScoAudioMode)) {
756 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
757 } else {
758 Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
759 + " failed, mScoAudioMode=" + mScoAudioMode);
760 broadcastScoConnectionState(
761 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
762 }
763 break;
764 case SCO_STATE_DEACTIVATING:
765 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
766 break;
767 case SCO_STATE_DEACTIVATE_REQ:
768 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
769 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
770 break;
771 default:
772 Log.w(TAG, "requestScoState: failed to connect in state "
773 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
774 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
775 break;
776
777 }
778 }
779 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
780 switch (mScoAudioState) {
781 case SCO_STATE_ACTIVE_INTERNAL:
782 if (mBluetoothHeadset == null) {
783 if (getBluetoothHeadset()) {
784 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
785 } else {
786 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
787 + " disconnection, mScoAudioMode=" + mScoAudioMode);
788 mScoAudioState = SCO_STATE_INACTIVE;
789 broadcastScoConnectionState(
790 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
791 }
792 break;
793 }
794 if (mBluetoothHeadsetDevice == null) {
795 mScoAudioState = SCO_STATE_INACTIVE;
796 broadcastScoConnectionState(
797 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
798 break;
799 }
800 if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset,
801 mBluetoothHeadsetDevice, mScoAudioMode)) {
802 mScoAudioState = SCO_STATE_DEACTIVATING;
803 } else {
804 mScoAudioState = SCO_STATE_INACTIVE;
805 broadcastScoConnectionState(
806 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
807 }
808 break;
809 case SCO_STATE_ACTIVATE_REQ:
810 mScoAudioState = SCO_STATE_INACTIVE;
811 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
812 break;
813 default:
814 Log.w(TAG, "requestScoState: failed to disconnect in state "
815 + mScoAudioState + ", scoAudioMode=" + scoAudioMode);
816 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
817 break;
818 }
819 }
820 }
821 }
822
823 //-----------------------------------------------------
824 // Utilities
825 private void sendStickyBroadcastToAll(Intent intent) {
826 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
827 final long ident = Binder.clearCallingIdentity();
828 try {
829 mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL);
830 } finally {
831 Binder.restoreCallingIdentity(ident);
832 }
833 }
834
835 private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
836 BluetoothDevice device, int scoAudioMode) {
837 switch (scoAudioMode) {
838 case SCO_MODE_RAW:
839 return bluetoothHeadset.disconnectAudio();
840 case SCO_MODE_VIRTUAL_CALL:
841 return bluetoothHeadset.stopScoUsingVirtualVoiceCall();
842 case SCO_MODE_VR:
843 return bluetoothHeadset.stopVoiceRecognition(device);
844 default:
845 return false;
846 }
847 }
848
849 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset,
850 BluetoothDevice device, int scoAudioMode) {
851 switch (scoAudioMode) {
852 case SCO_MODE_RAW:
853 return bluetoothHeadset.connectAudio();
854 case SCO_MODE_VIRTUAL_CALL:
855 return bluetoothHeadset.startScoUsingVirtualVoiceCall();
856 case SCO_MODE_VR:
857 return bluetoothHeadset.startVoiceRecognition(device);
858 default:
859 return false;
860 }
861 }
862
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700863 private void checkScoAudioState() {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800864 if (mBluetoothHeadset != null
865 && mBluetoothHeadsetDevice != null
866 && mScoAudioState == SCO_STATE_INACTIVE
867 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
868 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
869 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700870 }
871 }
872
873
874 private ScoClient getScoClient(IBinder cb, boolean create) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800875 for (ScoClient existingClient : mScoClients) {
876 if (existingClient.getBinder() == cb) {
877 return existingClient;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700878 }
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700879 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800880 if (create) {
881 ScoClient newClient = new ScoClient(cb);
882 mScoClients.add(newClient);
883 return newClient;
884 }
885 return null;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700886 }
887
888 private void clearAllScoClients(int exceptPid, boolean stopSco) {
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800889 ScoClient savedClient = null;
890 for (ScoClient cl : mScoClients) {
891 if (cl.getPid() != exceptPid) {
892 cl.clearCount(stopSco);
893 } else {
894 savedClient = cl;
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700895 }
Jean-Michel Trivic4b9c962019-02-01 09:58:54 -0800896 }
897 mScoClients.clear();
898 if (savedClient != null) {
899 mScoClients.add(savedClient);
Jean-Michel Trivi58850372018-09-14 16:01:28 -0700900 }
901 }
902
903 private boolean getBluetoothHeadset() {
904 boolean result = false;
905 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
906 if (adapter != null) {
907 result = adapter.getProfileProxy(mDeviceBroker.getContext(),
908 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET);
909 }
910 // If we could not get a bluetooth headset proxy, send a failure message
911 // without delay to reset the SCO audio state and clear SCO clients.
912 // If we could get a proxy, send a delayed failure message that will reset our state
913 // in case we don't receive onServiceConnected().
914 mDeviceBroker.handleFailureToConnectToBtHeadsetService(
915 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0);
916 return result;
917 }
918
919 private int mapBluetoothCodecToAudioFormat(int btCodecType) {
920 switch (btCodecType) {
921 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
922 return AudioSystem.AUDIO_FORMAT_SBC;
923 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
924 return AudioSystem.AUDIO_FORMAT_AAC;
925 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
926 return AudioSystem.AUDIO_FORMAT_APTX;
927 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
928 return AudioSystem.AUDIO_FORMAT_APTX_HD;
929 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
930 return AudioSystem.AUDIO_FORMAT_LDAC;
931 default:
932 return AudioSystem.AUDIO_FORMAT_DEFAULT;
933 }
934 }
935}