Merge "AudioManager: set/get audio device volume behavior" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-04-03 16:27:14 +00:00
committed by Android (Google) Code Review
5 changed files with 287 additions and 6 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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.
}

View File

@@ -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;
/**