diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 457fccf0a24ed..f7a97e00fbff4 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -172,6 +172,11 @@ public final class AudioPlaybackConfiguration implements Parcelable { * The state used to update port id, does not actually change the state of the player */ public static final int PLAYER_UPDATE_PORT_ID = 6; + /** + * @hide + * Used to update the mute state of a player through its port id + */ + public static final int PLAYER_UPDATE_MUTED = 7; /** @hide */ @IntDef({ @@ -183,6 +188,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { PLAYER_STATE_STOPPED, PLAYER_UPDATE_DEVICE_ID, PLAYER_UPDATE_PORT_ID, + PLAYER_UPDATE_MUTED, }) @Retention(RetentionPolicy.SOURCE) public @interface PlayerState {} @@ -198,11 +204,50 @@ public final class AudioPlaybackConfiguration implements Parcelable { case PLAYER_STATE_STOPPED: return "PLAYER_STATE_STOPPED"; case PLAYER_UPDATE_DEVICE_ID: return "PLAYER_UPDATE_DEVICE_ID"; case PLAYER_UPDATE_PORT_ID: return "PLAYER_UPDATE_PORT_ID"; + case PLAYER_UPDATE_MUTED: return "PLAYER_UPDATE_MUTED"; default: return "invalid state " + state; } } + /** + * @hide + * Used to update the mute state of a player through its port ID. Must be kept in sync with + * frameworks/native/include/audiomanager/AudioManager.h + */ + public static final String EXTRA_PLAYER_EVENT_MUTE = + "android.media.extra.PLAYER_EVENT_MUTE"; + + /** + * @hide + * Flag used when muted by master volume. + */ + public static final int PLAYER_MUTE_MASTER = (1 << 0); + /** + * @hide + * Flag used when muted by stream volume. + */ + public static final int PLAYER_MUTE_STREAM_VOLUME = (1 << 1); + /** + * @hide + * Flag used when muted by stream mute. + */ + public static final int PLAYER_MUTE_STREAM_MUTED = (1 << 2); + /** + * @hide + * Flag used when playback is restricted by AppOps manager with OP_PLAY_AUDIO. + */ + public static final int PLAYER_MUTE_PLAYBACK_RESTRICTED = (1 << 3); + + /** @hide */ + @IntDef( + flag = true, + value = {PLAYER_MUTE_MASTER, PLAYER_MUTE_STREAM_VOLUME, PLAYER_MUTE_STREAM_MUTED, + PLAYER_MUTE_PLAYBACK_RESTRICTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface PlayerMuteEvent { + } + // immutable data private final int mPlayerIId; @@ -678,6 +723,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { case PLAYER_STATE_STOPPED: return "stopped"; case PLAYER_UPDATE_DEVICE_ID: return "device updated"; case PLAYER_UPDATE_PORT_ID: return "port updated"; + case PLAYER_UPDATE_MUTED: return "muted updated"; default: return "unknown player state - FIXME"; } diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 52d5619656e88..7c9e494425641 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -54,6 +54,7 @@ import android.media.audiopolicy.AudioVolumeGroup; import android.media.audiopolicy.IAudioPolicyCallback; import android.media.projection.IMediaProjection; import android.net.Uri; +import android.os.PersistableBundle; import android.os.UserHandle; import android.view.KeyEvent; @@ -86,6 +87,8 @@ interface IAudioService { oneway void playerSessionId(in int piid, in int sessionId); + oneway void portEvent(in int portId, in int event, in @nullable PersistableBundle extras); + // Java-only methods below. void adjustStreamVolume(int streamType, int direction, int flags, String callingPackage); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d2c27c06aab70..f0a7df40d5909 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -134,6 +134,7 @@ import android.os.HwBinder; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallbackList; @@ -11141,6 +11142,16 @@ public class AudioService extends IAudioService.Stub mPlaybackMonitor.playerEvent(piid, event, eventValue, Binder.getCallingUid()); } + /** + * Update event for port id + * @param portId Port id to update + * @param event The new event for the given port + * @param extras Bundle of extra values to describe the event + */ + public void portEvent(int portId, int event, @Nullable PersistableBundle extras) { + mPlaybackMonitor.portEvent(portId, event, extras, Binder.getCallingUid()); + } + public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio) { mPlaybackMonitor.playerHasOpPlayAudio(piid, hasOpPlayAudio, Binder.getCallingUid()); } diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 02036db4551ec..e722f76a61da6 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -16,6 +16,14 @@ package com.android.server.audio; +import static android.media.AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_MUTE; +import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_MASTER; +import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_PLAYBACK_RESTRICTED; +import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_MUTED; +import static android.media.AudioPlaybackConfiguration.PLAYER_MUTE_STREAM_VOLUME; +import static android.media.AudioPlaybackConfiguration.PLAYER_PIID_INVALID; +import static android.media.AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -24,6 +32,7 @@ import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; +import android.media.AudioPlaybackConfiguration.PlayerMuteEvent; import android.media.AudioSystem; import android.media.IPlaybackConfigDispatcher; import android.media.PlayerBase; @@ -33,7 +42,9 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.Message; +import android.os.PersistableBundle; import android.os.RemoteException; +import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; @@ -349,6 +360,56 @@ public final class PlaybackActivityMonitor } } + /** + * Update event for port + * @param portId Port id to update + * @param event The new port event + * @param extras The values associated with this event + * @param binderUid Calling binder uid + */ + public void portEvent(int portId, int event, @Nullable PersistableBundle extras, + int binderUid) { + if (!UserHandle.isCore(binderUid)) { + Log.e(TAG, "Forbidden operation from uid " + binderUid); + return; + } + + if (DEBUG) { + Log.v(TAG, TextUtils.formatSimple("portEvent(portId=%d, event=%s, extras=%s)", + portId, AudioPlaybackConfiguration.playerStateToString(event), extras)); + } + + synchronized (mPlayerLock) { + int piid = mPortIdToPiid.get(portId, PLAYER_PIID_INVALID); + if (piid == PLAYER_PIID_INVALID) { + if (DEBUG) { + Log.v(TAG, "No piid assigned for invalid/internal port id " + portId); + } + return; + } + final AudioPlaybackConfiguration apc = mPlayers.get(piid); + if (apc == null) { + if (DEBUG) { + Log.v(TAG, "No AudioPlaybackConfiguration assigned for piid " + piid); + } + return; + } + + if (apc.getPlayerType() + == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { + // FIXME SoundPool not ready for state reporting + return; + } + + if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) { + mEventHandler.sendMessage( + mEventHandler.obtainMessage(MSG_L_UPDATE_PLAYER_MUTED_EVENT, piid, + portId, + extras)); + } + } + } + public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) { // no check on UID yet because this is only for logging at the moment sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); @@ -1062,18 +1123,32 @@ public final class PlaybackActivityMonitor @Override public String eventToString() { - if (mEvent == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) { - return AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent) + " portId:" - + mEventValue + " mapped to player piid:" + mPlayerIId; - } - StringBuilder builder = new StringBuilder("player piid:").append(mPlayerIId).append( - " state:") + " event:") .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent)); - if (mEventValue != 0) { - builder.append(" DeviceId:").append(mEventValue); + + switch (mEvent) { + case AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID: + return AudioPlaybackConfiguration.toLogFriendlyPlayerState(mEvent) + " portId:" + + mEventValue + " mapped to player piid:" + mPlayerIId; + case AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID: + if (mEventValue != 0) { + builder.append(" deviceId:").append(mEventValue); + } + return builder.toString(); + case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED: + builder.append(" muteFromMasterMute:").append( + (mEventValue & PLAYER_MUTE_MASTER) != 0); + builder.append(" muteFromStreamVolume:").append( + (mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0); + builder.append(" muteFromStreamMute:").append( + (mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0); + builder.append(" muteFromPlaybackRestricted:").append( + (mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0); + return builder.toString(); + default: + return builder.toString(); } - return builder.toString(); } } @@ -1322,12 +1397,22 @@ public final class PlaybackActivityMonitor */ private static final int MSG_L_UPDATE_PORT_EVENT = 2; + /** + * event for player getting muted + * args: + * msg.arg1: piid + * msg.arg2: port id + * msg.obj: extras describing the mute reason + * type: PersistableBundle + */ + private static final int MSG_L_UPDATE_PLAYER_MUTED_EVENT = 3; + /** * clear all ports assigned to a given piid * args: * msg.arg1: the piid */ - private static final int MSG_L_CLEAR_PORTS_FOR_PIID = 3; + private static final int MSG_L_CLEAR_PORTS_FOR_PIID = 4; private void initEventHandler() { mEventThread = new HandlerThread(TAG); @@ -1349,6 +1434,18 @@ public final class PlaybackActivityMonitor mPortIdToPiid.put(/*portId*/msg.arg1, /*piid*/msg.arg2); } break; + case MSG_L_UPDATE_PLAYER_MUTED_EVENT: + // TODO: replace PersistableBundle with own struct + PersistableBundle extras = (PersistableBundle) msg.obj; + if (extras == null) { + Log.w(TAG, "Received mute event with no extras"); + break; + } + @PlayerMuteEvent int eventValue = extras.getInt(EXTRA_PLAYER_EVENT_MUTE); + + sEventLogger.log(new PlayerEvent(/*piid=*/msg.arg1, PLAYER_UPDATE_MUTED, + eventValue)); + break; case MSG_L_CLEAR_PORTS_FOR_PIID: int piid = msg.arg1; if (piid == AudioPlaybackConfiguration.PLAYER_PIID_INVALID) {