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