summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--media/java/android/media/AudioDeviceAttributes.java8
-rw-r--r--media/java/android/media/AudioDeviceInfo.java22
-rw-r--r--media/java/android/media/AudioManager.java144
-rw-r--r--media/java/android/media/IAudioService.aidl5
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java114
5 files changed, 287 insertions, 6 deletions
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index f5b08061f35d..0ab62c14ab9f 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -99,11 +99,11 @@ public final class AudioDeviceAttributes implements Parcelable {
if (role != ROLE_OUTPUT && role != ROLE_INPUT) {
throw new IllegalArgumentException("Invalid role " + role);
}
- if (role == ROLE_OUTPUT && !AudioDeviceInfo.isValidAudioDeviceTypeOut(type)) {
- throw new IllegalArgumentException("Invalid output device type " + type);
+ if (role == ROLE_OUTPUT) {
+ AudioDeviceInfo.enforceValidAudioDeviceTypeOut(type);
}
- if (role == ROLE_INPUT && !AudioDeviceInfo.isValidAudioDeviceTypeIn(type)) {
- throw new IllegalArgumentException("Invalid input device type " + type);
+ if (role == ROLE_INPUT) {
+ AudioDeviceInfo.enforceValidAudioDeviceTypeIn(type);
}
mRole = role;
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index db2a1e8b6e7c..6b0e17d84357 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -280,6 +280,28 @@ public final class AudioDeviceInfo {
}
}
+ /**
+ * @hide
+ * Throws IAE on an invalid output device type
+ * @param type
+ */
+ public static void enforceValidAudioDeviceTypeOut(int type) {
+ if (!isValidAudioDeviceTypeOut(type)) {
+ throw new IllegalArgumentException("Illegal output device type " + type);
+ }
+ }
+
+ /**
+ * @hide
+ * Throws IAE on an invalid input device type
+ * @param type
+ */
+ public static void enforceValidAudioDeviceTypeIn(int type) {
+ if (!isValidAudioDeviceTypeIn(type)) {
+ throw new IllegalArgumentException("Illegal input device type " + type);
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 7408987e465e..8477aa3ca26e 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4571,6 +4571,150 @@ public class AudioManager {
}
}
+ /**
+ * @hide
+ * Volume behavior for an audio device where a software attenuation is applied
+ * @see #setDeviceVolumeBehavior(int, String, int)
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is always set to provide no attenuation
+ * nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(int, String, int)
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is either set to muted, or to provide
+ * no attenuation nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(int, String, int)
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol such as BT AVRCP.
+ * @see #setDeviceVolumeBehavior(int, String, int)
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
+ * normal vs in phone call).
+ * @see #setMode(int)
+ * @see #setDeviceVolumeBehavior(int, String, int)
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
+
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehavior {}
+
+ /**
+ * @hide
+ * Throws IAE on an invalid volume behavior value
+ * @param volumeBehavior behavior value to check
+ */
+ public static void enforceValidVolumeBehavior(int volumeBehavior) {
+ switch (volumeBehavior) {
+ case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ case DEVICE_VOLUME_BEHAVIOR_FULL:
+ case DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ return;
+ default:
+ throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
+ }
+ }
+
+ /**
+ * @hide
+ * Sets the volume behavior for an audio output device.
+ * @param deviceType the type of audio device to be affected. Currently only supports
+ * {@link AudioDeviceInfo#TYPE_HDMI}, {@link AudioDeviceInfo#TYPE_HDMI_ARC},
+ * {@link AudioDeviceInfo#TYPE_LINE_DIGITAL} and {@link AudioDeviceInfo#TYPE_AUX_LINE}
+ * @param deviceAddress the address of the device, if any
+ * @param deviceVolumeBehavior one of the device behaviors
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setDeviceVolumeBehavior(int deviceType, @Nullable String deviceAddress,
+ @DeviceVolumeBehavior int deviceVolumeBehavior) {
+ setDeviceVolumeBehavior(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ deviceType, deviceAddress), deviceVolumeBehavior);
+ }
+
+ /**
+ * @hide
+ * Sets the volume behavior for an audio output device.
+ * @param device the device to be affected. Currently only supports devices of type
+ * {@link AudioDeviceInfo#TYPE_HDMI}, {@link AudioDeviceInfo#TYPE_HDMI_ARC},
+ * {@link AudioDeviceInfo#TYPE_LINE_DIGITAL} and {@link AudioDeviceInfo#TYPE_AUX_LINE}
+ * @param deviceVolumeBehavior one of the device behaviors
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @DeviceVolumeBehavior int deviceVolumeBehavior) {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ enforceValidVolumeBehavior(deviceVolumeBehavior);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ service.setDeviceVolumeBehavior(device, deviceVolumeBehavior,
+ mApplicationContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the volume device behavior for the given device type and address
+ * @param deviceType an audio output device type, as defined in {@link AudioDeviceInfo}
+ * @param deviceAddress the address of the audio device, if any.
+ * @return the volume behavior for the device
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @DeviceVolumeBehavior int getDeviceVolumeBehavior(int deviceType,
+ @Nullable String deviceAddress) {
+ // verify arguments
+ AudioDeviceInfo.enforceValidAudioDeviceTypeOut(deviceType);
+ return getDeviceVolumeBehavior(new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ deviceType, deviceAddress));
+ }
+
+ /**
+ * @hide
+ * Returns the volume device behavior for the given audio device
+ * @param device the audio device
+ * @return the volume behavior for the device
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @DeviceVolumeBehavior int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device)
+ {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ return service.getDeviceVolumeBehavior(device);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/**
* Indicate wired accessory connection state change.
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 453a5d8a5b7e..bb10e1fe2f2c 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -294,6 +294,11 @@ interface IAudioService {
oneway void setRttEnabled(in boolean rttEnabled);
+ void setDeviceVolumeBehavior(in AudioDeviceAttributes device,
+ in int deviceVolumeBehavior, in String pkgName);
+
+ int getDeviceVolumeBehavior(in AudioDeviceAttributes device);
+
// WARNING: read warning at top of file, new methods that need to be used by native
// code via IAudioManager.h need to be added to the top section.
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index f6cdaebc333a..d85028d6f5b7 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -2332,8 +2332,7 @@ public class AudioService extends IAudioService.Stub
}
private void enforceModifyAudioRoutingPermission() {
- if (mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission");
}
@@ -4610,6 +4609,117 @@ public class AudioService extends IAudioService.Stub
observeDevicesForStreams(-1);
}
+ /**
+ * @see AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @param device the audio device to be affected
+ * @param deviceVolumeBehavior one of the device behaviors
+ */
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
+ // verify permissions
+ enforceModifyAudioRoutingPermission();
+ // verify arguments
+ Objects.requireNonNull(device);
+ AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
+ if (pkgName == null) {
+ pkgName = "";
+ }
+ // translate Java device type to native device type (for the devices masks for full / fixed)
+ final int type;
+ switch (device.getType()) {
+ case AudioDeviceInfo.TYPE_HDMI:
+ type = AudioSystem.DEVICE_OUT_HDMI;
+ break;
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ type = AudioSystem.DEVICE_OUT_HDMI_ARC;
+ break;
+ case AudioDeviceInfo.TYPE_LINE_DIGITAL:
+ type = AudioSystem.DEVICE_OUT_SPDIF;
+ break;
+ case AudioDeviceInfo.TYPE_AUX_LINE:
+ type = AudioSystem.DEVICE_OUT_LINE;
+ break;
+ default:
+ // unsupported for now
+ throw new IllegalArgumentException("Unsupported device type " + device.getType());
+ }
+ // update device masks based on volume behavior
+ switch (deviceVolumeBehavior) {
+ case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ mFullVolumeDevices.remove(type);
+ mFixedVolumeDevices.remove(type);
+ break;
+ case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
+ mFullVolumeDevices.remove(type);
+ mFixedVolumeDevices.add(type);
+ break;
+ case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
+ mFullVolumeDevices.add(type);
+ mFixedVolumeDevices.remove(type);
+ break;
+ case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ throw new IllegalArgumentException("Absolute volume unsupported for now");
+ }
+ // log event and caller
+ sDeviceLogger.log(new AudioEventLogger.StringEvent(
+ "Volume behavior " + deviceVolumeBehavior
+ + " for dev=0x" + Integer.toHexString(type) + " by pkg:" + pkgName));
+ // make sure we have a volume entry for this device, and that volume is updated according
+ // to volume behavior
+ checkAddAllFixedVolumeDevices(type, "setDeviceVolumeBehavior:" + pkgName);
+ }
+
+ /**
+ * @see AudioManager#getDeviceVolumeBehavior(AudioDeviceAttributes)
+ * @param device the audio output device type
+ * @return the volume behavior for the device
+ */
+ public @AudioManager.DeviceVolumeBehavior int getDeviceVolumeBehavior(
+ @NonNull AudioDeviceAttributes device) {
+ // verify permissions
+ enforceModifyAudioRoutingPermission();
+ // translate Java device type to native device type (for the devices masks for full / fixed)
+ final int type;
+ switch (device.getType()) {
+ case AudioDeviceInfo.TYPE_HEARING_AID:
+ type = AudioSystem.DEVICE_OUT_HEARING_AID;
+ break;
+ case AudioDeviceInfo.TYPE_BLUETOOTH_A2DP:
+ type = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+ break;
+ case AudioDeviceInfo.TYPE_HDMI:
+ type = AudioSystem.DEVICE_OUT_HDMI;
+ break;
+ case AudioDeviceInfo.TYPE_HDMI_ARC:
+ type = AudioSystem.DEVICE_OUT_HDMI_ARC;
+ break;
+ case AudioDeviceInfo.TYPE_LINE_DIGITAL:
+ type = AudioSystem.DEVICE_OUT_SPDIF;
+ break;
+ case AudioDeviceInfo.TYPE_AUX_LINE:
+ type = AudioSystem.DEVICE_OUT_LINE;
+ break;
+ default:
+ // unsupported for now
+ throw new IllegalArgumentException("Unsupported device type " + device.getType());
+ }
+ if ((mFullVolumeDevices.contains(type))) {
+ return AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL;
+ }
+ if ((mFixedVolumeDevices.contains(type))) {
+ return AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED;
+ }
+ if ((mAbsVolumeMultiModeCaseDevices.contains(type))) {
+ return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
+ }
+ if (type == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP
+ && mDeviceBroker.isAvrcpAbsoluteVolumeSupported()) {
+ return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
+ }
+ return AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE;
+ }
+
/*package*/ static final int CONNECTION_STATE_DISCONNECTED = 0;
/*package*/ static final int CONNECTION_STATE_CONNECTED = 1;
/**