diff options
| -rw-r--r-- | media/java/android/media/AudioDeviceAttributes.java | 8 | ||||
| -rw-r--r-- | media/java/android/media/AudioDeviceInfo.java | 22 | ||||
| -rw-r--r-- | media/java/android/media/AudioManager.java | 144 | ||||
| -rw-r--r-- | media/java/android/media/IAudioService.aidl | 5 | ||||
| -rw-r--r-- | services/core/java/com/android/server/audio/AudioService.java | 114 |
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; /** |