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 static android.bluetooth.BluetoothDevice.DEVICE_TYPE_DEFAULT; 19 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET; 20 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_WATCH; 21 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_CARKIT; 22 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEADPHONES; 23 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_HEARING_AID; 24 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_RECEIVER; 25 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER; 26 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; 27 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_WATCH; 28 import static android.media.audio.Flags.automaticBtDeviceType; 29 30 import android.annotation.NonNull; 31 import android.annotation.Nullable; 32 import android.bluetooth.BluetoothA2dp; 33 import android.bluetooth.BluetoothAdapter; 34 import android.bluetooth.BluetoothClass; 35 import android.bluetooth.BluetoothCodecConfig; 36 import android.bluetooth.BluetoothCodecStatus; 37 import android.bluetooth.BluetoothDevice; 38 import android.bluetooth.BluetoothHeadset; 39 import android.bluetooth.BluetoothHearingAid; 40 import android.bluetooth.BluetoothLeAudio; 41 import android.bluetooth.BluetoothLeAudioCodecConfig; 42 import android.bluetooth.BluetoothLeAudioCodecStatus; 43 import android.bluetooth.BluetoothProfile; 44 import android.content.Context; 45 import android.content.Intent; 46 import android.media.AudioDeviceAttributes; 47 import android.media.AudioManager; 48 import android.media.AudioManager.AudioDeviceCategory; 49 import android.media.AudioSystem; 50 import android.media.BluetoothProfileConnectionInfo; 51 import android.os.Binder; 52 import android.os.Bundle; 53 import android.os.UserHandle; 54 import android.provider.Settings; 55 import android.text.TextUtils; 56 import android.util.Log; 57 import android.util.Pair; 58 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.server.utils.EventLogger; 61 62 import java.io.PrintWriter; 63 import java.util.ArrayList; 64 import java.util.HashMap; 65 import java.util.List; 66 import java.util.Map; 67 import java.util.Objects; 68 69 /** 70 * @hide 71 * Class to encapsulate all communication with Bluetooth services 72 */ 73 public class BtHelper { 74 75 private static final String TAG = "AS.BtHelper"; 76 77 private final @NonNull AudioDeviceBroker mDeviceBroker; 78 private final @NonNull Context mContext; 79 BtHelper(@onNull AudioDeviceBroker broker, Context context)80 BtHelper(@NonNull AudioDeviceBroker broker, Context context) { 81 mDeviceBroker = broker; 82 mContext = context; 83 } 84 85 // BluetoothHeadset API to control SCO connection 86 private @Nullable BluetoothHeadset mBluetoothHeadset; 87 88 // Bluetooth headset device 89 private @Nullable BluetoothDevice mBluetoothHeadsetDevice; 90 private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices = 91 new HashMap<>(); 92 93 private @Nullable BluetoothHearingAid mHearingAid = null; 94 95 private @Nullable BluetoothLeAudio mLeAudio = null; 96 97 private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig; 98 99 // Reference to BluetoothA2dp to query for AbsoluteVolume. 100 private @Nullable BluetoothA2dp mA2dp = null; 101 102 private @Nullable BluetoothCodecConfig mA2dpCodecConfig; 103 104 private @AudioSystem.AudioFormatNativeEnumForBtCodec 105 int mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; 106 107 // If absolute volume is supported in AVRCP device 108 private boolean mAvrcpAbsVolSupported = false; 109 110 // Current connection state indicated by bluetooth headset 111 private int mScoConnectionState; 112 113 // Indicate if SCO audio connection is currently active and if the initiator is 114 // audio service (internal) or bluetooth headset (external) 115 private int mScoAudioState; 116 117 // Indicates the mode used for SCO audio connection. The mode is virtual call if the request 118 // originated from an app targeting an API version before JB MR2 and raw audio after that. 119 private int mScoAudioMode; 120 121 // SCO audio state is not active 122 private static final int SCO_STATE_INACTIVE = 0; 123 // SCO audio activation request waiting for headset service to connect 124 private static final int SCO_STATE_ACTIVATE_REQ = 1; 125 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 126 // in call audio) 127 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 128 // SCO audio state is active or starting due to a request from AudioManager API 129 private static final int SCO_STATE_ACTIVE_INTERNAL = 3; 130 // SCO audio deactivation request waiting for headset service to connect 131 private static final int SCO_STATE_DEACTIVATE_REQ = 4; 132 // SCO audio deactivation in progress, waiting for Bluetooth audio intent 133 private static final int SCO_STATE_DEACTIVATING = 5; 134 135 // SCO audio mode is undefined 136 /*package*/ static final int SCO_MODE_UNDEFINED = -1; 137 // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall()) 138 /*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0; 139 // SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition()) 140 private static final int SCO_MODE_VR = 2; 141 // max valid SCO audio mode values 142 private static final int SCO_MODE_MAX = 2; 143 144 private static final int BT_HEARING_AID_GAIN_MIN = -128; 145 private static final int BT_LE_AUDIO_MIN_VOL = 0; 146 private static final int BT_LE_AUDIO_MAX_VOL = 255; 147 148 // BtDevice constants currently rolling out under flag protection. Use own 149 // constants instead to avoid mainline dependency from flag library import 150 // TODO(b/335936458): remove once the BtDevice flag is rolled out 151 private static final String DEVICE_TYPE_SPEAKER = "Speaker"; 152 private static final String DEVICE_TYPE_HEADSET = "Headset"; 153 private static final String DEVICE_TYPE_CARKIT = "Carkit"; 154 private static final String DEVICE_TYPE_HEARING_AID = "HearingAid"; 155 156 /** 157 * Returns a string representation of the scoAudioMode. 158 */ scoAudioModeToString(int scoAudioMode)159 public static String scoAudioModeToString(int scoAudioMode) { 160 switch (scoAudioMode) { 161 case SCO_MODE_UNDEFINED: 162 return "SCO_MODE_UNDEFINED"; 163 case SCO_MODE_VIRTUAL_CALL: 164 return "SCO_MODE_VIRTUAL_CALL"; 165 case SCO_MODE_VR: 166 return "SCO_MODE_VR"; 167 default: 168 return "SCO_MODE_(" + scoAudioMode + ")"; 169 } 170 } 171 172 /** 173 * Returns a string representation of the scoAudioState. 174 */ scoAudioStateToString(int scoAudioState)175 public static String scoAudioStateToString(int scoAudioState) { 176 switch (scoAudioState) { 177 case SCO_STATE_INACTIVE: 178 return "SCO_STATE_INACTIVE"; 179 case SCO_STATE_ACTIVATE_REQ: 180 return "SCO_STATE_ACTIVATE_REQ"; 181 case SCO_STATE_ACTIVE_EXTERNAL: 182 return "SCO_STATE_ACTIVE_EXTERNAL"; 183 case SCO_STATE_ACTIVE_INTERNAL: 184 return "SCO_STATE_ACTIVE_INTERNAL"; 185 case SCO_STATE_DEACTIVATING: 186 return "SCO_STATE_DEACTIVATING"; 187 default: 188 return "SCO_STATE_(" + scoAudioState + ")"; 189 } 190 } 191 192 // A2DP device events 193 /*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0; 194 deviceEventToString(int event)195 /*package*/ static String deviceEventToString(int event) { 196 switch (event) { 197 case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE"; 198 default: 199 return new String("invalid event:" + event); 200 } 201 } 202 getName(@onNull BluetoothDevice device)203 /*package*/ @NonNull static String getName(@NonNull BluetoothDevice device) { 204 final String deviceName = device.getName(); 205 if (deviceName == null) { 206 return ""; 207 } 208 return deviceName; 209 } 210 211 //---------------------------------------------------------------------- 212 // Interface for AudioDeviceBroker 213 214 // @GuardedBy("mDeviceBroker.mSetModeLock") 215 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onSystemReady()216 /*package*/ synchronized void onSystemReady() { 217 mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; 218 resetBluetoothSco(); 219 getBluetoothHeadset(); 220 221 //FIXME: this is to maintain compatibility with deprecated intent 222 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 223 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 224 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 225 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 226 sendStickyBroadcastToAll(newIntent); 227 228 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 229 if (adapter != null) { 230 adapter.getProfileProxy(mDeviceBroker.getContext(), 231 mBluetoothProfileServiceListener, BluetoothProfile.A2DP); 232 adapter.getProfileProxy(mDeviceBroker.getContext(), 233 mBluetoothProfileServiceListener, BluetoothProfile.A2DP_SINK); 234 adapter.getProfileProxy(mDeviceBroker.getContext(), 235 mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); 236 adapter.getProfileProxy(mDeviceBroker.getContext(), 237 mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO); 238 adapter.getProfileProxy(mDeviceBroker.getContext(), 239 mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST); 240 } 241 } 242 onAudioServerDiedRestoreA2dp()243 /*package*/ synchronized void onAudioServerDiedRestoreA2dp() { 244 final int forMed = mDeviceBroker.getBluetoothA2dpEnabled() 245 ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP; 246 mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()"); 247 } 248 setAvrcpAbsoluteVolumeSupported(boolean supported)249 /*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) { 250 mAvrcpAbsVolSupported = supported; 251 Log.i(TAG, "setAvrcpAbsoluteVolumeSupported supported=" + supported); 252 } 253 setAvrcpAbsoluteVolumeIndex(int index)254 /*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) { 255 if (mA2dp == null) { 256 if (AudioService.DEBUG_VOL) { 257 AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent( 258 "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp").printLog(TAG)); 259 } 260 return; 261 } 262 if (!mAvrcpAbsVolSupported) { 263 AudioService.sVolumeLogger.enqueue(new EventLogger.StringEvent( 264 "setAvrcpAbsoluteVolumeIndex: abs vol not supported ").printLog(TAG)); 265 return; 266 } 267 if (AudioService.DEBUG_VOL) { 268 Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index); 269 } 270 AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent( 271 AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index)); 272 try { 273 mA2dp.setAvrcpAbsoluteVolume(index); 274 } catch (Exception e) { 275 Log.e(TAG, "Exception while changing abs volume", e); 276 } 277 } 278 getCodec( @onNull BluetoothDevice device, @AudioService.BtProfile int profile)279 private synchronized Pair<Integer, Boolean> getCodec( 280 @NonNull BluetoothDevice device, @AudioService.BtProfile int profile) { 281 282 switch (profile) { 283 case BluetoothProfile.A2DP: { 284 boolean changed = mA2dpCodecConfig != null; 285 if (mA2dp == null) { 286 mA2dpCodecConfig = null; 287 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 288 } 289 BluetoothCodecStatus btCodecStatus = null; 290 try { 291 btCodecStatus = mA2dp.getCodecStatus(device); 292 } catch (Exception e) { 293 Log.e(TAG, "Exception while getting status of " + device, e); 294 } 295 if (btCodecStatus == null) { 296 Log.e(TAG, "getCodec, null A2DP codec status for device: " + device); 297 mA2dpCodecConfig = null; 298 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 299 } 300 final BluetoothCodecConfig btCodecConfig = btCodecStatus.getCodecConfig(); 301 if (btCodecConfig == null) { 302 mA2dpCodecConfig = null; 303 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 304 } 305 changed = !btCodecConfig.equals(mA2dpCodecConfig); 306 mA2dpCodecConfig = btCodecConfig; 307 return new Pair<>(AudioSystem.bluetoothA2dpCodecToAudioFormat( 308 btCodecConfig.getCodecType()), changed); 309 } 310 case BluetoothProfile.LE_AUDIO: { 311 boolean changed = mLeAudioCodecConfig != null; 312 if (mLeAudio == null) { 313 mLeAudioCodecConfig = null; 314 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 315 } 316 BluetoothLeAudioCodecStatus btLeCodecStatus = null; 317 int groupId = mLeAudio.getGroupId(device); 318 try { 319 btLeCodecStatus = mLeAudio.getCodecStatus(groupId); 320 } catch (Exception e) { 321 Log.e(TAG, "Exception while getting status of " + device, e); 322 } 323 if (btLeCodecStatus == null) { 324 Log.e(TAG, "getCodec, null LE codec status for device: " + device); 325 mLeAudioCodecConfig = null; 326 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 327 } 328 BluetoothLeAudioCodecConfig btLeCodecConfig = 329 btLeCodecStatus.getOutputCodecConfig(); 330 if (btLeCodecConfig == null) { 331 mLeAudioCodecConfig = null; 332 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, changed); 333 } 334 changed = !btLeCodecConfig.equals(mLeAudioCodecConfig); 335 mLeAudioCodecConfig = btLeCodecConfig; 336 return new Pair<>(AudioSystem.bluetoothLeCodecToAudioFormat( 337 btLeCodecConfig.getCodecType()), changed); 338 } 339 case BluetoothProfile.LE_AUDIO_BROADCAST: { 340 // We assume LC3 for LE Audio broadcast codec as there is no API to get the codec 341 // config on LE Broadcast profile proxy. 342 boolean changed = mLeAudioBroadcastCodec != AudioSystem.AUDIO_FORMAT_LC3; 343 mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_LC3; 344 return new Pair<>(mLeAudioBroadcastCodec, changed); 345 } 346 default: 347 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false); 348 } 349 } 350 351 /*package*/ synchronized Pair<Integer, Boolean> getCodecWithFallback(@onNull BluetoothDevice device, @AudioService.BtProfile int profile, boolean isLeOutput, @NonNull String source)352 getCodecWithFallback(@NonNull BluetoothDevice device, 353 @AudioService.BtProfile int profile, 354 boolean isLeOutput, @NonNull String source) { 355 // For profiles other than A2DP and LE Audio output, the audio codec format must be 356 // AUDIO_FORMAT_DEFAULT as native audio policy manager expects a specific audio format 357 // only if audio HW module selection based on format is supported for the device type. 358 if (!(profile == BluetoothProfile.A2DP 359 || (isLeOutput && ((profile == BluetoothProfile.LE_AUDIO) 360 || (profile == BluetoothProfile.LE_AUDIO_BROADCAST))))) { 361 return new Pair<>(AudioSystem.AUDIO_FORMAT_DEFAULT, false); 362 } 363 Pair<Integer, Boolean> codecAndChanged = 364 getCodec(device, profile); 365 if (codecAndChanged.first == AudioSystem.AUDIO_FORMAT_DEFAULT) { 366 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 367 "getCodec DEFAULT from " + source + " fallback to " 368 + (profile == BluetoothProfile.A2DP ? "SBC" : "LC3"))); 369 return new Pair<>(profile == BluetoothProfile.A2DP 370 ? AudioSystem.AUDIO_FORMAT_SBC : AudioSystem.AUDIO_FORMAT_LC3, true); 371 } 372 373 return codecAndChanged; 374 } 375 376 // @GuardedBy("mDeviceBroker.mSetModeLock") 377 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onReceiveBtEvent(Intent intent)378 /*package*/ synchronized void onReceiveBtEvent(Intent intent) { 379 final String action = intent.getAction(); 380 381 Log.i(TAG, "onReceiveBtEvent action: " + action + " mScoAudioState: " + mScoAudioState); 382 if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { 383 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, 384 android.bluetooth.BluetoothDevice.class); 385 if (btDevice != null && !isProfilePoxyConnected(BluetoothProfile.HEADSET)) { 386 AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( 387 "onReceiveBtEvent ACTION_ACTIVE_DEVICE_CHANGED " 388 + "received with null profile proxy for device: " 389 + btDevice)).printLog(TAG)); 390 return; 391 } 392 onSetBtScoActiveDevice(btDevice); 393 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 394 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 395 onScoAudioStateChanged(btState); 396 } 397 } 398 399 /** 400 * Exclusively called from AudioDeviceBroker when handling MSG_L_RECEIVED_BT_EVENT 401 * as part of the serialization of the communication route selection 402 */ 403 // @GuardedBy("mDeviceBroker.mSetModeLock") 404 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onScoAudioStateChanged(int state)405 private void onScoAudioStateChanged(int state) { 406 boolean broadcast = false; 407 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; 408 if (mDeviceBroker.isScoManagedByAudio()) { 409 switch (state) { 410 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 411 mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged"); 412 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; 413 broadcast = true; 414 break; 415 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 416 mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged"); 417 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 418 broadcast = true; 419 break; 420 default: 421 break; 422 } 423 } else { 424 switch (state) { 425 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 426 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; 427 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL 428 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 429 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 430 } else if (mDeviceBroker.isBluetoothScoRequested()) { 431 // broadcast intent if the connection was initated by AudioService 432 broadcast = true; 433 } 434 mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged"); 435 break; 436 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 437 mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged"); 438 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 439 // There are two cases where we want to immediately reconnect audio: 440 // 1) If a new start request was received while disconnecting: this was 441 // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ. 442 // 2) If audio was connected then disconnected via Bluetooth APIs and 443 // we still have pending activation requests by apps: this is indicated by 444 // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested. 445 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) { 446 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null 447 && connectBluetoothScoAudioHelper(mBluetoothHeadset, 448 mBluetoothHeadsetDevice, mScoAudioMode)) { 449 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 450 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING; 451 broadcast = true; 452 break; 453 } 454 } 455 if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) { 456 broadcast = true; 457 } 458 mScoAudioState = SCO_STATE_INACTIVE; 459 break; 460 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 461 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL 462 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 463 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 464 } 465 break; 466 default: 467 break; 468 } 469 } 470 if (broadcast) { 471 broadcastScoConnectionState(scoAudioState); 472 //FIXME: this is to maintain compatibility with deprecated intent 473 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 474 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 475 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); 476 sendStickyBroadcastToAll(newIntent); 477 } 478 } 479 /** 480 * 481 * @return false if SCO isn't connected 482 */ isBluetoothScoOn()483 /*package*/ synchronized boolean isBluetoothScoOn() { 484 if (mBluetoothHeadset == null || mBluetoothHeadsetDevice == null) { 485 return false; 486 } 487 return mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 488 == BluetoothHeadset.STATE_AUDIO_CONNECTED; 489 } 490 491 // @GuardedBy("mDeviceBroker.mSetModeLock") 492 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") startBluetoothSco(int scoAudioMode, @NonNull String eventSource)493 /*package*/ synchronized boolean startBluetoothSco(int scoAudioMode, 494 @NonNull String eventSource) { 495 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource)); 496 return requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); 497 } 498 499 // @GuardedBy("mDeviceBroker.mSetModeLock") 500 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") stopBluetoothSco(@onNull String eventSource)501 /*package*/ synchronized boolean stopBluetoothSco(@NonNull String eventSource) { 502 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(eventSource)); 503 return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL); 504 } 505 setLeAudioVolume(int index, int maxIndex, int streamType)506 /*package*/ synchronized void setLeAudioVolume(int index, int maxIndex, int streamType) { 507 if (mLeAudio == null) { 508 if (AudioService.DEBUG_VOL) { 509 Log.i(TAG, "setLeAudioVolume: null mLeAudio"); 510 } 511 return; 512 } 513 /* leaudio expect volume value in range 0 to 255 */ 514 int volume = (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex); 515 516 if (AudioService.DEBUG_VOL) { 517 Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx=" 518 + index + " volume=" + volume); 519 } 520 AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent( 521 AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, streamType, index, 522 maxIndex, /*caller=*/null)); 523 try { 524 mLeAudio.setVolume(volume); 525 } catch (Exception e) { 526 Log.e(TAG, "Exception while setting LE volume", e); 527 } 528 } 529 setHearingAidVolume(int index, int streamType, boolean isHeadAidConnected)530 /*package*/ synchronized void setHearingAidVolume(int index, int streamType, 531 boolean isHeadAidConnected) { 532 if (mHearingAid == null) { 533 if (AudioService.DEBUG_VOL) { 534 Log.i(TAG, "setHearingAidVolume: null mHearingAid"); 535 } 536 return; 537 } 538 //hearing aid expect volume value in range -128dB to 0dB 539 int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10, 540 AudioSystem.DEVICE_OUT_HEARING_AID); 541 if (gainDB < BT_HEARING_AID_GAIN_MIN) { 542 gainDB = BT_HEARING_AID_GAIN_MIN; 543 } 544 if (AudioService.DEBUG_VOL) { 545 Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx=" 546 + index + " gain=" + gainDB); 547 } 548 // do not log when hearing aid is not connected to avoid confusion when reading dumpsys 549 if (isHeadAidConnected) { 550 AudioService.sVolumeLogger.enqueue(new AudioServiceEvents.VolumeEvent( 551 AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB)); 552 } 553 try { 554 mHearingAid.setVolume(gainDB); 555 } catch (Exception e) { 556 Log.i(TAG, "Exception while setting hearing aid volume", e); 557 } 558 } 559 onBroadcastScoConnectionState(int state)560 /*package*/ synchronized void onBroadcastScoConnectionState(int state) { 561 if (state == mScoConnectionState) { 562 return; 563 } 564 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); 565 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 566 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, 567 mScoConnectionState); 568 sendStickyBroadcastToAll(newIntent); 569 mScoConnectionState = state; 570 } 571 572 // @GuardedBy("mDeviceBroker.mSetModeLock") 573 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") resetBluetoothSco()574 /*package*/ synchronized void resetBluetoothSco() { 575 mScoAudioState = SCO_STATE_INACTIVE; 576 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 577 mDeviceBroker.clearA2dpSuspended(false /* internalOnly */); 578 mDeviceBroker.clearLeAudioSuspended(false /* internalOnly */); 579 mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); 580 } 581 582 // @GuardedBy("mDeviceBroker.mSetModeLock") 583 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onBtProfileDisconnected(int profile)584 /*package*/ synchronized void onBtProfileDisconnected(int profile) { 585 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 586 "BT profile " + BluetoothProfile.getProfileName(profile) 587 + " disconnected").printLog(TAG)); 588 switch (profile) { 589 case BluetoothProfile.HEADSET: 590 mBluetoothHeadset = null; 591 break; 592 case BluetoothProfile.A2DP: 593 mA2dp = null; 594 mA2dpCodecConfig = null; 595 break; 596 case BluetoothProfile.HEARING_AID: 597 mHearingAid = null; 598 break; 599 case BluetoothProfile.LE_AUDIO: 600 if (mLeAudio != null && mLeAudioCallback != null) { 601 mLeAudio.unregisterCallback(mLeAudioCallback); 602 } 603 mLeAudio = null; 604 mLeAudioCallback = null; 605 mLeAudioCodecConfig = null; 606 break; 607 case BluetoothProfile.LE_AUDIO_BROADCAST: 608 mLeAudioBroadcastCodec = AudioSystem.AUDIO_FORMAT_DEFAULT; 609 break; 610 case BluetoothProfile.A2DP_SINK: 611 // nothing to do in BtHelper 612 break; 613 default: 614 // Not a valid profile to disconnect 615 Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect " 616 + BluetoothProfile.getProfileName(profile)); 617 break; 618 } 619 } 620 621 // BluetoothLeAudio callback used to update the list of addresses in the same group as a 622 // connected LE Audio device 623 class MyLeAudioCallback implements BluetoothLeAudio.Callback { 624 @Override onCodecConfigChanged(int groupId, @NonNull BluetoothLeAudioCodecStatus status)625 public void onCodecConfigChanged(int groupId, 626 @NonNull BluetoothLeAudioCodecStatus status) { 627 // Do nothing 628 } 629 630 @Override onGroupNodeAdded(@onNull BluetoothDevice device, int groupId)631 public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) { 632 mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); 633 } 634 635 @Override onGroupNodeRemoved(@onNull BluetoothDevice device, int groupId)636 public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) { 637 mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); 638 } 639 @Override onGroupStatusChanged(int groupId, int groupStatus)640 public void onGroupStatusChanged(int groupId, int groupStatus) { 641 mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId); 642 } 643 } 644 645 MyLeAudioCallback mLeAudioCallback = null; 646 647 // @GuardedBy("mDeviceBroker.mSetModeLock") 648 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onBtProfileConnected(int profile, BluetoothProfile proxy)649 /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) { 650 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 651 "BT profile " + BluetoothProfile.getProfileName(profile) + " connected to proxy " 652 + proxy).printLog(TAG)); 653 if (proxy == null) { 654 Log.e(TAG, "onBtProfileConnected: null proxy for profile: " + profile); 655 return; 656 } 657 switch (profile) { 658 case BluetoothProfile.HEADSET: 659 onHeadsetProfileConnected((BluetoothHeadset) proxy); 660 return; 661 case BluetoothProfile.A2DP: 662 if (((BluetoothA2dp) proxy).equals(mA2dp)) { 663 return; 664 } 665 mA2dp = (BluetoothA2dp) proxy; 666 break; 667 case BluetoothProfile.HEARING_AID: 668 if (((BluetoothHearingAid) proxy).equals(mHearingAid)) { 669 return; 670 } 671 mHearingAid = (BluetoothHearingAid) proxy; 672 break; 673 case BluetoothProfile.LE_AUDIO: 674 if (((BluetoothLeAudio) proxy).equals(mLeAudio)) { 675 return; 676 } 677 if (mLeAudio != null && mLeAudioCallback != null) { 678 mLeAudio.unregisterCallback(mLeAudioCallback); 679 } 680 mLeAudio = (BluetoothLeAudio) proxy; 681 mLeAudioCallback = new MyLeAudioCallback(); 682 mLeAudio.registerCallback( 683 mContext.getMainExecutor(), mLeAudioCallback); 684 break; 685 case BluetoothProfile.A2DP_SINK: 686 case BluetoothProfile.LE_AUDIO_BROADCAST: 687 // nothing to do in BtHelper 688 return; 689 default: 690 // Not a valid profile to connect 691 Log.e(TAG, "onBtProfileConnected: Not a valid profile to connect " 692 + BluetoothProfile.getProfileName(profile)); 693 return; 694 } 695 696 // this part is only for A2DP, LE Audio unicast and Hearing aid 697 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 698 if (adapter == null) { 699 Log.e(TAG, "onBtProfileConnected: Null BluetoothAdapter when connecting profile: " 700 + BluetoothProfile.getProfileName(profile)); 701 return; 702 } 703 List<BluetoothDevice> activeDevices = adapter.getActiveDevices(profile); 704 if (activeDevices.isEmpty() || activeDevices.get(0) == null) { 705 return; 706 } 707 BluetoothDevice device = activeDevices.get(0); 708 switch (profile) { 709 case BluetoothProfile.A2DP: { 710 BluetoothProfileConnectionInfo bpci = 711 BluetoothProfileConnectionInfo.createA2dpInfo(false, -1); 712 postBluetoothActiveDevice(device, bpci); 713 } break; 714 case BluetoothProfile.HEARING_AID: { 715 BluetoothProfileConnectionInfo bpci = 716 BluetoothProfileConnectionInfo.createHearingAidInfo(false); 717 postBluetoothActiveDevice(device, bpci); 718 } break; 719 case BluetoothProfile.LE_AUDIO: { 720 int groupId = mLeAudio.getGroupId(device); 721 BluetoothLeAudioCodecStatus btLeCodecStatus = null; 722 try { 723 btLeCodecStatus = mLeAudio.getCodecStatus(groupId); 724 } catch (Exception e) { 725 Log.e(TAG, "Exception while getting status of " + device, e); 726 } 727 if (btLeCodecStatus == null) { 728 Log.i(TAG, "onBtProfileConnected null LE codec status for groupId: " 729 + groupId + ", device: " + device); 730 break; 731 } 732 List<BluetoothLeAudioCodecConfig> outputCodecConfigs = 733 btLeCodecStatus.getOutputCodecSelectableCapabilities(); 734 if (!outputCodecConfigs.isEmpty()) { 735 BluetoothProfileConnectionInfo bpci = 736 BluetoothProfileConnectionInfo.createLeAudioInfo( 737 false /*suppressNoisyIntent*/, true /*isLeOutput*/); 738 postBluetoothActiveDevice(device, bpci); 739 } 740 List<BluetoothLeAudioCodecConfig> inputCodecConfigs = 741 btLeCodecStatus.getInputCodecSelectableCapabilities(); 742 if (!inputCodecConfigs.isEmpty()) { 743 BluetoothProfileConnectionInfo bpci = 744 BluetoothProfileConnectionInfo.createLeAudioInfo( 745 false /*suppressNoisyIntent*/, false /*isLeOutput*/); 746 postBluetoothActiveDevice(device, bpci); 747 } 748 } break; 749 default: 750 // Not a valid profile to connect 751 Log.wtf(TAG, "Invalid profile! onBtProfileConnected"); 752 break; 753 } 754 } 755 postBluetoothActiveDevice( BluetoothDevice device, BluetoothProfileConnectionInfo bpci)756 private void postBluetoothActiveDevice( 757 BluetoothDevice device, BluetoothProfileConnectionInfo bpci) { 758 AudioDeviceBroker.BtDeviceChangedData data = new AudioDeviceBroker.BtDeviceChangedData( 759 device, null, bpci, "mBluetoothProfileServiceListener"); 760 AudioDeviceBroker.BtDeviceInfo info = mDeviceBroker.createBtDeviceInfo( 761 data, device, BluetoothProfile.STATE_CONNECTED); 762 mDeviceBroker.postBluetoothActiveDevice(info, 0 /* delay */); 763 } 764 isProfilePoxyConnected(int profile)765 /*package*/ synchronized boolean isProfilePoxyConnected(int profile) { 766 switch (profile) { 767 case BluetoothProfile.HEADSET: 768 return mBluetoothHeadset != null; 769 case BluetoothProfile.A2DP: 770 return mA2dp != null; 771 case BluetoothProfile.HEARING_AID: 772 return mHearingAid != null; 773 case BluetoothProfile.LE_AUDIO: 774 return mLeAudio != null; 775 case BluetoothProfile.A2DP_SINK: 776 case BluetoothProfile.LE_AUDIO_BROADCAST: 777 default: 778 // return true for profiles that are not managed by the BtHelper because 779 // the fact that the profile proxy is not connected does not affect 780 // the device connection handling. 781 return true; 782 } 783 } 784 785 // @GuardedBy("mDeviceBroker.mSetModeLock") 786 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onHeadsetProfileConnected(@onNull BluetoothHeadset headset)787 private void onHeadsetProfileConnected(@NonNull BluetoothHeadset headset) { 788 // Discard timeout message 789 mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); 790 mBluetoothHeadset = headset; 791 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 792 if (adapter != null) { 793 List<BluetoothDevice> activeDevices = 794 adapter.getActiveDevices(BluetoothProfile.HEADSET); 795 for (BluetoothDevice device : activeDevices) { 796 if (device == null) { 797 continue; 798 } 799 onSetBtScoActiveDevice(device); 800 } 801 } else { 802 Log.e(TAG, "onHeadsetProfileConnected: Null BluetoothAdapter"); 803 } 804 805 // Refresh SCO audio state 806 checkScoAudioState(); 807 if (mScoAudioState != SCO_STATE_ACTIVATE_REQ 808 && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) { 809 return; 810 } 811 boolean status = false; 812 if (mBluetoothHeadsetDevice != null) { 813 switch (mScoAudioState) { 814 case SCO_STATE_ACTIVATE_REQ: 815 status = connectBluetoothScoAudioHelper( 816 mBluetoothHeadset, 817 mBluetoothHeadsetDevice, mScoAudioMode); 818 if (status) { 819 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 820 } 821 break; 822 case SCO_STATE_DEACTIVATE_REQ: 823 status = disconnectBluetoothScoAudioHelper( 824 mBluetoothHeadset, 825 mBluetoothHeadsetDevice, mScoAudioMode); 826 if (status) { 827 mScoAudioState = SCO_STATE_DEACTIVATING; 828 } 829 break; 830 } 831 } 832 if (!status) { 833 mScoAudioState = SCO_STATE_INACTIVE; 834 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 835 } 836 } 837 838 //---------------------------------------------------------------------- broadcastScoConnectionState(int state)839 private void broadcastScoConnectionState(int state) { 840 mDeviceBroker.postBroadcastScoConnectionState(state); 841 } 842 getHeadsetAudioDevice()843 @Nullable AudioDeviceAttributes getHeadsetAudioDevice() { 844 if (mBluetoothHeadsetDevice == null) { 845 return null; 846 } 847 return getHeadsetAudioDevice(mBluetoothHeadsetDevice); 848 } 849 getHeadsetAudioDevice(BluetoothDevice btDevice)850 private @NonNull AudioDeviceAttributes getHeadsetAudioDevice(BluetoothDevice btDevice) { 851 AudioDeviceAttributes deviceAttr = mResolvedScoAudioDevices.get(btDevice); 852 if (deviceAttr != null) { 853 // Returns the cached device attributes so that it is consistent as the previous one. 854 return deviceAttr; 855 } 856 return btHeadsetDeviceToAudioDevice(btDevice); 857 } 858 btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice)859 private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) { 860 if (btDevice == null) { 861 return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""); 862 } 863 String address = btDevice.getAddress(); 864 String name = getName(btDevice); 865 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 866 address = ""; 867 } 868 BluetoothClass btClass = btDevice.getBluetoothClass(); 869 int nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 870 if (btClass != null) { 871 switch (btClass.getDeviceClass()) { 872 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 873 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 874 nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 875 break; 876 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 877 nativeType = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 878 break; 879 } 880 } 881 if (AudioService.DEBUG_DEVICES) { 882 Log.i(TAG, "btHeadsetDeviceToAudioDevice btDevice: " + btDevice 883 + " btClass: " + (btClass == null ? "Unknown" : btClass) 884 + " nativeType: " + nativeType + " address: " + address); 885 } 886 return new AudioDeviceAttributes(nativeType, address, name); 887 } 888 handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive)889 private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) { 890 if (btDevice == null) { 891 return true; 892 } 893 int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET; 894 AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice); 895 boolean result = false; 896 if (isActive) { 897 result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice); 898 } else { 899 int[] outDeviceTypes = { 900 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, 901 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET, 902 AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT 903 }; 904 for (int outDeviceType : outDeviceTypes) { 905 result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( 906 outDeviceType, audioDevice.getAddress(), audioDevice.getName()), 907 isActive, btDevice); 908 } 909 } 910 // handleDeviceConnection() && result to make sure the method get executed 911 result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes( 912 inDevice, audioDevice.getAddress(), audioDevice.getName()), 913 isActive, btDevice) && result; 914 if (result) { 915 if (isActive) { 916 mResolvedScoAudioDevices.put(btDevice, audioDevice); 917 } else { 918 mResolvedScoAudioDevices.remove(btDevice); 919 } 920 } 921 return result; 922 } 923 924 // Return `(null)` if given BluetoothDevice is null. Otherwise, return the anonymized address. getAnonymizedAddress(BluetoothDevice btDevice)925 private String getAnonymizedAddress(BluetoothDevice btDevice) { 926 return btDevice == null ? "(null)" : btDevice.getAnonymizedAddress(); 927 } 928 929 // @GuardedBy("mDeviceBroker.mSetModeLock") 930 @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onSetBtScoActiveDevice(BluetoothDevice btDevice)931 /*package */ synchronized void onSetBtScoActiveDevice(BluetoothDevice btDevice) { 932 Log.i(TAG, "onSetBtScoActiveDevice: " + getAnonymizedAddress(mBluetoothHeadsetDevice) 933 + " -> " + getAnonymizedAddress(btDevice)); 934 final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; 935 if (Objects.equals(btDevice, previousActiveDevice)) { 936 return; 937 } 938 if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) { 939 Log.w(TAG, "onSetBtScoActiveDevice() failed to remove previous device " 940 + getAnonymizedAddress(previousActiveDevice)); 941 } 942 if (!handleBtScoActiveDeviceChange(btDevice, true)) { 943 Log.e(TAG, "onSetBtScoActiveDevice() failed to add new device " 944 + getAnonymizedAddress(btDevice)); 945 // set mBluetoothHeadsetDevice to null when failing to add new device 946 btDevice = null; 947 } 948 mBluetoothHeadsetDevice = btDevice; 949 if (mBluetoothHeadsetDevice == null) { 950 resetBluetoothSco(); 951 } 952 } 953 954 // NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async 955 // methods inside listener. 956 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 957 new BluetoothProfile.ServiceListener() { 958 public void onServiceConnected(int profile, BluetoothProfile proxy) { 959 switch(profile) { 960 case BluetoothProfile.A2DP: 961 case BluetoothProfile.HEADSET: 962 case BluetoothProfile.HEARING_AID: 963 case BluetoothProfile.LE_AUDIO: 964 case BluetoothProfile.A2DP_SINK: 965 case BluetoothProfile.LE_AUDIO_BROADCAST: 966 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 967 "BT profile service: connecting " 968 + BluetoothProfile.getProfileName(profile) 969 + " profile").printLog(TAG)); 970 mDeviceBroker.postBtProfileConnected(profile, proxy); 971 break; 972 973 default: 974 break; 975 } 976 } 977 public void onServiceDisconnected(int profile) { 978 979 switch (profile) { 980 case BluetoothProfile.A2DP: 981 case BluetoothProfile.HEADSET: 982 case BluetoothProfile.HEARING_AID: 983 case BluetoothProfile.LE_AUDIO: 984 case BluetoothProfile.A2DP_SINK: 985 case BluetoothProfile.LE_AUDIO_BROADCAST: 986 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 987 "BT profile service: disconnecting " 988 + BluetoothProfile.getProfileName(profile) 989 + " profile").printLog(TAG)); 990 mDeviceBroker.postBtProfileDisconnected(profile); 991 break; 992 993 default: 994 break; 995 } 996 } 997 }; 998 999 //---------------------------------------------------------------------- 1000 1001 // @GuardedBy("mDeviceBroker.mSetModeLock") 1002 // @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") 1003 @GuardedBy("BtHelper.this") requestScoState(int state, int scoAudioMode)1004 private boolean requestScoState(int state, int scoAudioMode) { 1005 checkScoAudioState(); 1006 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1007 // Make sure that the state transitions to CONNECTING even if we cannot initiate 1008 // the connection except if already connected internally 1009 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { 1010 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); 1011 } 1012 switch (mScoAudioState) { 1013 case SCO_STATE_INACTIVE: 1014 mScoAudioMode = scoAudioMode; 1015 if (scoAudioMode == SCO_MODE_UNDEFINED) { 1016 mScoAudioMode = SCO_MODE_VIRTUAL_CALL; 1017 if (mBluetoothHeadsetDevice != null) { 1018 mScoAudioMode = Settings.Global.getInt( 1019 mDeviceBroker.getContentResolver(), 1020 "bluetooth_sco_channel_" 1021 + mBluetoothHeadsetDevice.getAddress(), 1022 SCO_MODE_VIRTUAL_CALL); 1023 if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) { 1024 mScoAudioMode = SCO_MODE_VIRTUAL_CALL; 1025 } 1026 } 1027 } 1028 if (mBluetoothHeadset == null) { 1029 if (getBluetoothHeadset()) { 1030 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 1031 } else { 1032 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" 1033 + " connection, mScoAudioMode=" + mScoAudioMode); 1034 broadcastScoConnectionState( 1035 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1036 return false; 1037 } 1038 break; 1039 } 1040 if (mBluetoothHeadsetDevice == null) { 1041 Log.w(TAG, "requestScoState: no active device while connecting," 1042 + " mScoAudioMode=" + mScoAudioMode); 1043 broadcastScoConnectionState( 1044 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1045 return false; 1046 } 1047 if (connectBluetoothScoAudioHelper(mBluetoothHeadset, 1048 mBluetoothHeadsetDevice, mScoAudioMode)) { 1049 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1050 } else { 1051 Log.w(TAG, "requestScoState: connect to " 1052 + getAnonymizedAddress(mBluetoothHeadsetDevice) 1053 + " failed, mScoAudioMode=" + mScoAudioMode); 1054 broadcastScoConnectionState( 1055 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1056 return false; 1057 } 1058 break; 1059 case SCO_STATE_DEACTIVATING: 1060 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 1061 break; 1062 case SCO_STATE_DEACTIVATE_REQ: 1063 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1064 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 1065 break; 1066 case SCO_STATE_ACTIVE_INTERNAL: 1067 // Already in ACTIVE mode, simply return 1068 break; 1069 case SCO_STATE_ACTIVE_EXTERNAL: 1070 /* Confirm SCO Audio connection to requesting app as it is already connected 1071 * externally (i.e. through SCO APIs by Telecom service). 1072 * Once SCO Audio is disconnected by the external owner, we will reconnect it 1073 * automatically on behalf of the requesting app and the state will move to 1074 * SCO_STATE_ACTIVE_INTERNAL. 1075 */ 1076 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 1077 break; 1078 default: 1079 Log.w(TAG, "requestScoState: failed to connect in state " 1080 + mScoAudioState + ", scoAudioMode=" + scoAudioMode); 1081 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1082 return false; 1083 } 1084 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1085 switch (mScoAudioState) { 1086 case SCO_STATE_ACTIVE_INTERNAL: 1087 if (mBluetoothHeadset == null) { 1088 if (getBluetoothHeadset()) { 1089 mScoAudioState = SCO_STATE_DEACTIVATE_REQ; 1090 } else { 1091 Log.w(TAG, "requestScoState: getBluetoothHeadset failed during" 1092 + " disconnection, mScoAudioMode=" + mScoAudioMode); 1093 mScoAudioState = SCO_STATE_INACTIVE; 1094 broadcastScoConnectionState( 1095 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1096 return false; 1097 } 1098 break; 1099 } 1100 if (mBluetoothHeadsetDevice == null) { 1101 mScoAudioState = SCO_STATE_INACTIVE; 1102 broadcastScoConnectionState( 1103 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1104 break; 1105 } 1106 if (disconnectBluetoothScoAudioHelper(mBluetoothHeadset, 1107 mBluetoothHeadsetDevice, mScoAudioMode)) { 1108 mScoAudioState = SCO_STATE_DEACTIVATING; 1109 } else { 1110 mScoAudioState = SCO_STATE_INACTIVE; 1111 broadcastScoConnectionState( 1112 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1113 } 1114 break; 1115 case SCO_STATE_ACTIVATE_REQ: 1116 mScoAudioState = SCO_STATE_INACTIVE; 1117 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1118 break; 1119 default: 1120 Log.w(TAG, "requestScoState: failed to disconnect in state " 1121 + mScoAudioState + ", scoAudioMode=" + scoAudioMode); 1122 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1123 return false; 1124 } 1125 } 1126 return true; 1127 } 1128 1129 //----------------------------------------------------- 1130 // Utilities sendStickyBroadcastToAll(Intent intent)1131 private void sendStickyBroadcastToAll(Intent intent) { 1132 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 1133 final long ident = Binder.clearCallingIdentity(); 1134 try { 1135 mDeviceBroker.getContext().sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1136 } finally { 1137 Binder.restoreCallingIdentity(ident); 1138 } 1139 } 1140 disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)1141 private static boolean disconnectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, 1142 BluetoothDevice device, int scoAudioMode) { 1143 switch (scoAudioMode) { 1144 case SCO_MODE_VIRTUAL_CALL: 1145 return bluetoothHeadset.stopScoUsingVirtualVoiceCall(); 1146 case SCO_MODE_VR: 1147 return bluetoothHeadset.stopVoiceRecognition(device); 1148 default: 1149 return false; 1150 } 1151 } 1152 connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, BluetoothDevice device, int scoAudioMode)1153 private static boolean connectBluetoothScoAudioHelper(BluetoothHeadset bluetoothHeadset, 1154 BluetoothDevice device, int scoAudioMode) { 1155 switch (scoAudioMode) { 1156 case SCO_MODE_VIRTUAL_CALL: 1157 return bluetoothHeadset.startScoUsingVirtualVoiceCall(); 1158 case SCO_MODE_VR: 1159 return bluetoothHeadset.startVoiceRecognition(device); 1160 default: 1161 return false; 1162 } 1163 } 1164 checkScoAudioState()1165 private void checkScoAudioState() { 1166 if (mBluetoothHeadset != null 1167 && mBluetoothHeadsetDevice != null 1168 && mScoAudioState == SCO_STATE_INACTIVE 1169 && mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 1170 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1171 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 1172 } 1173 } 1174 getBluetoothHeadset()1175 private boolean getBluetoothHeadset() { 1176 boolean result = false; 1177 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1178 if (adapter != null) { 1179 result = adapter.getProfileProxy(mDeviceBroker.getContext(), 1180 mBluetoothProfileServiceListener, BluetoothProfile.HEADSET); 1181 } 1182 // If we could not get a bluetooth headset proxy, send a failure message 1183 // without delay to reset the SCO audio state and clear SCO clients. 1184 // If we could get a proxy, send a delayed failure message that will reset our state 1185 // in case we don't receive onServiceConnected(). 1186 mDeviceBroker.handleFailureToConnectToBtHeadsetService( 1187 result ? AudioDeviceBroker.BT_HEADSET_CNCT_TIMEOUT_MS : 0); 1188 return result; 1189 } 1190 getLeAudioDeviceGroupId(BluetoothDevice device)1191 /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) { 1192 if (mLeAudio == null || device == null) { 1193 return BluetoothLeAudio.GROUP_ID_INVALID; 1194 } 1195 return mLeAudio.getGroupId(device); 1196 } 1197 1198 /** 1199 * Returns all addresses and identity addresses for LE Audio devices a group. 1200 * @param groupId The ID of the group from which to get addresses. 1201 * @return A List of Pair(String main_address, String identity_address). Note that the 1202 * addresses returned by BluetoothDevice can be null. 1203 */ getLeAudioGroupAddresses(int groupId)1204 /*package*/ List<Pair<String, String>> getLeAudioGroupAddresses(int groupId) { 1205 List<Pair<String, String>> addresses = new ArrayList<>(); 1206 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1207 if (adapter == null || mLeAudio == null) { 1208 return addresses; 1209 } 1210 List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO); 1211 for (BluetoothDevice device : activeDevices) { 1212 if (device != null && mLeAudio.getGroupId(device) == groupId) { 1213 addresses.add(new Pair(device.getAddress(), device.getIdentityAddress())); 1214 } 1215 } 1216 return addresses; 1217 } 1218 1219 /** 1220 * Returns the String equivalent of the btCodecType. 1221 * 1222 * This uses an "ENCODING_" prefix for consistency with Audio; 1223 * we could alternately use the "SOURCE_CODEC_TYPE_" prefix from Bluetooth. 1224 */ bluetoothCodecToEncodingString(int btCodecType)1225 public static String bluetoothCodecToEncodingString(int btCodecType) { 1226 switch (btCodecType) { 1227 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC: 1228 return "ENCODING_SBC"; 1229 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC: 1230 return "ENCODING_AAC"; 1231 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX: 1232 return "ENCODING_APTX"; 1233 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD: 1234 return "ENCODING_APTX_HD"; 1235 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC: 1236 return "ENCODING_LDAC"; 1237 case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS: 1238 return "ENCODING_OPUS"; 1239 default: 1240 return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; 1241 } 1242 } 1243 getProfileFromType(int deviceType)1244 /*package */ static int getProfileFromType(int deviceType) { 1245 if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) { 1246 return BluetoothProfile.A2DP; 1247 } else if (AudioSystem.isBluetoothScoDevice(deviceType)) { 1248 return BluetoothProfile.HEADSET; 1249 } else if (AudioSystem.isBluetoothLeDevice(deviceType)) { 1250 return BluetoothProfile.LE_AUDIO; 1251 } 1252 return 0; // 0 is not a valid profile 1253 } 1254 getPreferredAudioProfiles(String address)1255 /*package */ static Bundle getPreferredAudioProfiles(String address) { 1256 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1257 return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address)); 1258 } 1259 1260 @Nullable getBluetoothDevice(String address)1261 /*package */ static BluetoothDevice getBluetoothDevice(String address) { 1262 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1263 if (adapter == null || !BluetoothAdapter.checkBluetoothAddress(address)) { 1264 return null; 1265 } 1266 1267 return adapter.getRemoteDevice(address); 1268 } 1269 1270 @AudioDeviceCategory getBtDeviceCategory(String address)1271 /*package*/ static int getBtDeviceCategory(String address) { 1272 if (!automaticBtDeviceType()) { 1273 return AUDIO_DEVICE_CATEGORY_UNKNOWN; 1274 } 1275 1276 BluetoothDevice device = BtHelper.getBluetoothDevice(address); 1277 if (device == null) { 1278 return AUDIO_DEVICE_CATEGORY_UNKNOWN; 1279 } 1280 1281 byte[] deviceType = device.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE); 1282 if (deviceType == null) { 1283 return AUDIO_DEVICE_CATEGORY_UNKNOWN; 1284 } 1285 String deviceCategory = new String(deviceType); 1286 switch (deviceCategory) { 1287 case DEVICE_TYPE_HEARING_AID: 1288 return AUDIO_DEVICE_CATEGORY_HEARING_AID; 1289 case DEVICE_TYPE_CARKIT: 1290 return AUDIO_DEVICE_CATEGORY_CARKIT; 1291 case DEVICE_TYPE_HEADSET: 1292 case DEVICE_TYPE_UNTETHERED_HEADSET: 1293 return AUDIO_DEVICE_CATEGORY_HEADPHONES; 1294 case DEVICE_TYPE_SPEAKER: 1295 return AUDIO_DEVICE_CATEGORY_SPEAKER; 1296 case DEVICE_TYPE_WATCH: 1297 return AUDIO_DEVICE_CATEGORY_WATCH; 1298 case DEVICE_TYPE_DEFAULT: 1299 default: 1300 // fall through 1301 } 1302 1303 BluetoothClass deviceClass = device.getBluetoothClass(); 1304 if (deviceClass == null) { 1305 return AUDIO_DEVICE_CATEGORY_UNKNOWN; 1306 } 1307 1308 switch (deviceClass.getDeviceClass()) { 1309 case BluetoothClass.Device.WEARABLE_WRIST_WATCH: 1310 return AUDIO_DEVICE_CATEGORY_WATCH; 1311 case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: 1312 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: 1313 case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: 1314 return AUDIO_DEVICE_CATEGORY_SPEAKER; 1315 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 1316 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: 1317 return AUDIO_DEVICE_CATEGORY_HEADPHONES; 1318 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: 1319 return AUDIO_DEVICE_CATEGORY_RECEIVER; 1320 default: 1321 return AUDIO_DEVICE_CATEGORY_UNKNOWN; 1322 } 1323 } 1324 1325 /** 1326 * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices 1327 * have been applied. 1328 */ onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice)1329 public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) { 1330 BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice); 1331 } 1332 1333 /** 1334 * Returns the string equivalent for the btDeviceClass class. 1335 */ btDeviceClassToString(int btDeviceClass)1336 public static String btDeviceClassToString(int btDeviceClass) { 1337 switch (btDeviceClass) { 1338 case BluetoothClass.Device.AUDIO_VIDEO_UNCATEGORIZED: 1339 return "AUDIO_VIDEO_UNCATEGORIZED"; 1340 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 1341 return "AUDIO_VIDEO_WEARABLE_HEADSET"; 1342 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 1343 return "AUDIO_VIDEO_HANDSFREE"; 1344 case 0x040C: 1345 return "AUDIO_VIDEO_RESERVED_0x040C"; // uncommon 1346 case BluetoothClass.Device.AUDIO_VIDEO_MICROPHONE: 1347 return "AUDIO_VIDEO_MICROPHONE"; 1348 case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER: 1349 return "AUDIO_VIDEO_LOUDSPEAKER"; 1350 case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES: 1351 return "AUDIO_VIDEO_HEADPHONES"; 1352 case BluetoothClass.Device.AUDIO_VIDEO_PORTABLE_AUDIO: 1353 return "AUDIO_VIDEO_PORTABLE_AUDIO"; 1354 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 1355 return "AUDIO_VIDEO_CAR_AUDIO"; 1356 case BluetoothClass.Device.AUDIO_VIDEO_SET_TOP_BOX: 1357 return "AUDIO_VIDEO_SET_TOP_BOX"; 1358 case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO: 1359 return "AUDIO_VIDEO_HIFI_AUDIO"; 1360 case BluetoothClass.Device.AUDIO_VIDEO_VCR: 1361 return "AUDIO_VIDEO_VCR"; 1362 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CAMERA: 1363 return "AUDIO_VIDEO_VIDEO_CAMERA"; 1364 case BluetoothClass.Device.AUDIO_VIDEO_CAMCORDER: 1365 return "AUDIO_VIDEO_CAMCORDER"; 1366 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_MONITOR: 1367 return "AUDIO_VIDEO_VIDEO_MONITOR"; 1368 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER: 1369 return "AUDIO_VIDEO_VIDEO_DISPLAY_AND_LOUDSPEAKER"; 1370 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_CONFERENCING: 1371 return "AUDIO_VIDEO_VIDEO_CONFERENCING"; 1372 case 0x0444: 1373 return "AUDIO_VIDEO_RESERVED_0x0444"; // uncommon 1374 case BluetoothClass.Device.AUDIO_VIDEO_VIDEO_GAMING_TOY: 1375 return "AUDIO_VIDEO_VIDEO_GAMING_TOY"; 1376 default: // other device classes printed as a hex string. 1377 return TextUtils.formatSimple("0x%04x", btDeviceClass); 1378 } 1379 } 1380 1381 //------------------------------------------------------------ dump(PrintWriter pw, String prefix)1382 /*package*/ void dump(PrintWriter pw, String prefix) { 1383 pw.println("\n" + prefix + "mBluetoothHeadset: " + mBluetoothHeadset); 1384 pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice); 1385 if (mBluetoothHeadsetDevice != null) { 1386 final BluetoothClass bluetoothClass = mBluetoothHeadsetDevice.getBluetoothClass(); 1387 if (bluetoothClass != null) { 1388 pw.println(prefix + "mBluetoothHeadsetDevice.DeviceClass: " 1389 + btDeviceClassToString(bluetoothClass.getDeviceClass())); 1390 } 1391 } 1392 pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState)); 1393 pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode)); 1394 pw.println("\n" + prefix + "mHearingAid: " + mHearingAid); 1395 pw.println("\n" + prefix + "mLeAudio: " + mLeAudio); 1396 pw.println(prefix + "mA2dp: " + mA2dp); 1397 pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported); 1398 } 1399 1400 } 1401