Fix logic for categorizing a volume group as invalid
A volume group is invalid if it contains as legacy stream only bt sco.
In Auto a volume group can contain multiple streams. See telring which
has ring, voice call and bt sco as legacy streams.
When a volume group contains only bt sco as legacy stream we try to
replace the mapping of bt_sco with voice_call if available.
Do not throw IAE when querrying the state of an inexistant volume group
Test: manual
Flag: com.android.media.audio.replace_stream_bt_sco
Bug: 354945865
Change-Id: Id1dcaec524dc141c8494f644257387d753e2239b
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 1183768..dbdb7b8 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4306,7 +4306,8 @@
super.getVolumeGroupVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
- throw new IllegalArgumentException("No volume group for id " + groupId);
+ Log.e(TAG, "No volume group for id " + groupId);
+ return 0;
}
VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
// Return 0 when muted, not min index since for e.g. Voice Call, it has a non zero
@@ -4322,7 +4323,8 @@
super.getVolumeGroupMaxVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
- throw new IllegalArgumentException("No volume group for id " + groupId);
+ Log.e(TAG, "No volume group for id " + groupId);
+ return 0;
}
VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
return vgs.getMaxIndex();
@@ -4336,7 +4338,8 @@
super.getVolumeGroupMinVolumeIndex_enforcePermission();
synchronized (VolumeStreamState.class) {
if (sVolumeGroupStates.indexOfKey(groupId) < 0) {
- throw new IllegalArgumentException("No volume group for id " + groupId);
+ Log.e(TAG, "No volume group for id " + groupId);
+ return 0;
}
VolumeGroupState vgs = sVolumeGroupStates.get(groupId);
return vgs.getMinIndex();
@@ -8259,11 +8262,21 @@
private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>();
private void initVolumeGroupStates() {
+ int btScoGroupId = -1;
+ VolumeGroupState voiceCallGroup = null;
for (final AudioVolumeGroup avg : getAudioVolumeGroups()) {
try {
- // if no valid attributes, this volume group is not controllable
- if (ensureValidAttributes(avg)) {
- sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
+ if (ensureValidVolumeGroup(avg)) {
+ final VolumeGroupState vgs = new VolumeGroupState(avg);
+ sVolumeGroupStates.append(avg.getId(), vgs);
+ if (vgs.isVoiceCall()) {
+ voiceCallGroup = vgs;
+ }
+ } else {
+ // invalid volume group will be reported for bt sco group with no other
+ // legacy stream type, we try to replace it in sVolumeGroupStates with the
+ // voice call volume group
+ btScoGroupId = avg.getId();
}
} catch (IllegalArgumentException e) {
// Volume Groups without attributes are not controllable through set/get volume
@@ -8271,10 +8284,15 @@
if (DEBUG_VOL) {
Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
}
- continue;
}
}
+ if (replaceStreamBtSco() && btScoGroupId >= 0 && voiceCallGroup != null) {
+ // the bt sco group is deprecated, storing the voice call group instead
+ // to keep the code backwards compatible when calling the volume group APIs
+ sVolumeGroupStates.append(btScoGroupId, voiceCallGroup);
+ }
+
// need mSettingsLock for vgs.applyAllVolumes -> vss.setIndex which grabs this lock after
// VSS.class. Locking order needs to be preserved
synchronized (mSettingsLock) {
@@ -8285,7 +8303,15 @@
}
}
- private boolean ensureValidAttributes(AudioVolumeGroup avg) {
+ /**
+ * Returns false if the legacy stream types only contains the deprecated
+ * {@link AudioSystem#STREAM_BLUETOOTH_SCO}.
+ *
+ * @throws IllegalArgumentException if it has more than one non-default {@link AudioAttributes}
+ *
+ * @param avg the volume group to check
+ */
+ private boolean ensureValidVolumeGroup(AudioVolumeGroup avg) {
boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
.anyMatch(aa -> !aa.equals(AudioProductStrategy.getDefaultAttributes()));
if (!hasAtLeastOneValidAudioAttributes) {
@@ -8293,10 +8319,11 @@
+ " has no valid audio attributes");
}
if (replaceStreamBtSco()) {
- for (int streamType : avg.getLegacyStreamTypes()) {
- if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
- return false;
- }
+ // if there are multiple legacy stream types associated we can omit stream bt sco
+ // otherwise this is not a valid volume group
+ if (avg.getLegacyStreamTypes().length == 1
+ && avg.getLegacyStreamTypes()[0] == AudioSystem.STREAM_BLUETOOTH_SCO) {
+ return false;
}
}
return true;
@@ -8637,6 +8664,10 @@
return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_MUSIC;
}
+ public boolean isVoiceCall() {
+ return mHasValidStreamType && mPublicStreamType == AudioSystem.STREAM_VOICE_CALL;
+ }
+
public void applyAllVolumes(boolean userSwitch) {
String caller = "from vgs";
synchronized (AudioService.VolumeStreamState.class) {