1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.audio; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.bluetooth.BluetoothA2dp; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothClass; 23 import android.bluetooth.BluetoothCodecConfig; 24 import android.bluetooth.BluetoothCodecStatus; 25 import android.bluetooth.BluetoothDevice; 26 import android.bluetooth.BluetoothHeadset; 27 import android.bluetooth.BluetoothHearingAid; 28 import android.bluetooth.BluetoothProfile; 29 import android.content.Intent; 30 import android.media.AudioManager; 31 import android.media.AudioSystem; 32 import android.os.Binder; 33 import android.os.IBinder; 34 import android.os.RemoteException; 35 import android.os.UserHandle; 36 import android.provider.Settings; 37 import android.util.Log; 38 39 import com.android.internal.annotations.GuardedBy; 40 41 import java.io.PrintWriter; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.NoSuchElementException; 45 import java.util.Objects; 46 47 /** 48 * @hide 49 * Class to encapsulate all communication with Bluetooth services 50 */ 51 public class BtHelper { 52 53 private static final String TAG = "AS.BtHelper"; 54 55 private final @NonNull AudioDeviceBroker mDeviceBroker; 56 BtHelper(@onNull AudioDeviceBroker broker)57 BtHelper(@NonNull AudioDeviceBroker broker) { 58 mDeviceBroker = broker; 59 } 60 61 // List of clients having issued a SCO start request 62 @GuardedBy("BtHelper.this") 63 private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>(); 64 65 // BluetoothHeadset API to control SCO connection 66 private @Nullable BluetoothHeadset mBluetoothHeadset; 67 68 // Bluetooth headset device 69 private @Nullable BluetoothDevice mBluetoothHeadsetDevice; 70 71 private @Nullable BluetoothHearingAid mHearingAid; 72 73 // Reference to BluetoothA2dp to query for AbsoluteVolume. 74 private @Nullable BluetoothA2dp mA2dp; 75 76 // If absolute volume is supported in AVRCP device 77 private boolean mAvrcpAbsVolSupported = false; 78 79 // Current connection state indicated by bluetooth headset 80 private int mScoConnectionState; 81 82 // Indicate if SCO audio connection is currently active and if the initiator is 83 // audio service (internal) or bluetooth headset (external) 84 private int mScoAudioState; 85 86 // Indicates the mode used for SCO audio connection. The mode is virtual call if the request 87 // originated from an app targeting an API version before JB MR2 and raw audio after that. 88 private int mScoAudioMode; 89 90 // SCO audio state is not active 91 private static final int SCO_STATE_INACTIVE = 0; 92 // SCO audio activation request waiting for headset service to connect 93 private static final int SCO_STATE_ACTIVATE_REQ = 1; 94 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 95 // in call audio) 96 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 97 // SCO audio state is active or starting due to a request from AudioManager API 98 private static final int SCO_STATE_ACTIVE_INTERNAL = 3; 99 // SCO audio deactivation request waiting for headset service to connect 100 private static final int SCO_STATE_DEACTIVATE_REQ = 4; 101 // SCO audio deactivation in progress, waiting for Bluetooth audio intent 102 private static final int SCO_STATE_DEACTIVATING = 5; 103 104 // SCO audio mode is undefined 105 /*package*/ static final int SCO_MODE_UNDEFINED = -1; 106 // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) 107 /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; 108 // SCO audio mode is raw audio (BluetoothHeadset.connectAudio()) 109 private static final int SCO_MODE_RAW = 1; 110 // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) 111 private static final int SCO_MODE_VR = 2; 112 // max valid SCO audio mode values 113 private static final int SCO_MODE_MAX = 2; 114 115 private static final int BT_HEARING_AID_GAIN_MIN = -128; 116 117 /** 118 * Returns a string representation of the scoAudioMode. 119 */ scoAudioModeToString(int scoAudioMode)120 public static String scoAudioModeToString(int scoAudioMode) { 121 switch (scoAudioMode) { 122 case SCO_MODE_UNDEFINED: 123 return "SCO_MODE_UNDEFINED"; 124 case SCO_MODE_VIRTUAL_CALL: 125 return "SCO_MODE_VIRTUAL_CALL"; 126 case SCO_MODE_RAW: 127 return "SCO_MODE_RAW"; 128 case SCO_MODE_VR: 129 return "SCO_MODE_VR"; 130 default: 131 return "SCO_MODE_(" + scoAudioMode + ")"; 132 } 133 } 134 135 /** 136 * Returns a string representation of the scoAudioState. 137 */ scoAudioStateToString(int scoAudioState)138 public static String scoAudioStateToString(int scoAudioState) { 139 switch (scoAudioState) { 140 case SCO_STATE_INACTIVE: 141 return "SCO_STATE_INACTIVE"; 142 case SCO_STATE_ACTIVATE_REQ: 143 return "SCO_STATE_ACTIVATE_REQ"; 144 case SCO_STATE_ACTIVE_EXTERNAL: 145 return "SCO_STATE_ACTIVE_EXTERNAL"; 146 case SCO_STATE_ACTIVE_INTERNAL: 147 return "SCO_STATE_ACTIVE_INTERNAL"; 148 case SCO_STATE_DEACTIVATING: 149 return "SCO_STATE_DEACTIVATING"; 150 default: 151 return "SCO_STATE_(" + scoAudioState + ")"; 152 } 153 } 154 155 //---------------------------------------------------------------------- 156 /*package*/ static class BluetoothA2dpDeviceInfo { 157 private final @NonNull BluetoothDevice mBtDevice; 158 private final int mVolume; 159 private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec; 160 BluetoothA2dpDeviceInfo(@onNull BluetoothDevice btDevice)161 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) { 162 this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT); 163 } 164 BluetoothA2dpDeviceInfo(@onNull BluetoothDevice btDevice, int volume, int codec)165 BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) { 166 mBtDevice = btDevice; 167 mVolume = volume; 168 mCodec = codec; 169 } 170 getBtDevice()171 public @NonNull BluetoothDevice getBtDevice() { 172 return mBtDevice; 173 } 174 getVolume()175 public int getVolume() { 176 return mVolume; 177 } 178 getCodec()179 public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() { 180 return mCodec; 181 } 182 183 // redefine equality op so we can match messages intended for this device 184 @Override equals(Object o)185 public boolean equals(Object o) { 186 if (o == null) { 187 return false; 188 } 189 if (this == o) { 190 return true; 191 } 192 if (o instanceof BluetoothA2dpDeviceInfo) { 193 return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice()); 194 } 195 return false; 196 } 197 198 199 } 200 201 // A2DP device events 202 /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0; 203 /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1; 204 a2dpDeviceEventToString(int event)205 /*package*/ static String a2dpDeviceEventToString(int event) { 206 switch (event) { 207 case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE"; 208 case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE"; 209 default: 210 return new String("invalid event:" + event); 211 } 212 } 213 getName(@onNull BluetoothDevice device)214 /*package*/ @NonNull static String getName(@NonNull BluetoothDevice device) { 215 final String deviceName = device.getName(); 216 if (deviceName == null) { 217 return ""; 218 } 219 return deviceName; 220 } 221 222 //---------------------------------------------------------------------- 223 // Interface for AudioDeviceBroker 224 225 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 226 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") onSystemReady()227 /*package*/ synchronized void onSystemReady() { 228 mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; 229 resetBluetoothSco(); 230 getBluetoothHeadset(); 231 232 //FIXME: this is to maintain compatibility with deprecated intent 233 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 234 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 235 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 236 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 237 sendStickyBroadcastToAll(newIntent); 238 239 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 240 if (adapter != null) { 241 adapter.getProfileProxy(mDeviceBroker.getContext(), 242 mBluetoothProfileServiceListener, BluetoothProfile.A2DP); 243 adapter.getProfileProxy(mDeviceBroker.getContext(), 244 mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); 245 } 246 } 247 onAudioServerDiedRestoreA2dp()248 /*package*/ synchronized void onAudioServerDiedRestoreA2dp() { 249 final int forMed = mDeviceBroker.getBluetoothA2dpEnabled() 250 ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP; 251 mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()"); 252 } 253 isAvrcpAbsoluteVolumeSupported()254 /*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() { 255 return (mA2dp != null && mAvrcpAbsVolSupported); 256 } 257 setAvrcpAbsoluteVolumeSupported(boolean supported)258 /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) { 259 mAvrcpAbsVolSupported = supported; 260 Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported); 261 } 262 setAvrcpAbsoluteVolumeIndex(int index)263 /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) { 264 if (mA2dp == null) { 265 if (AudioService.DEBUG_VOL) { 266 AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent( 267 "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG)); 268 return; 269 } 270 } 271 if (!mAvrcpAbsVolSupported) { 272 AudioService.sVolumeLogger.log(new AudioEventLogger.StringEvent( 273 "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG)); 274 return; 275 } 276 if (AudioService.DEBUG_VOL) { 277 Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index); 278 } 279 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( 280 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index)); 281 mA2dp.setAvrcpAbsoluteVolume(index); 282 } 283 getA2dpCodec( @onNull BluetoothDevice device)284 /*package*/ synchronized @AudioSystem.AudioFormatNativeEnumForBtCodec int getA2dpCodec( 285 @NonNull BluetoothDevice device) { 286 if (mA2dp == null) { 287 return AudioSystem.AUDIO_FORMAT_DEFAULT; 288 } 289 final BluetoothCodecStatus btCodecStatus = mA2dp.getCodecStatus(device); 290 if (btCodecStatus == null) { 291 return AudioSystem.AUDIO_FORMAT_DEFAULT; 292 } 293 final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig(); 294 if (btCodecConfig == null) { 295 return AudioSystem.AUDIO_FORMAT_DEFAULT; 296 } 297 return AudioSystem.bluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); 298 } 299 300 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 301 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") receiveBtEvent(Intent intent)302 /*package*/ synchronized void receiveBtEvent(Intent intent) { 303 final String action = intent.getAction(); 304 if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { 305 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 306 setBtScoActiveDevice(btDevice); 307 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 308 boolean broadcast = false; 309 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; 310 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 311 // broadcast intent if the connection was initated by AudioService 312 if (!mScoClients.isEmpty() 313 && (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL 314 || mScoAudioState == SCO_STATE_ACTIVATE_REQ 315 || mScoAudioState == SCO_STATE_DEACTIVATE_REQ 316 || mScoAudioState == SCO_STATE_DEACTIVATING)) { 317 broadcast = true; 318 } 319 switch (btState) { 320 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 321 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; 322 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL 323 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 324 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 325 } 326 mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent"); 327 break; 328 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 329 mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent"); 330 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 331 // There are two cases where we want to immediately reconnect audio: 332 // 1) If a new start request was received while disconnecting: this was 333 // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ. 334 // 2) If audio was connected then disconnected via Bluetooth APIs and 335 // we still have pending activation requests by apps: this is indicated by 336 // state SCO_STATE_ACTIVE_EXTERNAL and the mScoClients list not empty. 337 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ 338 || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL 339 && !mScoClients.isEmpty())) { 340 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null 341 && connectBluetoothScoAudioHelper(mBluetoothHeadset, 342 mBluetoothHeadsetDevice, mScoAudioMode)) { 343 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 344 broadcast = false; 345 break; 346 } 347 } 348 // Tear down SCO if disconnected from external 349 if (mScoAudioState == SCO_STATE_DEACTIVATING) { 350 clearAllScoClients(0, false); 351 } 352 mScoAudioState = SCO_STATE_INACTIVE; 353 break; 354 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 355 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL 356 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 357 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 358 } 359 broadcast = false; 360 break; 361 default: 362 // do not broadcast CONNECTING or invalid state 363 broadcast = false; 364 break; 365 } 366 if (broadcast) { 367 broadcastScoConnectionState(scoAudioState); 368 //FIXME: this is to maintain compatibility with deprecated intent 369 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 370 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 371 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); 372 sendStickyBroadcastToAll(newIntent); 373 } 374 } 375 } 376 377 /** 378 * 379 * @return false if SCO isn't connected 380 */ isBluetoothScoOn()381 /*package*/ synchronized boolean isBluetoothScoOn() { 382 if (mBluetoothHeadset == null) { 383 return false; 384 } 385 return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 386 == BluetoothHeadset.STATE_AUDIO_CONNECTED; 387 } 388 389 /** 390 * Disconnect all SCO connections started by {@link AudioManager} except those started by 391 * {@param exceptPid} 392 * 393 * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept 394 */ 395 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 396 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") disconnectBluetoothSco(int exceptPid)397 /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { 398 checkScoAudioState(); 399 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { 400 return; 401 } 402 clearAllScoClients(exceptPid, true); 403 } 404 405 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 406 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") startBluetoothScoForClient(IBinder cb, int scoAudioMode, @NonNull String eventSource)407 /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, 408 @NonNull String eventSource) { 409 ScoClient client = getScoClient(cb, true); 410 // The calling identity must be cleared before calling ScoClient.incCount(). 411 // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs 412 // and this must be done on behalf of system server to make sure permissions are granted. 413 // The caller identity must be cleared after getScoClient() because it is needed if a new 414 // client is created. 415 final long ident = Binder.clearCallingIdentity(); 416 try { 417 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); 418 client.requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); 419 } catch (NullPointerException e) { 420 Log.e(TAG, "Null ScoClient", e); 421 } 422 Binder.restoreCallingIdentity(ident); 423 } 424 425 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 426 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource)427 /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, 428 @NonNull String eventSource) { 429 ScoClient client = getScoClient(cb, false); 430 // The calling identity must be cleared before calling ScoClient.decCount(). 431 // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs 432 // and this must be done on behalf of system server to make sure permissions are granted. 433 final long ident = Binder.clearCallingIdentity(); 434 if (client != null) { 435 stopAndRemoveClient(client, eventSource); 436 } 437 Binder.restoreCallingIdentity(ident); 438 } 439 440 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 441 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") stopBluetoothScoForPid(int pid)442 /*package*/ synchronized void stopBluetoothScoForPid(int pid) { 443 ScoClient client = getScoClientForPid(pid); 444 if (client == null) { 445 return; 446 } 447 final String eventSource = new StringBuilder("stopBluetoothScoForPid(") 448 .append(pid).append(")").toString(); 449 stopAndRemoveClient(client, eventSource); 450 } 451 452 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") 453 // @GuardedBy("BtHelper.this") stopAndRemoveClient(ScoClient client, @NonNull String eventSource)454 private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) { 455 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource)); 456 client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 457 SCO_MODE_VIRTUAL_CALL); 458 // If a disconnection is pending, the client will be removed when clearAllScoClients() 459 // is called form receiveBtEvent() 460 if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ 461 && mScoAudioState != SCO_STATE_DEACTIVATING) { 462 client.remove(false /*stop */, true /*unregister*/); 463 } 464 } 465 setHearingAidVolume(int index, int streamType)466 /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { 467 if (mHearingAid == null) { 468 if (AudioService.DEBUG_VOL) { 469 Log.i(TAG, "setHearingAidVolume: null mHearingAid"); 470 } 471 return; 472 } 473 //hearing aid expect volume value in range -128dB to 0dB 474 int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, 475 AudioSystem.DEVICE_OUT_HEARING_AID); 476 if (gainDB < BT_HEARING_AID_GAIN_MIN) { 477 gainDB = BT_HEARING_AID_GAIN_MIN; 478 } 479 if (AudioService.DEBUG_VOL) { 480 Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" 481 + index + " gain=" + gainDB); 482 } 483 AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( 484 AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); 485 mHearingAid.setVolume(gainDB); 486 } 487 onBroadcastScoConnectionState(int state)488 /*package*/ synchronized void onBroadcastScoConnectionState(int state) { 489 if (state == mScoConnectionState) { 490 return; 491 } 492 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); 493 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 494 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, 495 mScoConnectionState); 496 sendStickyBroadcastToAll(newIntent); 497 mScoConnectionState = state; 498 } 499 disconnectAllBluetoothProfiles()500 /*package*/ synchronized void disconnectAllBluetoothProfiles() { 501 mDeviceBroker.postDisconnectA2dp(); 502 mDeviceBroker.postDisconnectA2dpSink(); 503 mDeviceBroker.postDisconnectHeadset(); 504 mDeviceBroker.postDisconnectHearingAid(); 505 } 506 507 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 508 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") resetBluetoothSco()509 /*package*/ synchronized void resetBluetoothSco() { 510 clearAllScoClients(0, false); 511 mScoAudioState = SCO_STATE_INACTIVE; 512 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 513 AudioSystem.setParameters("A2dpSuspended=false"); 514 mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); 515 } 516 517 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 518 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") disconnectHeadset()519 /*package*/ synchronized void disconnectHeadset() { 520 setBtScoActiveDevice(null); 521 mBluetoothHeadset = null; 522 } 523 onA2dpProfileConnected(BluetoothA2dp a2dp)524 /*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) { 525 mA2dp = a2dp; 526 final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices(); 527 if (deviceList.isEmpty()) { 528 return; 529 } 530 final BluetoothDevice btDevice = deviceList.get(0); 531 // the device is guaranteed CONNECTED 532 mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice, 533 BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1); 534 } 535 onA2dpSinkProfileConnected(BluetoothProfile profile)536 /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { 537 final List<BluetoothDevice> deviceList = profile.getConnectedDevices(); 538 if (deviceList.isEmpty()) { 539 return; 540 } 541 final BluetoothDevice btDevice = deviceList.get(0); 542 final @BluetoothProfile.BtProfileState int state = 543 profile.getConnectionState(btDevice); 544 mDeviceBroker.postSetA2dpSourceConnectionState( 545 state, new BluetoothA2dpDeviceInfo(btDevice)); 546 } 547 onHearingAidProfileConnected(BluetoothHearingAid hearingAid)548 /*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) { 549 mHearingAid = hearingAid; 550 final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices(); 551 if (deviceList.isEmpty()) { 552 return; 553 } 554 final BluetoothDevice btDevice = deviceList.get(0); 555 final @BluetoothProfile.BtProfileState int state = 556 mHearingAid.getConnectionState(btDevice); 557 mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( 558 btDevice, state, 559 /*suppressNoisyIntent*/ false, 560 /*musicDevice*/ android.media.AudioSystem.DEVICE_NONE, 561 /*eventSource*/ "mBluetoothProfileServiceListener"); 562 } 563 564 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 565 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") onHeadsetProfileConnected(BluetoothHeadset headset)566 /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { 567 // Discard timeout message 568 mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); 569 mBluetoothHeadset = headset; 570 setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice()); 571 // Refresh SCO audio state 572 checkScoAudioState(); 573 if (mScoAudioState != SCO_STATE_ACTIVATE_REQ 574 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 575 return; 576 } 577 boolean status = false; 578 if (mBluetoothHeadsetDevice != null) { 579 switch (mScoAudioState) { 580 case SCO_STATE_ACTIVATE_REQ: 581 status = connectBluetoothScoAudioHelper( 582 mBluetoothHeadset, 583 mBluetoothHeadsetDevice, mScoAudioMode); 584 if (status) { 585 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 586 } 587 break; 588 case SCO_STATE_DEACTIVATE_REQ: 589 status = disconnectBluetoothScoAudioHelper( 590 mBluetoothHeadset, 591 mBluetoothHeadsetDevice, mScoAudioMode); 592 if (status) { 593 mScoAudioState = SCO_STATE_DEACTIVATING; 594 } 595 break; 596 } 597 } 598 if (!status) { 599 mScoAudioState = SCO_STATE_INACTIVE; 600 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 601 } 602 } 603 604 //---------------------------------------------------------------------- broadcastScoConnectionState(int state)605 private void broadcastScoConnectionState(int state) { 606 mDeviceBroker.postBroadcastScoConnectionState(state); 607 } 608 handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive)609 private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { 610 if (btDevice == null) { 611 return true; 612 } 613 String address = btDevice.getAddress(); 614 BluetoothClass btClass = btDevice.getBluetoothClass(); 615 int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; 616 int[] outDeviceTypes = { 617 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, 618 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 619 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT 620 }; 621 if (btClass != null) { 622 switch (btClass.getDeviceClass()) { 623 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 624 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 625 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET }; 626 break; 627 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 628 outDeviceTypes = new int[] { AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT }; 629 break; 630 } 631 } 632 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 633 address = ""; 634 } 635 String btDeviceName = getName(btDevice); 636 boolean result = false; 637 if (isActive) { 638 result |= mDeviceBroker.handleDeviceConnection( 639 isActive, outDeviceTypes[0], address, btDeviceName); 640 } else { 641 for (int outDeviceType : outDeviceTypes) { 642 result |= mDeviceBroker.handleDeviceConnection( 643 isActive, outDeviceType, address, btDeviceName); 644 } 645 } 646 // handleDeviceConnection() && result to make sure the method get executed 647 result = mDeviceBroker.handleDeviceConnection( 648 isActive, inDevice, address, btDeviceName) && result; 649 return result; 650 } 651 652 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 653 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") 654 @GuardedBy("BtHelper.this") setBtScoActiveDevice(BluetoothDevice btDevice)655 private void setBtScoActiveDevice(BluetoothDevice btDevice) { 656 Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); 657 final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; 658 if (Objects.equals(btDevice, previousActiveDevice)) { 659 return; 660 } 661 if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { 662 Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device " 663 + previousActiveDevice); 664 } 665 if (!handleBtScoActiveDeviceChange(btDevice, true)) { 666 Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice); 667 // set mBluetoothHeadsetDevice to null when failing to add new device 668 btDevice = null; 669 } 670 mBluetoothHeadsetDevice = btDevice; 671 if (mBluetoothHeadsetDevice == null) { 672 resetBluetoothSco(); 673 } 674 } 675 676 // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async 677 // methods inside listener. 678 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 679 new BluetoothProfile.ServiceListener() { 680 public void onServiceConnected(int profile, BluetoothProfile proxy) { 681 switch(profile) { 682 case BluetoothProfile.A2DP: 683 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( 684 "BT profile service: connecting A2DP profile")); 685 mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy); 686 break; 687 688 case BluetoothProfile.A2DP_SINK: 689 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( 690 "BT profile service: connecting A2DP_SINK profile")); 691 mDeviceBroker.postBtA2dpSinkProfileConnected(proxy); 692 break; 693 694 case BluetoothProfile.HEADSET: 695 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( 696 "BT profile service: connecting HEADSET profile")); 697 mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy); 698 break; 699 700 case BluetoothProfile.HEARING_AID: 701 AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( 702 "BT profile service: connecting HEARING_AID profile")); 703 mDeviceBroker.postBtHearingAidProfileConnected( 704 (BluetoothHearingAid) proxy); 705 break; 706 default: 707 break; 708 } 709 } 710 public void onServiceDisconnected(int profile) { 711 712 switch (profile) { 713 case BluetoothProfile.A2DP: 714 mDeviceBroker.postDisconnectA2dp(); 715 break; 716 717 case BluetoothProfile.A2DP_SINK: 718 mDeviceBroker.postDisconnectA2dpSink(); 719 break; 720 721 case BluetoothProfile.HEADSET: 722 mDeviceBroker.postDisconnectHeadset(); 723 break; 724 725 case BluetoothProfile.HEARING_AID: 726 mDeviceBroker.postDisconnectHearingAid(); 727 break; 728 729 default: 730 break; 731 } 732 } 733 }; 734 735 //---------------------------------------------------------------------- 736 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 737 @GuardedBy("AudioDeviceBroker.mDeviceStateLock") scoClientDied(Object obj)738 /*package*/ synchronized void scoClientDied(Object obj) { 739 final ScoClient client = (ScoClient) obj; 740 client.remove(true /*stop*/, false /*unregister*/); 741 Log.w(TAG, "SCO client died"); 742 } 743 744 private class ScoClient implements IBinder.DeathRecipient { 745 private IBinder mCb; // To be notified of client's death 746 private int mCreatorPid; 747 ScoClient(IBinder cb)748 ScoClient(IBinder cb) { 749 mCb = cb; 750 mCreatorPid = Binder.getCallingPid(); 751 } 752 registerDeathRecipient()753 public void registerDeathRecipient() { 754 try { 755 mCb.linkToDeath(this, 0); 756 } catch (RemoteException e) { 757 Log.w(TAG, "ScoClient could not link to " + mCb + " binder death"); 758 } 759 } 760 unregisterDeathRecipient()761 public void unregisterDeathRecipient() { 762 try { 763 mCb.unlinkToDeath(this, 0); 764 } catch (NoSuchElementException e) { 765 Log.w(TAG, "ScoClient could not not unregistered to binder"); 766 } 767 } 768 769 @Override binderDied()770 public void binderDied() { 771 // process this from DeviceBroker's message queue to take the right locks since 772 // this event can impact SCO mode and requires querying audio mode stack 773 mDeviceBroker.postScoClientDied(this); 774 } 775 getBinder()776 IBinder getBinder() { 777 return mCb; 778 } 779 getPid()780 int getPid() { 781 return mCreatorPid; 782 } 783 784 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 785 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") 786 @GuardedBy("BtHelper.this") requestScoState(int state, int scoAudioMode)787 private boolean requestScoState(int state, int scoAudioMode) { 788 checkScoAudioState(); 789 if (mScoClients.size() != 1) { 790 Log.i(TAG, "requestScoState: state=" + state + ", scoAudioMode=" + scoAudioMode 791 + ", num SCO clients=" + mScoClients.size()); 792 return true; 793 } 794 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 795 // Make sure that the state transitions to CONNECTING even if we cannot initiate 796 // the connection. 797 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); 798 // Accept SCO audio activation only in NORMAL audio mode or if the mode is 799 // currently controlled by the same client process. 800 final int modeOwnerPid = mDeviceBroker.getModeOwnerPid(); 801 if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { 802 Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " 803 + modeOwnerPid + " != creatorPid " + mCreatorPid); 804 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 805 return false; 806 } 807 switch (mScoAudioState) { 808 case SCO_STATE_INACTIVE: 809 mScoAudioMode = scoAudioMode; 810 if (scoAudioMode == SCO_MODE_UNDEFINED) { 811 mScoAudioMode = SCO_MODE_VIRTUAL_CALL; 812 if (mBluetoothHeadsetDevice != null) { 813 mScoAudioMode = Settings.Global.getInt( 814 mDeviceBroker.getContentResolver(), 815 "bluetooth_sco_channel_" 816 + mBluetoothHeadsetDevice.getAddress(), 817 SCO_MODE_VIRTUAL_CALL); 818 if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { 819 mScoAudioMode = SCO_MODE_VIRTUAL_CALL; 820 } 821 } 822 } 823 if (mBluetoothHeadset == null) { 824 if (getBluetoothHeadset()) { 825 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 826 } else { 827 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" 828 + " connection, mScoAudioMode=" + mScoAudioMode); 829 broadcastScoConnectionState( 830 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 831 return false; 832 } 833 break; 834 } 835 if (mBluetoothHeadsetDevice == null) { 836 Log.w(TAG, "requestScoState: no active device while connecting," 837 + " mScoAudioMode=" + mScoAudioMode); 838 broadcastScoConnectionState( 839 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 840 return false; 841 } 842 if (connectBluetoothScoAudioHelper(mBluetoothHeadset, 843 mBluetoothHeadsetDevice, mScoAudioMode)) { 844 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 845 } else { 846 Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice 847 + " failed, mScoAudioMode=" + mScoAudioMode); 848 broadcastScoConnectionState( 849 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 850 return false; 851 } 852 break; 853 case SCO_STATE_DEACTIVATING: 854 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 855 break; 856 case SCO_STATE_DEACTIVATE_REQ: 857 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 858 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 859 break; 860 case SCO_STATE_ACTIVE_INTERNAL: 861 Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return"); 862 break; 863 default: 864 Log.w(TAG, "requestScoState: failed to connect in state " 865 + mScoAudioState + ", scoAudioMode=" + scoAudioMode); 866 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 867 return false; 868 } 869 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 870 switch (mScoAudioState) { 871 case SCO_STATE_ACTIVE_INTERNAL: 872 if (mBluetoothHeadset == null) { 873 if (getBluetoothHeadset()) { 874 mScoAudioState = SCO_STATE_DEACTIVATE_REQ; 875 } else { 876 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" 877 + " disconnection, mScoAudioMode=" + mScoAudioMode); 878 mScoAudioState = SCO_STATE_INACTIVE; 879 broadcastScoConnectionState( 880 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 881 return false; 882 } 883 break; 884 } 885 if (mBluetoothHeadsetDevice == null) { 886 mScoAudioState = SCO_STATE_INACTIVE; 887 broadcastScoConnectionState( 888 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 889 break; 890 } 891 if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset, 892 mBluetoothHeadsetDevice, mScoAudioMode)) { 893 mScoAudioState = SCO_STATE_DEACTIVATING; 894 } else { 895 mScoAudioState = SCO_STATE_INACTIVE; 896 broadcastScoConnectionState( 897 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 898 } 899 break; 900 case SCO_STATE_ACTIVATE_REQ: 901 mScoAudioState = SCO_STATE_INACTIVE; 902 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 903 break; 904 default: 905 Log.w(TAG, "requestScoState: failed to disconnect in state " 906 + mScoAudioState + ", scoAudioMode=" + scoAudioMode); 907 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 908 return false; 909 } 910 } 911 return true; 912 } 913 914 @GuardedBy("BtHelper.this") remove(boolean stop, boolean unregister)915 void remove(boolean stop, boolean unregister) { 916 if (unregister) { 917 unregisterDeathRecipient(); 918 } 919 if (stop) { 920 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 921 SCO_MODE_VIRTUAL_CALL); 922 } 923 mScoClients.remove(this); 924 } 925 } 926 927 //----------------------------------------------------- 928 // Utilities sendStickyBroadcastToAll(Intent intent)929 private void sendStickyBroadcastToAll(Intent intent) { 930 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 931 final long ident = Binder.clearCallingIdentity(); 932 try { 933 mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); 934 } finally { 935 Binder.restoreCallingIdentity(ident); 936 } 937 } 938 disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)939 private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, 940 BluetoothDevice device, int scoAudioMode) { 941 switch (scoAudioMode) { 942 case SCO_MODE_RAW: 943 return bluetoothHeadset.disconnectAudio(); 944 case SCO_MODE_VIRTUAL_CALL: 945 return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); 946 case SCO_MODE_VR: 947 return bluetoothHeadset.stopVoiceRecognition(device); 948 default: 949 return false; 950 } 951 } 952 connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)953 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, 954 BluetoothDevice device, int scoAudioMode) { 955 switch (scoAudioMode) { 956 case SCO_MODE_RAW: 957 return bluetoothHeadset.connectAudio(); 958 case SCO_MODE_VIRTUAL_CALL: 959 return bluetoothHeadset.startScoUsingVirtualVoiceCall(); 960 case SCO_MODE_VR: 961 return bluetoothHeadset.startVoiceRecognition(device); 962 default: 963 return false; 964 } 965 } 966 checkScoAudioState()967 private void checkScoAudioState() { 968 if (mBluetoothHeadset != null 969 && mBluetoothHeadsetDevice != null 970 && mScoAudioState == SCO_STATE_INACTIVE 971 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 972 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 973 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 974 } 975 } 976 977 978 @GuardedBy("BtHelper.this") getScoClient(IBinder cb, boolean create)979 private ScoClient getScoClient(IBinder cb, boolean create) { 980 for (ScoClient existingClient : mScoClients) { 981 if (existingClient.getBinder() == cb) { 982 return existingClient; 983 } 984 } 985 if (create) { 986 ScoClient newClient = new ScoClient(cb); 987 newClient.registerDeathRecipient(); 988 mScoClients.add(newClient); 989 return newClient; 990 } 991 return null; 992 } 993 994 @GuardedBy("BtHelper.this") getScoClientForPid(int pid)995 private ScoClient getScoClientForPid(int pid) { 996 for (ScoClient cl : mScoClients) { 997 if (cl.getPid() == pid) { 998 return cl; 999 } 1000 } 1001 return null; 1002 } 1003 1004 // @GuardedBy("AudioDeviceBroker.mSetModeLock") 1005 //@GuardedBy("AudioDeviceBroker.mDeviceStateLock") 1006 @GuardedBy("BtHelper.this") clearAllScoClients(int exceptPid, boolean stopSco)1007 private void clearAllScoClients(int exceptPid, boolean stopSco) { 1008 final ArrayList<ScoClient> clients = new ArrayList<ScoClient>(); 1009 for (ScoClient cl : mScoClients) { 1010 if (cl.getPid() != exceptPid) { 1011 clients.add(cl); 1012 } 1013 } 1014 for (ScoClient cl : clients) { 1015 cl.remove(stopSco, true /*unregister*/); 1016 } 1017 1018 } 1019 getBluetoothHeadset()1020 private boolean getBluetoothHeadset() { 1021 boolean result = false; 1022 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1023 if (adapter != null) { 1024 result = adapter.getProfileProxy(mDeviceBroker.getContext(), 1025 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); 1026 } 1027 // If we could not get a bluetooth headset proxy, send a failure message 1028 // without delay to reset the SCO audio state and clear SCO clients. 1029 // If we could get a proxy, send a delayed failure message that will reset our state 1030 // in case we don't receive onServiceConnected(). 1031 mDeviceBroker.handleFailureToConnectToBtHeadsetService( 1032 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0); 1033 return result; 1034 } 1035 1036 /** 1037 * Returns the String equivalent of the btCodecType. 1038 * 1039 * This uses an "ENCODING_" prefix for consistency with Audio; 1040 * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. 1041 */ bluetoothCodecToEncodingString(int btCodecType)1042 public static String bluetoothCodecToEncodingString(int btCodecType) { 1043 switch (btCodecType) { 1044 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: 1045 return "ENCODING_SBC"; 1046 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: 1047 return "ENCODING_AAC"; 1048 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: 1049 return "ENCODING_APTX"; 1050 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: 1051 return "ENCODING_APTX_HD"; 1052 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: 1053 return "ENCODING_LDAC"; 1054 default: 1055 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; 1056 } 1057 } 1058 1059 //------------------------------------------------------------ dump(PrintWriter pw, String prefix)1060 /*package*/ void dump(PrintWriter pw, String prefix) { 1061 pw.println("\n" + prefix + "mBluetoothHeadset: " + mBluetoothHeadset); 1062 pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice); 1063 pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState)); 1064 pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode)); 1065 pw.println(prefix + "Sco clients:"); 1066 mScoClients.forEach((cl) -> { 1067 pw.println(" " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); }); 1068 1069 pw.println("\n" + prefix + "mHearingAid: " + mHearingAid); 1070 pw.println(prefix + "mA2dp: " + mA2dp); 1071 pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported); 1072 } 1073 1074 } 1075