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