summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jean-Michel Trivi <jmtrivi@google.com> 2022-09-28 21:46:51 +0000
committer Jean-Michel Trivi <jmtrivi@google.com> 2022-11-23 18:14:47 +0000
commit283a4c7153204ddfc56ae73993f047c5ddf47680 (patch)
tree49b77b0903ed1124bd9d4958437dd9b4234fb54f
parent29c1eeed4b69e5f5411d9325baf5d4dc610a9f4e (diff)
AudioDeviceVolumeManager and VolumeInfo API changes
Context service name for accessing AudioDeviceVolume manager. Address API comments on VolumeInfo and AudioDeviceVolumeManager. Add support in VolumeInfo to know whether it carries an explicit mute/unmute information (vs just default value of isMuted()). Bug: 244326361 Test: atest android.media.audio.cts.AudioDeviceVolumeManagerTest Test: atest android.media.audio.cts.VolumeInfoTest Change-Id: Id718fbd7e0d5795ac480d9188faad4a70fba9808
-rw-r--r--core/api/system-current.txt6
-rw-r--r--core/java/android/app/SystemServiceRegistry.java8
-rw-r--r--core/java/android/content/Context.java13
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java38
-rw-r--r--media/java/android/media/AudioManager.java8
-rwxr-xr-xmedia/java/android/media/IAudioService.aidl5
-rw-r--r--media/java/android/media/VolumeInfo.java103
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java87
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java4
9 files changed, 208 insertions, 64 deletions
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 78b2342bbbee..602b595630b6 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3055,6 +3055,7 @@ package android.content {
field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
+ field public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
field public static final String BACKUP_SERVICE = "backup";
field public static final String BATTERY_STATS_SERVICE = "batterystats";
field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
@@ -6251,7 +6252,7 @@ package android.media {
}
public class AudioDeviceVolumeManager {
- ctor public AudioDeviceVolumeManager(@NonNull android.content.Context);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
}
@@ -6589,8 +6590,9 @@ package android.media {
method public int getMaxVolumeIndex();
method public int getMinVolumeIndex();
method public int getStreamType();
- method @Nullable public android.media.audiopolicy.AudioVolumeGroup getVolumeGroup();
+ method @NonNull public android.media.audiopolicy.AudioVolumeGroup getVolumeGroup();
method public int getVolumeIndex();
+ method public boolean hasMuteCommand();
method public boolean hasStreamType();
method public boolean hasVolumeGroup();
method public boolean isMuted();
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index aaa3d21a0b25..804ebd82bcc2 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -119,6 +119,7 @@ import android.location.CountryDetector;
import android.location.ICountryDetector;
import android.location.ILocationManager;
import android.location.LocationManager;
+import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import android.media.MediaFrameworkInitializer;
import android.media.MediaFrameworkPlatformInitializer;
@@ -344,6 +345,13 @@ public final class SystemServiceRegistry {
return new AudioManager(ctx);
}});
+ registerService(Context.AUDIO_DEVICE_VOLUME_SERVICE, AudioDeviceVolumeManager.class,
+ new CachedServiceFetcher<AudioDeviceVolumeManager>() {
+ @Override
+ public AudioDeviceVolumeManager createService(ContextImpl ctx) {
+ return new AudioDeviceVolumeManager(ctx);
+ }});
+
registerService(Context.MEDIA_ROUTER_SERVICE, MediaRouter.class,
new CachedServiceFetcher<MediaRouter>() {
@Override
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index ae1f68958d0f..d8a784b0f66b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3851,6 +3851,7 @@ public abstract class Context {
WIFI_RTT_RANGING_SERVICE,
NSD_SERVICE,
AUDIO_SERVICE,
+ AUDIO_DEVICE_VOLUME_SERVICE,
AUTH_SERVICE,
FINGERPRINT_SERVICE,
//@hide: FACE_SERVICE,
@@ -4694,6 +4695,18 @@ public abstract class Context {
public static final String AUDIO_SERVICE = "audio";
/**
+ * @hide
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.AudioDeviceVolumeManager} for handling management of audio device
+ * (e.g. speaker, USB headset) volume.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.AudioDeviceVolumeManager
+ */
+ @SystemApi
+ public static final String AUDIO_DEVICE_VOLUME_SERVICE = "audio_device_volume";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.media.MediaTranscodingManager} for transcoding media.
*
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 40f6dc541f86..4e2ce91342fc 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -21,7 +21,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.content.Context;
import android.os.IBinder;
@@ -44,8 +43,7 @@ import java.util.concurrent.Executor;
@SystemApi
public class AudioDeviceVolumeManager {
- // define when using Log.*
- //private static final String TAG = "AudioDeviceVolumeManager";
+ private static final String TAG = "AudioDeviceVolumeManager";
/** @hide
* Indicates no special treatment in the handling of the volume adjustment */
@@ -70,20 +68,15 @@ public class AudioDeviceVolumeManager {
private static IAudioService sService;
private final @NonNull String mPackageName;
- private final @Nullable String mAttributionTag;
/**
+ * @hide
* Constructor
* @param context the Context for the device volume operations
*/
- @SuppressLint("ManagerConstructor")
- // reason for suppression: even though the functionality handled by this class is implemented in
- // AudioService, we want to avoid bloating android.media.AudioManager
- // with @SystemApi functionality
public AudioDeviceVolumeManager(@NonNull Context context) {
Objects.requireNonNull(context);
mPackageName = context.getApplicationContext().getOpPackageName();
- mAttributionTag = context.getApplicationContext().getAttributionTag();
}
/**
@@ -325,13 +318,38 @@ public class AudioDeviceVolumeManager {
* @param ada the device for which volume is to be modified
*/
@SystemApi
+ // TODO alternatively require MODIFY_AUDIO_SYSTEM_SETTINGS when defined
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada) {
try {
- getService().setDeviceVolume(vi, ada, mPackageName, mAttributionTag);
+ getService().setDeviceVolume(vi, ada, mPackageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the volume on the given audio device for the given volume information.
+ * For instance if using a {@link VolumeInfo} configured for {@link AudioManager#STREAM_ALARM},
+ * it will return the alarm volume. When no volume index has ever been set for the given
+ * device, the default volume will be returned (the volume setting that would have been
+ * applied if playback for that use case had started).
+ * @param vi the volume information, only stream-based volumes are supported. Information
+ * other than the stream type is ignored.
+ * @param ada the device for which volume is to be retrieved
+ */
+ @SystemApi
+ // TODO alternatively require MODIFY_AUDIO_SYSTEM_SETTINGS when defined
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada) {
+ try {
+ return getService().getDeviceVolume(vi, ada, mPackageName);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ return VolumeInfo.getDefaultVolumeInfo();
}
/**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 17d7045eacaf..f3931df03327 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1212,7 +1212,13 @@ public class AudioManager {
}
}
- private static boolean isPublicStreamType(int streamType) {
+ /**
+ * @hide
+ * Checks whether a stream type is part of the public SDK
+ * @param streamType
+ * @return true if the stream type is available in SDK
+ */
+ public static boolean isPublicStreamType(int streamType) {
switch (streamType) {
case STREAM_VOICE_CALL:
case STREAM_SYSTEM:
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 7c9e49442564..2e766d59fbb3 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -102,7 +102,10 @@ interface IAudioService {
in String callingPackage, in String attributionTag);
void setDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
- in String callingPackage, in String attributionTag);
+ in String callingPackage);
+
+ VolumeInfo getDeviceVolume(in VolumeInfo vi, in AudioDeviceAttributes ada,
+ in String callingPackage);
oneway void handleVolumeKey(in KeyEvent event, boolean isOnTv,
String callingPackage, String caller);
diff --git a/media/java/android/media/VolumeInfo.java b/media/java/android/media/VolumeInfo.java
index 6b4f604025e2..afb44bbb8b56 100644
--- a/media/java/android/media/VolumeInfo.java
+++ b/media/java/android/media/VolumeInfo.java
@@ -28,7 +28,6 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import java.util.List;
import java.util.Objects;
/**
@@ -36,33 +35,36 @@ import java.util.Objects;
* A class to represent volume information.
* Can be used to represent volume associated with a stream type or {@link AudioVolumeGroup}.
* Volume index is optional when used to represent a category of volume.
- * Index ranges are supported too, making the representation of volume changes agnostic to the
- * range (e.g. can be used to map BT A2DP absolute volume range to internal range).
+ * Volume ranges are supported too, making the representation of volume changes agnostic regarding
+ * the range of values that are supported (e.g. can be used to map BT A2DP absolute volume range to
+ * internal range).
*/
@SystemApi
public final class VolumeInfo implements Parcelable {
private static final String TAG = "VolumeInfo";
private final boolean mUsesStreamType; // false implies AudioVolumeGroup is used
+ private final boolean mHasMuteCommand;
private final boolean mIsMuted;
private final int mVolIndex;
private final int mMinVolIndex;
private final int mMaxVolIndex;
- private final int mVolGroupId;
- private final int mStreamType;
+ private final @Nullable AudioVolumeGroup mVolGroup;
+ private final @AudioManager.PublicStreamTypes int mStreamType;
private static IAudioService sService;
private static VolumeInfo sDefaultVolumeInfo;
- private VolumeInfo(boolean usesStreamType, boolean isMuted, int volIndex,
- int minVolIndex, int maxVolIndex,
- int volGroupId, int streamType) {
+ private VolumeInfo(boolean usesStreamType, boolean hasMuteCommand, boolean isMuted,
+ int volIndex, int minVolIndex, int maxVolIndex,
+ AudioVolumeGroup volGroup, int streamType) {
mUsesStreamType = usesStreamType;
+ mHasMuteCommand = hasMuteCommand;
mIsMuted = isMuted;
mVolIndex = volIndex;
mMinVolIndex = minVolIndex;
mMaxVolIndex = maxVolIndex;
- mVolGroupId = volGroupId;
+ mVolGroup = volGroup;
mStreamType = streamType;
}
@@ -79,8 +81,10 @@ public final class VolumeInfo implements Parcelable {
/**
* Returns the associated stream type, or will throw if {@link #hasStreamType()} returned false.
* @return a stream type value, see AudioManager.STREAM_*
+ * @throws IllegalStateException when called on a VolumeInfo not configured for
+ * stream types.
*/
- public int getStreamType() {
+ public @AudioManager.PublicStreamTypes int getStreamType() {
if (!mUsesStreamType) {
throw new IllegalStateException("VolumeInfo doesn't use stream types");
}
@@ -99,24 +103,28 @@ public final class VolumeInfo implements Parcelable {
/**
* Returns the associated volume group, or will throw if {@link #hasVolumeGroup()} returned
* false.
- * @return the volume group corresponding to this VolumeInfo, or null if an error occurred
- * in the volume group management
+ * @return the volume group corresponding to this VolumeInfo
+ * @throws IllegalStateException when called on a VolumeInfo not configured for
+ * volume groups.
*/
- public @Nullable AudioVolumeGroup getVolumeGroup() {
+ public @NonNull AudioVolumeGroup getVolumeGroup() {
if (mUsesStreamType) {
throw new IllegalStateException("VolumeInfo doesn't use AudioVolumeGroup");
}
- List<AudioVolumeGroup> volGroups = AudioVolumeGroup.getAudioVolumeGroups();
- for (AudioVolumeGroup group : volGroups) {
- if (group.getId() == mVolGroupId) {
- return group;
- }
- }
- return null;
+ return mVolGroup;
+ }
+
+ /**
+ * Return whether this instance is conveying a mute state
+ * @return true if the muted state was explicitly set for this instance
+ */
+ public boolean hasMuteCommand() {
+ return mHasMuteCommand;
}
/**
- * Returns whether this instance is conveying a mute state.
+ * Returns whether this instance is conveying a mute state that was explicitly set
+ * by {@link Builder#setMuted(boolean)}, false otherwise
* @return true if the volume state is muted
*/
public boolean isMuted() {
@@ -183,18 +191,21 @@ public final class VolumeInfo implements Parcelable {
*/
public static final class Builder {
private boolean mUsesStreamType = true; // false implies AudioVolumeGroup is used
- private int mStreamType = AudioManager.STREAM_MUSIC;
+ private @AudioManager.PublicStreamTypes int mStreamType = AudioManager.STREAM_MUSIC;
+ private boolean mHasMuteCommand = false;
private boolean mIsMuted = false;
private int mVolIndex = INDEX_NOT_SET;
private int mMinVolIndex = INDEX_NOT_SET;
private int mMaxVolIndex = INDEX_NOT_SET;
- private int mVolGroupId = -Integer.MIN_VALUE;
+ private @Nullable AudioVolumeGroup mVolGroup;
/**
* Builder constructor for stream type-based VolumeInfo
*/
- public Builder(int streamType) {
- // TODO validate stream type
+ public Builder(@AudioManager.PublicStreamTypes int streamType) {
+ if (!AudioManager.isPublicStreamType(streamType)) {
+ throw new IllegalArgumentException("Not a valid public stream type " + streamType);
+ }
mUsesStreamType = true;
mStreamType = streamType;
}
@@ -206,7 +217,7 @@ public final class VolumeInfo implements Parcelable {
Objects.requireNonNull(volGroup);
mUsesStreamType = false;
mStreamType = -Integer.MIN_VALUE;
- mVolGroupId = volGroup.getId();
+ mVolGroup = volGroup;
}
/**
@@ -217,11 +228,12 @@ public final class VolumeInfo implements Parcelable {
Objects.requireNonNull(info);
mUsesStreamType = info.mUsesStreamType;
mStreamType = info.mStreamType;
+ mHasMuteCommand = info.mHasMuteCommand;
mIsMuted = info.mIsMuted;
mVolIndex = info.mVolIndex;
mMinVolIndex = info.mMinVolIndex;
mMaxVolIndex = info.mMaxVolIndex;
- mVolGroupId = info.mVolGroupId;
+ mVolGroup = info.mVolGroup;
}
/**
@@ -230,6 +242,7 @@ public final class VolumeInfo implements Parcelable {
* @return the same builder instance
*/
public @NonNull Builder setMuted(boolean isMuted) {
+ mHasMuteCommand = true;
mIsMuted = isMuted;
return this;
}
@@ -239,7 +252,6 @@ public final class VolumeInfo implements Parcelable {
* @param volIndex a 0 or greater value, or {@link #INDEX_NOT_SET} if unknown
* @return the same builder instance
*/
- // TODO should we allow muted true + volume index set? (useful when toggling mute on/off?)
public @NonNull Builder setVolumeIndex(int volIndex) {
if (volIndex != INDEX_NOT_SET && volIndex < 0) {
throw new IllegalArgumentException("Volume index cannot be negative");
@@ -294,9 +306,9 @@ public final class VolumeInfo implements Parcelable {
throw new IllegalArgumentException("Min volume index:" + mMinVolIndex
+ " greater than max index:" + mMaxVolIndex);
}
- return new VolumeInfo(mUsesStreamType, mIsMuted,
+ return new VolumeInfo(mUsesStreamType, mHasMuteCommand, mIsMuted,
mVolIndex, mMinVolIndex, mMaxVolIndex,
- mVolGroupId, mStreamType);
+ mVolGroup, mStreamType);
}
}
@@ -304,8 +316,8 @@ public final class VolumeInfo implements Parcelable {
// Parcelable
@Override
public int hashCode() {
- return Objects.hash(mUsesStreamType, mStreamType, mIsMuted,
- mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroupId);
+ return Objects.hash(mUsesStreamType, mHasMuteCommand, mStreamType, mIsMuted,
+ mVolIndex, mMinVolIndex, mMaxVolIndex, mVolGroup);
}
@Override
@@ -316,19 +328,20 @@ public final class VolumeInfo implements Parcelable {
VolumeInfo that = (VolumeInfo) o;
return ((mUsesStreamType == that.mUsesStreamType)
&& (mStreamType == that.mStreamType)
- && (mIsMuted == that.mIsMuted)
- && (mVolIndex == that.mVolIndex)
- && (mMinVolIndex == that.mMinVolIndex)
- && (mMaxVolIndex == that.mMaxVolIndex)
- && (mVolGroupId == that.mVolGroupId));
+ && (mHasMuteCommand == that.mHasMuteCommand)
+ && (mIsMuted == that.mIsMuted)
+ && (mVolIndex == that.mVolIndex)
+ && (mMinVolIndex == that.mMinVolIndex)
+ && (mMaxVolIndex == that.mMaxVolIndex)
+ && Objects.equals(mVolGroup, that.mVolGroup));
}
@Override
public String toString() {
return new String("VolumeInfo:"
+ (mUsesStreamType ? (" streamType:" + mStreamType)
- : (" volGroupId" + mVolGroupId))
- + " muted:" + mIsMuted
+ : (" volGroup:" + mVolGroup))
+ + (mHasMuteCommand ? (" muted:" + mIsMuted) : ("[no mute cmd]"))
+ ((mVolIndex != INDEX_NOT_SET) ? (" volIndex:" + mVolIndex) : "")
+ ((mMinVolIndex != INDEX_NOT_SET) ? (" min:" + mMinVolIndex) : "")
+ ((mMaxVolIndex != INDEX_NOT_SET) ? (" max:" + mMaxVolIndex) : ""));
@@ -343,21 +356,29 @@ public final class VolumeInfo implements Parcelable {
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBoolean(mUsesStreamType);
dest.writeInt(mStreamType);
+ dest.writeBoolean(mHasMuteCommand);
dest.writeBoolean(mIsMuted);
dest.writeInt(mVolIndex);
dest.writeInt(mMinVolIndex);
dest.writeInt(mMaxVolIndex);
- dest.writeInt(mVolGroupId);
+ if (!mUsesStreamType) {
+ mVolGroup.writeToParcel(dest, 0 /*ignored*/);
+ }
}
private VolumeInfo(@NonNull Parcel in) {
mUsesStreamType = in.readBoolean();
mStreamType = in.readInt();
+ mHasMuteCommand = in.readBoolean();
mIsMuted = in.readBoolean();
mVolIndex = in.readInt();
mMinVolIndex = in.readInt();
mMaxVolIndex = in.readInt();
- mVolGroupId = in.readInt();
+ if (!mUsesStreamType) {
+ mVolGroup = AudioVolumeGroup.CREATOR.createFromParcel(in);
+ } else {
+ mVolGroup = null;
+ }
}
public static final @NonNull Parcelable.Creator<VolumeInfo> CREATOR =
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4fda2336403a..6af6d819ee10 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3622,6 +3622,18 @@ public class AudioService extends IAudioService.Stub
}
}
+ // TODO enforce MODIFY_AUDIO_SYSTEM_SETTINGS when defined
+ private void enforceModifyAudioRoutingOrSystemSettingsPermission() {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ != PackageManager.PERMISSION_GRANTED
+ /*&& mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_AUDIO_SYSTEM_SETTINGS)
+ != PackageManager.PERMISSION_DENIED*/) {
+ throw new SecurityException(
+ "Missing MODIFY_AUDIO_ROUTING or MODIFY_AUDIO_SYSTEM_SETTINGS permission");
+ }
+ }
+
private void enforceAccessUltrasoundPermission() {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3734,20 +3746,35 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
- * Part of service interface, check permissions and parameters here */
+ * Part of service interface, check permissions and parameters here
+ * Note calling package is for logging purposes only, not to be trusted
+ */
public void setDeviceVolume(@NonNull VolumeInfo vi, @NonNull AudioDeviceAttributes ada,
- @NonNull String callingPackage, @Nullable String attributionTag) {
- enforceModifyAudioRoutingPermission();
+ @NonNull String callingPackage) {
+ enforceModifyAudioRoutingOrSystemSettingsPermission();
Objects.requireNonNull(vi);
Objects.requireNonNull(ada);
Objects.requireNonNull(callingPackage);
+
if (!vi.hasStreamType()) {
Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
return;
}
int index = vi.getVolumeIndex();
- if (index == VolumeInfo.INDEX_NOT_SET) {
- throw new IllegalArgumentException("changing device volume requires a volume index");
+ if (index == VolumeInfo.INDEX_NOT_SET && !vi.hasMuteCommand()) {
+ throw new IllegalArgumentException(
+ "changing device volume requires a volume index or mute command");
+ }
+
+ // TODO handle unmuting if current audio device
+ // if a stream is not muted but the VolumeInfo is for muting, set the volume index
+ // for the device to min volume
+ if (vi.hasMuteCommand() && vi.isMuted() && !isStreamMute(vi.getStreamType())) {
+ setStreamVolumeWithAttributionInt(vi.getStreamType(),
+ mStreamStates[vi.getStreamType()].getMinIndex(),
+ /*flags*/ 0,
+ ada, callingPackage, null);
+ return;
}
if (vi.getMinVolumeIndex() == VolumeInfo.INDEX_NOT_SET
@@ -3769,7 +3796,7 @@ public class AudioService extends IAudioService.Stub
}
}
setStreamVolumeWithAttributionInt(vi.getStreamType(), index, /*flags*/ 0,
- ada, callingPackage, attributionTag);
+ ada, callingPackage, null);
}
/** Retain API for unsupported app usage */
@@ -4679,6 +4706,36 @@ public class AudioService extends IAudioService.Stub
}
}
+ /**
+ * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
+ */
+ public @NonNull VolumeInfo getDeviceVolume(@NonNull VolumeInfo vi,
+ @NonNull AudioDeviceAttributes ada, @NonNull String callingPackage) {
+ enforceModifyAudioRoutingOrSystemSettingsPermission();
+ Objects.requireNonNull(vi);
+ Objects.requireNonNull(ada);
+ Objects.requireNonNull(callingPackage);
+ if (!vi.hasStreamType()) {
+ Log.e(TAG, "Unsupported non-stream type based VolumeInfo", new Exception());
+ return getDefaultVolumeInfo();
+ }
+
+ int streamType = vi.getStreamType();
+ final VolumeInfo.Builder vib = new VolumeInfo.Builder(vi);
+ vib.setMinVolumeIndex(mStreamStates[streamType].mIndexMin);
+ vib.setMaxVolumeIndex(mStreamStates[streamType].mIndexMax);
+ synchronized (VolumeStreamState.class) {
+ final int index;
+ if (isFixedVolumeDevice(ada.getInternalType())) {
+ index = (mStreamStates[streamType].mIndexMax + 5) / 10;
+ } else {
+ index = (mStreamStates[streamType].getIndex(ada.getInternalType()) + 5) / 10;
+ }
+ vib.setVolumeIndex(index);
+ return vib.setMuted(mStreamStates[streamType].mIsMuted).build();
+ }
+ }
+
/** @see AudioManager#getStreamMaxVolume(int) */
public int getStreamMaxVolume(int streamType) {
ensureValidStreamType(streamType);
@@ -4719,7 +4776,6 @@ public class AudioService extends IAudioService.Stub
sDefaultVolumeInfo = new VolumeInfo.Builder(AudioSystem.STREAM_MUSIC)
.setMinVolumeIndex(getStreamMinVolume(AudioSystem.STREAM_MUSIC))
.setMaxVolumeIndex(getStreamMaxVolume(AudioSystem.STREAM_MUSIC))
- .setMuted(false)
.build();
}
return sDefaultVolumeInfo;
@@ -7960,6 +8016,23 @@ public class AudioService extends IAudioService.Stub
}
}
+ public @NonNull VolumeInfo getVolumeInfo(int device) {
+ synchronized (VolumeStreamState.class) {
+ int index = mIndexMap.get(device, -1);
+ if (index == -1) {
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ index = mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ final VolumeInfo vi = new VolumeInfo.Builder(mStreamType)
+ .setMinVolumeIndex(mIndexMin)
+ .setMaxVolumeIndex(mIndexMax)
+ .setVolumeIndex(index)
+ .setMuted(isFullyMuted())
+ .build();
+ return vi;
+ }
+ }
+
public boolean hasIndexForDevice(int device) {
synchronized (VolumeStreamState.class) {
return (mIndexMap.get(device, -1) != -1);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 7acb6d66c834..7f54b63bfe4b 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -84,12 +84,12 @@ public class AudioDeviceVolumeManagerTest {
final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
/*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
- mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName, TAG);
+ mAudioService.setDeviceVolume(volMin, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
AudioManager.STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
- mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName, TAG);
+ mAudioService.setDeviceVolume(volMid, usbDevice, mPackageName);
mTestLooper.dispatchAll();
verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
AudioManager.STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);