summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java98
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java58
2 files changed, 151 insertions, 5 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java
index 31416a30c5a7..7f0c1263570e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeController.java
@@ -16,6 +16,10 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.AudioInputControl.MUTE_DISABLED;
+import static android.bluetooth.AudioInputControl.MUTE_MUTED;
+import static android.bluetooth.AudioInputControl.MUTE_NOT_MUTED;
+
import static com.android.settingslib.bluetooth.HearingDeviceLocalDataManager.Data.INVALID_VOLUME;
import android.bluetooth.AudioInputControl;
@@ -171,7 +175,8 @@ public class AmbientVolumeController implements LocalBluetoothProfileManager.Ser
return null;
}
int gainSetting = getAmbient(device);
- return new RemoteAmbientState(gainSetting);
+ int mute = getMute(device);
+ return new RemoteAmbientState(gainSetting, mute);
}
/**
@@ -190,7 +195,9 @@ public class AmbientVolumeController implements LocalBluetoothProfileManager.Ser
if (!ambientControls.isEmpty()) {
synchronized (mDeviceAmbientStateMap) {
value = ambientControls.getFirst().getGainSetting();
- RemoteAmbientState updatedState = new RemoteAmbientState(value);
+ RemoteAmbientState state = mDeviceAmbientStateMap.getOrDefault(device,
+ new RemoteAmbientState(INVALID_VOLUME, MUTE_DISABLED));
+ RemoteAmbientState updatedState = new RemoteAmbientState(value, state.mute);
mDeviceAmbientStateMap.put(device, updatedState);
}
}
@@ -208,9 +215,55 @@ public class AmbientVolumeController implements LocalBluetoothProfileManager.Ser
Log.d(TAG, "setAmbient, value:" + value + ", device:" + device);
}
List<AudioInputControl> ambientControls = getAmbientControls(device);
+ ambientControls.forEach(control -> control.setGainSetting(value));
+ }
+
+ /**
+ * Gets the mute state from first ambient control point of the remote device and
+ * stores it in cached {@link RemoteAmbientState}. The value will be one of
+ * {@link AudioInputControl.Mute}.
+ *
+ * When any audio input point receives {@link AmbientCallback#onMuteChanged(int)} callback,
+ * only the changed value which is different from the value stored in the cached state will
+ * be notified to the {@link AmbientVolumeControlCallback} of this controller.
+ *
+ * @param device the remote device
+ */
+ public int getMute(@NonNull BluetoothDevice device) {
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ int value = MUTE_DISABLED;
if (!ambientControls.isEmpty()) {
- ambientControls.forEach(control -> control.setGainSetting(value));
+ synchronized (mDeviceAmbientStateMap) {
+ value = ambientControls.getFirst().getMute();
+ RemoteAmbientState state = mDeviceAmbientStateMap.getOrDefault(device,
+ new RemoteAmbientState(INVALID_VOLUME, MUTE_DISABLED));
+ RemoteAmbientState updatedState = new RemoteAmbientState(state.gainSetting, value);
+ mDeviceAmbientStateMap.put(device, updatedState);
+ }
}
+ return value;
+ }
+
+ /**
+ * Sets the mute state to all ambient control points of the remote device.
+ *
+ * @param device the remote device
+ * @param muted the mute state to be updated
+ */
+ public void setMuted(@NonNull BluetoothDevice device, boolean muted) {
+ if (DEBUG) {
+ Log.d(TAG, "setMuted, muted:" + muted + ", device:" + device);
+ }
+ List<AudioInputControl> ambientControls = getAmbientControls(device);
+ ambientControls.forEach(control -> {
+ try {
+ control.setMute(muted ? MUTE_MUTED : MUTE_NOT_MUTED);
+ } catch (IllegalStateException e) {
+ // Sometimes remote will throw this exception due to initialization not done
+ // yet. Catch it to prevent crashes on UI.
+ Log.w(TAG, "Remote mute state is currently disabled.");
+ }
+ });
}
/**
@@ -276,6 +329,16 @@ public class AmbientVolumeController implements LocalBluetoothProfileManager.Ser
}
/**
+ * This method is called when one of the remote device's ambient control point's mute
+ * state is changed.
+ *
+ * @param device the remote device
+ * @param mute the new mute state
+ */
+ default void onMuteChanged(@NonNull BluetoothDevice device, int mute) {
+ }
+
+ /**
* This method is called when any command to the remote device's ambient control point
* is failed.
*
@@ -319,9 +382,34 @@ public class AmbientVolumeController implements LocalBluetoothProfileManager.Ser
mCallback.onCommandFailed(mDevice);
}
}
- }
- public record RemoteAmbientState(int gainSetting) {
+ @Override
+ public void onMuteChanged(int mute) {
+ if (mCallback != null) {
+ synchronized (mDeviceAmbientStateMap) {
+ RemoteAmbientState previousState = mDeviceAmbientStateMap.get(mDevice);
+ if (previousState.mute != mute) {
+ mCallback.onMuteChanged(mDevice, mute);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onSetMuteFailed() {
+ Log.w(TAG, "onSetMuteFailed, device=" + mDevice);
+ if (mCallback != null) {
+ mCallback.onCommandFailed(mDevice);
+ }
+ }
+ }
+ public record RemoteAmbientState(int gainSetting, int mute) {
+ public boolean isMutable() {
+ return mute != MUTE_DISABLED;
+ }
+ public boolean isMuted() {
+ return mute == MUTE_MUTED;
+ }
}
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
index d2357082da43..abc1d226972b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/AmbientVolumeControllerTest.java
@@ -171,6 +171,37 @@ public class AmbientVolumeControllerTest {
}
@Test
+ public void getMute_verifyGetOnFirstControl() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.getMute(mDevice);
+
+ verify(controls.getFirst()).getMute();
+ }
+
+ @Test
+ public void setMuted_true_verifySetOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.setMuted(mDevice, true);
+
+ for (AudioInputControl control : controls) {
+ verify(control).setMute(AudioInputControl.MUTE_MUTED);
+ }
+ }
+
+ @Test
+ public void setMuted_false_verifySetOnAllControls() {
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+
+ mVolumeController.setMuted(mDevice, false);
+
+ for (AudioInputControl control : controls) {
+ verify(control).setMute(AudioInputControl.MUTE_NOT_MUTED);
+ }
+ }
+
+ @Test
public void ambientCallback_onGainSettingChanged_verifyCallbackIsCalledWhenStateChange() {
AmbientVolumeController.AmbientCallback ambientCallback =
mVolumeController.new AmbientCallback(mDevice, mCallback);
@@ -198,6 +229,33 @@ public class AmbientVolumeControllerTest {
verify(mCallback).onCommandFailed(mDevice);
}
+ @Test
+ public void ambientCallback_onMuteChanged_verifyCallbackIsCalledWhenStateChange() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+ final int testMute = 0;
+ List<AudioInputControl> controls = prepareValidAmbientControls();
+ when(controls.getFirst().getMute()).thenReturn(testMute);
+
+ mVolumeController.refreshAmbientState(mDevice);
+ ambientCallback.onMuteChanged(testMute);
+ verify(mCallback, never()).onMuteChanged(mDevice, testMute);
+
+ final int updatedTestMute = 1;
+ ambientCallback.onMuteChanged(updatedTestMute);
+ verify(mCallback).onMuteChanged(mDevice, updatedTestMute);
+ }
+
+ @Test
+ public void ambientCallback_onSetMuteFailed_verifyCallbackIsCalled() {
+ AmbientVolumeController.AmbientCallback ambientCallback =
+ mVolumeController.new AmbientCallback(mDevice, mCallback);
+
+ ambientCallback.onSetMuteFailed();
+
+ verify(mCallback).onCommandFailed(mDevice);
+ }
+
private List<AudioInputControl> prepareValidAmbientControls() {
List<AudioInputControl> controls = new ArrayList<>();
final int controlsCount = 2;