Loading core/api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -6683,6 +6683,7 @@ package android.media { } public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int getChannelMask(); method public int getClientPid(); method public int getClientUid(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMutedBy(); Loading @@ -6690,9 +6691,11 @@ package android.media { method public android.media.PlayerProxy getPlayerProxy(); method public int getPlayerState(); method public int getPlayerType(); method @IntRange(from=0) public int getSampleRate(); method @IntRange(from=0) public int getSessionId(); method public boolean isActive(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted(); method public boolean isSpatialized(); field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1 media/java/android/media/AudioPlaybackConfiguration.java +257 −60 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -178,6 +180,11 @@ public final class AudioPlaybackConfiguration implements Parcelable { * Used to update the mute state of a player through its port id */ public static final int PLAYER_UPDATE_MUTED = 7; /** * @hide * Used to update the spatialization status and format of a player through its port id */ public static final int PLAYER_UPDATE_FORMAT = 8; /** @hide */ @IntDef({ Loading @@ -190,6 +197,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { PLAYER_UPDATE_DEVICE_ID, PLAYER_UPDATE_PORT_ID, PLAYER_UPDATE_MUTED, PLAYER_UPDATE_FORMAT, }) @Retention(RetentionPolicy.SOURCE) public @interface PlayerState {} Loading @@ -206,11 +214,33 @@ public final class AudioPlaybackConfiguration implements Parcelable { 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"; case PLAYER_UPDATE_FORMAT: return "PLAYER_UPDATE_FORMAT"; default: return "invalid state " + state; } } /** * @hide * Used to update the spatialization status 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_SPATIALIZED = "android.media.extra.PLAYER_EVENT_SPATIALIZED"; /** * @hide * Used to update the sample rate 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_SAMPLE_RATE = "android.media.extra.PLAYER_EVENT_SAMPLE_RATE"; /** * @hide * Used to update the channel mask 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_CHANNEL_MASK = "android.media.extra.PLAYER_EVENT_CHANNEL_MASK"; /** * @hide * Used to update the mute state of a player through its port ID. Must be kept in sync with Loading Loading @@ -284,10 +314,16 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mPlayerState; private AudioAttributes mPlayerAttr; // never null private int mDeviceId; // lock for updateable properties private final Object mUpdateablePropLock = new Object(); @GuardedBy("mUpdateablePropLock") private int mDeviceId; @GuardedBy("mUpdateablePropLock") private int mSessionId; @GuardedBy("mUpdateablePropLock") private @NonNull FormatInfo mFormatInfo; @GuardedBy("mUpdateablePropLock") @PlayerMuteEvent private int mMutedState; /** Loading Loading @@ -320,6 +356,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mIPlayerShell = null; } mSessionId = pic.mSessionId; mFormatInfo = FormatInfo.DEFAULT; } /** Loading @@ -333,13 +370,23 @@ public final class AudioPlaybackConfiguration implements Parcelable { } } // sets the fields that are updateable and require synchronization private void setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format) { synchronized (mUpdateablePropLock) { mDeviceId = deviceId; mSessionId = sessionId; mMutedState = mutedState; mFormatInfo = format; } } // Note that this method is called server side, so no "privileged" information is ever sent // to a client that is not supposed to have access to it. /** * @hide * Creates a copy of the playback configuration that is stripped of any data enabling * identification of which application it is associated with ("anonymized"). * @param toSanitize * @param in the instance to copy from */ public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) { final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId); Loading @@ -357,14 +404,16 @@ public final class AudioPlaybackConfiguration implements Parcelable { builder.setUsage(in.mPlayerAttr.getUsage()); } anonymCopy.mPlayerAttr = builder.build(); anonymCopy.mDeviceId = in.mDeviceId; // anonymized data anonymCopy.mMutedState = 0; anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; anonymCopy.mClientUid = PLAYER_UPID_INVALID; anonymCopy.mClientPid = PLAYER_UPID_INVALID; anonymCopy.mIPlayerShell = null; anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; anonymCopy.setUpdateableFields( /*deviceId*/ PLAYER_DEVICEID_INVALID, /*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE, /*mutedState*/ 0, FormatInfo.DEFAULT); return anonymCopy; } Loading Loading @@ -401,10 +450,14 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @return the audio playback device or null if the device is not available at the time of query */ public @Nullable AudioDeviceInfo getAudioDeviceInfo() { if (mDeviceId == PLAYER_DEVICEID_INVALID) { final int deviceId; synchronized (mUpdateablePropLock) { deviceId = mDeviceId; } if (deviceId == PLAYER_DEVICEID_INVALID) { return null; } return AudioManager.getDeviceForPortId(mDeviceId, AudioManager.GET_DEVICES_OUTPUTS); return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS); } /** Loading @@ -415,8 +468,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ @SystemApi public @IntRange(from = 0) int getSessionId() { synchronized (mUpdateablePropLock) { return mSessionId; } } /** * @hide Loading @@ -428,8 +483,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted() { synchronized (mUpdateablePropLock) { return mMutedState != 0; } } /** * @hide Loading @@ -440,8 +497,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) @PlayerMuteEvent public int getMutedBy() { synchronized (mUpdateablePropLock) { return mMutedState; } } /** * @hide Loading Loading @@ -498,6 +557,43 @@ public final class AudioPlaybackConfiguration implements Parcelable { return ips == null ? null : new PlayerProxy(this); } /** * @hide * Return whether this player's output is spatialized * @return true if spatialized, false if not or playback hasn't started */ @SystemApi public boolean isSpatialized() { synchronized (mUpdateablePropLock) { return mFormatInfo.mIsSpatialized; } } /** * @hide * Return the sample rate in Hz of the content being played. * @return the sample rate in Hertz, or 0 if playback hasn't started */ @SystemApi public @IntRange(from = 0) int getSampleRate() { synchronized (mUpdateablePropLock) { return mFormatInfo.mSampleRate; } } /** * @hide * Return the player's channel mask * @return the channel mask, or 0 if playback hasn't started. See {@link AudioFormat} and * the definitions for the <code>CHANNEL_OUT_*</code> values used for the mask's bitfield */ @SystemApi public int getChannelMask() { synchronized (mUpdateablePropLock) { return (AudioFormat.convertNativeChannelMaskToOutMask(mFormatInfo.mNativeChannelMask)); } } /** * @hide * @return the IPlayer interface for the associated player Loading Loading @@ -527,10 +623,12 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @param sessionId the audio session ID */ public boolean handleSessionIdEvent(int sessionId) { synchronized (mUpdateablePropLock) { final boolean changed = sessionId != mSessionId; mSessionId = sessionId; return changed; } } /** * @hide Loading @@ -539,10 +637,26 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @return true if the state changed, false otherwise */ public boolean handleMutedEvent(@PlayerMuteEvent int mutedState) { synchronized (mUpdateablePropLock) { final boolean changed = mMutedState != mutedState; mMutedState = mutedState; return changed; } } /** * @hide * Handle a change of playback format * @param fi the latest format information * @return true if the format changed, false otherwise */ public boolean handleFormatEvent(@NonNull FormatInfo fi) { synchronized (mUpdateablePropLock) { final boolean changed = !mFormatInfo.equals(fi); mFormatInfo = fi; return changed; } } /** * @hide Loading @@ -558,7 +672,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ public boolean handleStateEvent(int event, int deviceId) { boolean changed = false; synchronized (this) { synchronized (mUpdateablePropLock) { // Do not update if it is only device id update if (event != PLAYER_UPDATE_DEVICE_ID) { Loading Loading @@ -647,8 +761,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public int hashCode() { return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid, mClientPid, mSessionId); synchronized (mUpdateablePropLock) { return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid, mClientPid, mSessionId); } } @Override Loading @@ -658,6 +774,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { synchronized (mUpdateablePropLock) { dest.writeInt(mPlayerIId); dest.writeInt(mDeviceId); dest.writeInt(mMutedState); Loading @@ -672,6 +789,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { } dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); dest.writeInt(mSessionId); mFormatInfo.writeToParcel(dest, 0); } } private AudioPlaybackConfiguration(Parcel in) { Loading @@ -686,6 +805,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); mSessionId = in.readInt(); mFormatInfo = FormatInfo.CREATOR.createFromParcel(in); } @Override Loading @@ -707,11 +827,14 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public String toString() { StringBuilder apcToString = new StringBuilder(); synchronized (mUpdateablePropLock) { apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append( " deviceId:").append(mDeviceId).append(" type:").append( toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append( toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append( mClientUid).append( "/").append(mClientPid).append(" state:").append( toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append( toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append( mPlayerAttr).append( " sessionId:").append(mSessionId).append(" mutedState:"); if (mMutedState == 0) { apcToString.append("none "); Loading @@ -735,7 +858,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { apcToString.append("volumeShaper "); } } apcToString.append(" ").append(mFormatInfo); } return apcToString.toString(); } Loading Loading @@ -787,6 +911,79 @@ public final class AudioPlaybackConfiguration implements Parcelable { } } //===================================================================== /** * @hide * Class to store sample rate, channel mask, and spatialization status */ public static final class FormatInfo implements Parcelable { static final FormatInfo DEFAULT = new FormatInfo( /*spatialized*/ false, /*channel mask*/ 0, /*sample rate*/ 0); final boolean mIsSpatialized; final int mNativeChannelMask; final int mSampleRate; public FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate) { mIsSpatialized = isSpatialized; mNativeChannelMask = nativeChannelMask; mSampleRate = sampleRate; } @Override public String toString() { return "FormatInfo{" + "isSpatialized=" + mIsSpatialized + ", channelMask=0x" + Integer.toHexString(mNativeChannelMask) + ", sampleRate=" + mSampleRate + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FormatInfo)) return false; FormatInfo that = (FormatInfo) o; return mIsSpatialized == that.mIsSpatialized && mNativeChannelMask == that.mNativeChannelMask && mSampleRate == that.mSampleRate; } @Override public int hashCode() { return Objects.hash(mIsSpatialized, mNativeChannelMask, mSampleRate); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { dest.writeBoolean(mIsSpatialized); dest.writeInt(mNativeChannelMask); dest.writeInt(mSampleRate); } private FormatInfo(Parcel in) { this( in.readBoolean(), // isSpatialized in.readInt(), // channelMask in.readInt() // sampleRate ); } public static final @NonNull Parcelable.Creator<FormatInfo> CREATOR = new Parcelable.Creator<FormatInfo>() { public FormatInfo createFromParcel(Parcel p) { return new FormatInfo(p); } public FormatInfo[] newArray(int size) { return new FormatInfo[size]; } }; } //===================================================================== // Utilities Loading services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +76 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.AudioPlaybackConfiguration.FormatInfo; import android.media.AudioPlaybackConfiguration.PlayerMuteEvent; import android.media.AudioSystem; import android.media.IPlaybackConfigDispatcher; Loading Loading @@ -75,7 +76,7 @@ import java.util.function.Consumer; public final class PlaybackActivityMonitor implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer { public static final String TAG = "AudioService.PlaybackActivityMonitor"; public static final String TAG = "AS.PlaybackActivityMon"; /*package*/ static final boolean DEBUG = false; /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1; Loading Loading @@ -343,7 +344,7 @@ public final class PlaybackActivityMonitor if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_UPDATE_PORT_EVENT, eventValue, piid)); mEventHandler.obtainMessage(MSG_II_UPDATE_PORT_EVENT, eventValue, piid)); return; } else if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { for (Integer uidInteger: mBannedUids) { Loading Loading @@ -399,7 +400,7 @@ public final class PlaybackActivityMonitor } if (DEBUG) { Log.v(TAG, TextUtils.formatSimple("portEvent(portId=%d, event=%s, extras=%s)", Log.v(TAG, TextUtils.formatSimple("BLA portEvent(portId=%d, event=%s, extras=%s)", portId, AudioPlaybackConfiguration.playerStateToString(event), extras)); } Loading Loading @@ -427,7 +428,12 @@ public final class PlaybackActivityMonitor if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_UPDATE_PLAYER_MUTED_EVENT, piid, mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid, portId, extras)); } else if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_FORMAT, piid, portId, extras)); } Loading Loading @@ -457,7 +463,7 @@ public final class PlaybackActivityMonitor // remove all port ids mapped to the released player mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_CLEAR_PORTS_FOR_PIID, piid, /*arg2=*/0)); mEventHandler.obtainMessage(MSG_I_CLEAR_PORTS_FOR_PIID, piid, /*arg2=*/0)); if (change && mDoNotLogPiidList.contains(piid)) { // do not dispatch a change for a "do not log" player Loading Loading @@ -1352,6 +1358,21 @@ public final class PlaybackActivityMonitor } } private static final class PlayerFormatEvent extends EventLogger.Event { private final int mPlayerIId; private final AudioPlaybackConfiguration.FormatInfo mFormat; PlayerFormatEvent(int piid, AudioPlaybackConfiguration.FormatInfo format) { mPlayerIId = piid; mFormat = format; } @Override public String eventToString() { return new String("player piid:" + mPlayerIId + " format update:" + mFormat); } } static final EventLogger sEventLogger = new EventLogger(100, "playback activity as reported through PlayerBase"); Loading Loading @@ -1478,7 +1499,7 @@ public final class PlaybackActivityMonitor * msg.arg1: port id * msg.arg2: piid */ private static final int MSG_L_UPDATE_PORT_EVENT = 2; private static final int MSG_II_UPDATE_PORT_EVENT = 2; /** * event for player getting muted Loading @@ -1488,14 +1509,24 @@ public final class PlaybackActivityMonitor * msg.obj: extras describing the mute reason * type: PersistableBundle */ private static final int MSG_L_UPDATE_PLAYER_MUTED_EVENT = 3; private static final int MSG_IIL_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 = 4; private static final int MSG_I_CLEAR_PORTS_FOR_PIID = 4; /** * event for player reporting playback format and spatialization status * args: * msg.arg1: piid * msg.arg2: port id * msg.obj: extras describing the sample rate, channel mask, spatialized * type: PersistableBundle */ private static final int MSG_IIL_UPDATE_PLAYER_FORMAT = 5; private void initEventHandler() { mEventThread = new HandlerThread(TAG); Loading @@ -1513,12 +1544,13 @@ public final class PlaybackActivityMonitor } mMuteAwaitConnectionTimeoutCb.accept((AudioDeviceAttributes) msg.obj); break; case MSG_L_UPDATE_PORT_EVENT: case MSG_II_UPDATE_PORT_EVENT: synchronized (mPlayerLock) { mPortIdToPiid.put(/*portId*/msg.arg1, /*piid*/msg.arg2); } break; case MSG_L_UPDATE_PLAYER_MUTED_EVENT: case MSG_IIL_UPDATE_PLAYER_MUTED_EVENT: // TODO: replace PersistableBundle with own struct PersistableBundle extras = (PersistableBundle) msg.obj; if (extras == null) { Loading @@ -1533,14 +1565,18 @@ public final class PlaybackActivityMonitor sEventLogger.enqueue( new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValue)); final AudioPlaybackConfiguration apc = mPlayers.get(piid); final AudioPlaybackConfiguration apc; synchronized (mPlayerLock) { apc = mPlayers.get(piid); } if (apc == null || !apc.handleMutedEvent(eventValue)) { break; // do not dispatch } dispatchPlaybackChange(/* iplayerReleased= */false); } break; case MSG_L_CLEAR_PORTS_FOR_PIID: case MSG_I_CLEAR_PORTS_FOR_PIID: int piid = msg.arg1; if (piid == AudioPlaybackConfiguration.PLAYER_PIID_INVALID) { Log.w(TAG, "Received clear ports with invalid piid"); Loading @@ -1554,6 +1590,34 @@ public final class PlaybackActivityMonitor } } break; case MSG_IIL_UPDATE_PLAYER_FORMAT: final PersistableBundle formatExtras = (PersistableBundle) msg.obj; if (formatExtras == null) { Log.w(TAG, "Received format event with no extras"); break; } final boolean spatialized = formatExtras.getBoolean( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SPATIALIZED, false); final int sampleRate = formatExtras.getInt( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SAMPLE_RATE, 0); final int nativeChannelMask = formatExtras.getInt( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_CHANNEL_MASK, 0); final FormatInfo format = new FormatInfo(spatialized, nativeChannelMask, sampleRate); sEventLogger.enqueue(new PlayerFormatEvent(msg.arg1, format)); final AudioPlaybackConfiguration apc; synchronized (mPlayerLock) { apc = mPlayers.get(msg.arg1); } if (apc == null || !apc.handleFormatEvent(format)) { break; // do not dispatch } // TODO optimize for no dispatch to non-privileged listeners dispatchPlaybackChange(/* iplayerReleased= */false); break; default: break; } Loading Loading
core/api/system-current.txt +3 −0 Original line number Diff line number Diff line Loading @@ -6683,6 +6683,7 @@ package android.media { } public final class AudioPlaybackConfiguration implements android.os.Parcelable { method public int getChannelMask(); method public int getClientPid(); method public int getClientUid(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getMutedBy(); Loading @@ -6690,9 +6691,11 @@ package android.media { method public android.media.PlayerProxy getPlayerProxy(); method public int getPlayerState(); method public int getPlayerType(); method @IntRange(from=0) public int getSampleRate(); method @IntRange(from=0) public int getSessionId(); method public boolean isActive(); method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted(); method public boolean isSpatialized(); field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_APP_OPS = 8; // 0x8 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_CLIENT_VOLUME = 16; // 0x10 field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int MUTED_BY_MASTER = 1; // 0x1
media/java/android/media/AudioPlaybackConfiguration.java +257 −60 Original line number Diff line number Diff line Loading @@ -32,6 +32,8 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -178,6 +180,11 @@ public final class AudioPlaybackConfiguration implements Parcelable { * Used to update the mute state of a player through its port id */ public static final int PLAYER_UPDATE_MUTED = 7; /** * @hide * Used to update the spatialization status and format of a player through its port id */ public static final int PLAYER_UPDATE_FORMAT = 8; /** @hide */ @IntDef({ Loading @@ -190,6 +197,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { PLAYER_UPDATE_DEVICE_ID, PLAYER_UPDATE_PORT_ID, PLAYER_UPDATE_MUTED, PLAYER_UPDATE_FORMAT, }) @Retention(RetentionPolicy.SOURCE) public @interface PlayerState {} Loading @@ -206,11 +214,33 @@ public final class AudioPlaybackConfiguration implements Parcelable { 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"; case PLAYER_UPDATE_FORMAT: return "PLAYER_UPDATE_FORMAT"; default: return "invalid state " + state; } } /** * @hide * Used to update the spatialization status 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_SPATIALIZED = "android.media.extra.PLAYER_EVENT_SPATIALIZED"; /** * @hide * Used to update the sample rate 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_SAMPLE_RATE = "android.media.extra.PLAYER_EVENT_SAMPLE_RATE"; /** * @hide * Used to update the channel mask 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_CHANNEL_MASK = "android.media.extra.PLAYER_EVENT_CHANNEL_MASK"; /** * @hide * Used to update the mute state of a player through its port ID. Must be kept in sync with Loading Loading @@ -284,10 +314,16 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mPlayerState; private AudioAttributes mPlayerAttr; // never null private int mDeviceId; // lock for updateable properties private final Object mUpdateablePropLock = new Object(); @GuardedBy("mUpdateablePropLock") private int mDeviceId; @GuardedBy("mUpdateablePropLock") private int mSessionId; @GuardedBy("mUpdateablePropLock") private @NonNull FormatInfo mFormatInfo; @GuardedBy("mUpdateablePropLock") @PlayerMuteEvent private int mMutedState; /** Loading Loading @@ -320,6 +356,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mIPlayerShell = null; } mSessionId = pic.mSessionId; mFormatInfo = FormatInfo.DEFAULT; } /** Loading @@ -333,13 +370,23 @@ public final class AudioPlaybackConfiguration implements Parcelable { } } // sets the fields that are updateable and require synchronization private void setUpdateableFields(int deviceId, int sessionId, int mutedState, FormatInfo format) { synchronized (mUpdateablePropLock) { mDeviceId = deviceId; mSessionId = sessionId; mMutedState = mutedState; mFormatInfo = format; } } // Note that this method is called server side, so no "privileged" information is ever sent // to a client that is not supposed to have access to it. /** * @hide * Creates a copy of the playback configuration that is stripped of any data enabling * identification of which application it is associated with ("anonymized"). * @param toSanitize * @param in the instance to copy from */ public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) { final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId); Loading @@ -357,14 +404,16 @@ public final class AudioPlaybackConfiguration implements Parcelable { builder.setUsage(in.mPlayerAttr.getUsage()); } anonymCopy.mPlayerAttr = builder.build(); anonymCopy.mDeviceId = in.mDeviceId; // anonymized data anonymCopy.mMutedState = 0; anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN; anonymCopy.mClientUid = PLAYER_UPID_INVALID; anonymCopy.mClientPid = PLAYER_UPID_INVALID; anonymCopy.mIPlayerShell = null; anonymCopy.mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; anonymCopy.setUpdateableFields( /*deviceId*/ PLAYER_DEVICEID_INVALID, /*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE, /*mutedState*/ 0, FormatInfo.DEFAULT); return anonymCopy; } Loading Loading @@ -401,10 +450,14 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @return the audio playback device or null if the device is not available at the time of query */ public @Nullable AudioDeviceInfo getAudioDeviceInfo() { if (mDeviceId == PLAYER_DEVICEID_INVALID) { final int deviceId; synchronized (mUpdateablePropLock) { deviceId = mDeviceId; } if (deviceId == PLAYER_DEVICEID_INVALID) { return null; } return AudioManager.getDeviceForPortId(mDeviceId, AudioManager.GET_DEVICES_OUTPUTS); return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS); } /** Loading @@ -415,8 +468,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ @SystemApi public @IntRange(from = 0) int getSessionId() { synchronized (mUpdateablePropLock) { return mSessionId; } } /** * @hide Loading @@ -428,8 +483,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted() { synchronized (mUpdateablePropLock) { return mMutedState != 0; } } /** * @hide Loading @@ -440,8 +497,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) @PlayerMuteEvent public int getMutedBy() { synchronized (mUpdateablePropLock) { return mMutedState; } } /** * @hide Loading Loading @@ -498,6 +557,43 @@ public final class AudioPlaybackConfiguration implements Parcelable { return ips == null ? null : new PlayerProxy(this); } /** * @hide * Return whether this player's output is spatialized * @return true if spatialized, false if not or playback hasn't started */ @SystemApi public boolean isSpatialized() { synchronized (mUpdateablePropLock) { return mFormatInfo.mIsSpatialized; } } /** * @hide * Return the sample rate in Hz of the content being played. * @return the sample rate in Hertz, or 0 if playback hasn't started */ @SystemApi public @IntRange(from = 0) int getSampleRate() { synchronized (mUpdateablePropLock) { return mFormatInfo.mSampleRate; } } /** * @hide * Return the player's channel mask * @return the channel mask, or 0 if playback hasn't started. See {@link AudioFormat} and * the definitions for the <code>CHANNEL_OUT_*</code> values used for the mask's bitfield */ @SystemApi public int getChannelMask() { synchronized (mUpdateablePropLock) { return (AudioFormat.convertNativeChannelMaskToOutMask(mFormatInfo.mNativeChannelMask)); } } /** * @hide * @return the IPlayer interface for the associated player Loading Loading @@ -527,10 +623,12 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @param sessionId the audio session ID */ public boolean handleSessionIdEvent(int sessionId) { synchronized (mUpdateablePropLock) { final boolean changed = sessionId != mSessionId; mSessionId = sessionId; return changed; } } /** * @hide Loading @@ -539,10 +637,26 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @return true if the state changed, false otherwise */ public boolean handleMutedEvent(@PlayerMuteEvent int mutedState) { synchronized (mUpdateablePropLock) { final boolean changed = mMutedState != mutedState; mMutedState = mutedState; return changed; } } /** * @hide * Handle a change of playback format * @param fi the latest format information * @return true if the format changed, false otherwise */ public boolean handleFormatEvent(@NonNull FormatInfo fi) { synchronized (mUpdateablePropLock) { final boolean changed = !mFormatInfo.equals(fi); mFormatInfo = fi; return changed; } } /** * @hide Loading @@ -558,7 +672,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ public boolean handleStateEvent(int event, int deviceId) { boolean changed = false; synchronized (this) { synchronized (mUpdateablePropLock) { // Do not update if it is only device id update if (event != PLAYER_UPDATE_DEVICE_ID) { Loading Loading @@ -647,8 +761,10 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public int hashCode() { return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid, mClientPid, mSessionId); synchronized (mUpdateablePropLock) { return Objects.hash(mPlayerIId, mDeviceId, mMutedState, mPlayerType, mClientUid, mClientPid, mSessionId); } } @Override Loading @@ -658,6 +774,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { synchronized (mUpdateablePropLock) { dest.writeInt(mPlayerIId); dest.writeInt(mDeviceId); dest.writeInt(mMutedState); Loading @@ -672,6 +789,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { } dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); dest.writeInt(mSessionId); mFormatInfo.writeToParcel(dest, 0); } } private AudioPlaybackConfiguration(Parcel in) { Loading @@ -686,6 +805,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder()); mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p); mSessionId = in.readInt(); mFormatInfo = FormatInfo.CREATOR.createFromParcel(in); } @Override Loading @@ -707,11 +827,14 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public String toString() { StringBuilder apcToString = new StringBuilder(); synchronized (mUpdateablePropLock) { apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append( " deviceId:").append(mDeviceId).append(" type:").append( toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append( toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append( mClientUid).append( "/").append(mClientPid).append(" state:").append( toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append( toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append( mPlayerAttr).append( " sessionId:").append(mSessionId).append(" mutedState:"); if (mMutedState == 0) { apcToString.append("none "); Loading @@ -735,7 +858,8 @@ public final class AudioPlaybackConfiguration implements Parcelable { apcToString.append("volumeShaper "); } } apcToString.append(" ").append(mFormatInfo); } return apcToString.toString(); } Loading Loading @@ -787,6 +911,79 @@ public final class AudioPlaybackConfiguration implements Parcelable { } } //===================================================================== /** * @hide * Class to store sample rate, channel mask, and spatialization status */ public static final class FormatInfo implements Parcelable { static final FormatInfo DEFAULT = new FormatInfo( /*spatialized*/ false, /*channel mask*/ 0, /*sample rate*/ 0); final boolean mIsSpatialized; final int mNativeChannelMask; final int mSampleRate; public FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate) { mIsSpatialized = isSpatialized; mNativeChannelMask = nativeChannelMask; mSampleRate = sampleRate; } @Override public String toString() { return "FormatInfo{" + "isSpatialized=" + mIsSpatialized + ", channelMask=0x" + Integer.toHexString(mNativeChannelMask) + ", sampleRate=" + mSampleRate + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof FormatInfo)) return false; FormatInfo that = (FormatInfo) o; return mIsSpatialized == that.mIsSpatialized && mNativeChannelMask == that.mNativeChannelMask && mSampleRate == that.mSampleRate; } @Override public int hashCode() { return Objects.hash(mIsSpatialized, mNativeChannelMask, mSampleRate); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) { dest.writeBoolean(mIsSpatialized); dest.writeInt(mNativeChannelMask); dest.writeInt(mSampleRate); } private FormatInfo(Parcel in) { this( in.readBoolean(), // isSpatialized in.readInt(), // channelMask in.readInt() // sampleRate ); } public static final @NonNull Parcelable.Creator<FormatInfo> CREATOR = new Parcelable.Creator<FormatInfo>() { public FormatInfo createFromParcel(Parcel p) { return new FormatInfo(p); } public FormatInfo[] newArray(int size) { return new FormatInfo[size]; } }; } //===================================================================== // Utilities Loading
services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +76 −12 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.media.AudioPlaybackConfiguration; import android.media.AudioPlaybackConfiguration.FormatInfo; import android.media.AudioPlaybackConfiguration.PlayerMuteEvent; import android.media.AudioSystem; import android.media.IPlaybackConfigDispatcher; Loading Loading @@ -75,7 +76,7 @@ import java.util.function.Consumer; public final class PlaybackActivityMonitor implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer { public static final String TAG = "AudioService.PlaybackActivityMonitor"; public static final String TAG = "AS.PlaybackActivityMon"; /*package*/ static final boolean DEBUG = false; /*package*/ static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1; Loading Loading @@ -343,7 +344,7 @@ public final class PlaybackActivityMonitor if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_PORT_ID) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_UPDATE_PORT_EVENT, eventValue, piid)); mEventHandler.obtainMessage(MSG_II_UPDATE_PORT_EVENT, eventValue, piid)); return; } else if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { for (Integer uidInteger: mBannedUids) { Loading Loading @@ -399,7 +400,7 @@ public final class PlaybackActivityMonitor } if (DEBUG) { Log.v(TAG, TextUtils.formatSimple("portEvent(portId=%d, event=%s, extras=%s)", Log.v(TAG, TextUtils.formatSimple("BLA portEvent(portId=%d, event=%s, extras=%s)", portId, AudioPlaybackConfiguration.playerStateToString(event), extras)); } Loading Loading @@ -427,7 +428,12 @@ public final class PlaybackActivityMonitor if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_UPDATE_PLAYER_MUTED_EVENT, piid, mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_MUTED_EVENT, piid, portId, extras)); } else if (event == AudioPlaybackConfiguration.PLAYER_UPDATE_FORMAT) { mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_IIL_UPDATE_PLAYER_FORMAT, piid, portId, extras)); } Loading Loading @@ -457,7 +463,7 @@ public final class PlaybackActivityMonitor // remove all port ids mapped to the released player mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_L_CLEAR_PORTS_FOR_PIID, piid, /*arg2=*/0)); mEventHandler.obtainMessage(MSG_I_CLEAR_PORTS_FOR_PIID, piid, /*arg2=*/0)); if (change && mDoNotLogPiidList.contains(piid)) { // do not dispatch a change for a "do not log" player Loading Loading @@ -1352,6 +1358,21 @@ public final class PlaybackActivityMonitor } } private static final class PlayerFormatEvent extends EventLogger.Event { private final int mPlayerIId; private final AudioPlaybackConfiguration.FormatInfo mFormat; PlayerFormatEvent(int piid, AudioPlaybackConfiguration.FormatInfo format) { mPlayerIId = piid; mFormat = format; } @Override public String eventToString() { return new String("player piid:" + mPlayerIId + " format update:" + mFormat); } } static final EventLogger sEventLogger = new EventLogger(100, "playback activity as reported through PlayerBase"); Loading Loading @@ -1478,7 +1499,7 @@ public final class PlaybackActivityMonitor * msg.arg1: port id * msg.arg2: piid */ private static final int MSG_L_UPDATE_PORT_EVENT = 2; private static final int MSG_II_UPDATE_PORT_EVENT = 2; /** * event for player getting muted Loading @@ -1488,14 +1509,24 @@ public final class PlaybackActivityMonitor * msg.obj: extras describing the mute reason * type: PersistableBundle */ private static final int MSG_L_UPDATE_PLAYER_MUTED_EVENT = 3; private static final int MSG_IIL_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 = 4; private static final int MSG_I_CLEAR_PORTS_FOR_PIID = 4; /** * event for player reporting playback format and spatialization status * args: * msg.arg1: piid * msg.arg2: port id * msg.obj: extras describing the sample rate, channel mask, spatialized * type: PersistableBundle */ private static final int MSG_IIL_UPDATE_PLAYER_FORMAT = 5; private void initEventHandler() { mEventThread = new HandlerThread(TAG); Loading @@ -1513,12 +1544,13 @@ public final class PlaybackActivityMonitor } mMuteAwaitConnectionTimeoutCb.accept((AudioDeviceAttributes) msg.obj); break; case MSG_L_UPDATE_PORT_EVENT: case MSG_II_UPDATE_PORT_EVENT: synchronized (mPlayerLock) { mPortIdToPiid.put(/*portId*/msg.arg1, /*piid*/msg.arg2); } break; case MSG_L_UPDATE_PLAYER_MUTED_EVENT: case MSG_IIL_UPDATE_PLAYER_MUTED_EVENT: // TODO: replace PersistableBundle with own struct PersistableBundle extras = (PersistableBundle) msg.obj; if (extras == null) { Loading @@ -1533,14 +1565,18 @@ public final class PlaybackActivityMonitor sEventLogger.enqueue( new PlayerEvent(piid, PLAYER_UPDATE_MUTED, eventValue)); final AudioPlaybackConfiguration apc = mPlayers.get(piid); final AudioPlaybackConfiguration apc; synchronized (mPlayerLock) { apc = mPlayers.get(piid); } if (apc == null || !apc.handleMutedEvent(eventValue)) { break; // do not dispatch } dispatchPlaybackChange(/* iplayerReleased= */false); } break; case MSG_L_CLEAR_PORTS_FOR_PIID: case MSG_I_CLEAR_PORTS_FOR_PIID: int piid = msg.arg1; if (piid == AudioPlaybackConfiguration.PLAYER_PIID_INVALID) { Log.w(TAG, "Received clear ports with invalid piid"); Loading @@ -1554,6 +1590,34 @@ public final class PlaybackActivityMonitor } } break; case MSG_IIL_UPDATE_PLAYER_FORMAT: final PersistableBundle formatExtras = (PersistableBundle) msg.obj; if (formatExtras == null) { Log.w(TAG, "Received format event with no extras"); break; } final boolean spatialized = formatExtras.getBoolean( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SPATIALIZED, false); final int sampleRate = formatExtras.getInt( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_SAMPLE_RATE, 0); final int nativeChannelMask = formatExtras.getInt( AudioPlaybackConfiguration.EXTRA_PLAYER_EVENT_CHANNEL_MASK, 0); final FormatInfo format = new FormatInfo(spatialized, nativeChannelMask, sampleRate); sEventLogger.enqueue(new PlayerFormatEvent(msg.arg1, format)); final AudioPlaybackConfiguration apc; synchronized (mPlayerLock) { apc = mPlayers.get(msg.arg1); } if (apc == null || !apc.handleFormatEvent(format)) { break; // do not dispatch } // TODO optimize for no dispatch to non-privileged listeners dispatchPlaybackChange(/* iplayerReleased= */false); break; default: break; } Loading