diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 2bf4149e2eb9c..97b9a51356daf 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -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(); @@ -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 diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java index 59a0f7b88937d..f64e5cc1c5f15 100644 --- a/media/java/android/media/AudioPlaybackConfiguration.java +++ b/media/java/android/media/AudioPlaybackConfiguration.java @@ -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; @@ -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({ @@ -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 {} @@ -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 @@ -284,10 +314,16 @@ public final class AudioPlaybackConfiguration implements Parcelable { private int mPlayerState; private AudioAttributes mPlayerAttr; // never null + // 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; /** @@ -320,6 +356,7 @@ public final class AudioPlaybackConfiguration implements Parcelable { mIPlayerShell = null; } mSessionId = pic.mSessionId; + mFormatInfo = FormatInfo.DEFAULT; } /** @@ -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); @@ -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; } @@ -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); } /** @@ -415,7 +468,9 @@ public final class AudioPlaybackConfiguration implements Parcelable { */ @SystemApi public @IntRange(from = 0) int getSessionId() { - return mSessionId; + synchronized (mUpdateablePropLock) { + return mSessionId; + } } /** @@ -428,7 +483,9 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean isMuted() { - return mMutedState != 0; + synchronized (mUpdateablePropLock) { + return mMutedState != 0; + } } /** @@ -440,7 +497,9 @@ public final class AudioPlaybackConfiguration implements Parcelable { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) @PlayerMuteEvent public int getMutedBy() { - return mMutedState; + synchronized (mUpdateablePropLock) { + return mMutedState; + } } /** @@ -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 CHANNEL_OUT_* 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 @@ -527,9 +623,11 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @param sessionId the audio session ID */ public boolean handleSessionIdEvent(int sessionId) { - final boolean changed = sessionId != mSessionId; - mSessionId = sessionId; - return changed; + synchronized (mUpdateablePropLock) { + final boolean changed = sessionId != mSessionId; + mSessionId = sessionId; + return changed; + } } /** @@ -539,9 +637,25 @@ public final class AudioPlaybackConfiguration implements Parcelable { * @return true if the state changed, false otherwise */ public boolean handleMutedEvent(@PlayerMuteEvent int mutedState) { - final boolean changed = mMutedState != mutedState; - mMutedState = mutedState; - return changed; + 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; + } } /** @@ -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) { @@ -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 @@ -658,20 +774,23 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mPlayerIId); - dest.writeInt(mDeviceId); - dest.writeInt(mMutedState); - dest.writeInt(mPlayerType); - dest.writeInt(mClientUid); - dest.writeInt(mClientPid); - dest.writeInt(mPlayerState); - mPlayerAttr.writeToParcel(dest, 0); - final IPlayerShell ips; - synchronized (this) { - ips = mIPlayerShell; + synchronized (mUpdateablePropLock) { + dest.writeInt(mPlayerIId); + dest.writeInt(mDeviceId); + dest.writeInt(mMutedState); + dest.writeInt(mPlayerType); + dest.writeInt(mClientUid); + dest.writeInt(mClientPid); + dest.writeInt(mPlayerState); + mPlayerAttr.writeToParcel(dest, 0); + final IPlayerShell ips; + synchronized (this) { + ips = mIPlayerShell; + } + dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); + dest.writeInt(mSessionId); + mFormatInfo.writeToParcel(dest, 0); } - dest.writeStrongInterface(ips == null ? null : ips.getIPlayer()); - dest.writeInt(mSessionId); } private AudioPlaybackConfiguration(Parcel in) { @@ -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 @@ -707,35 +827,39 @@ public final class AudioPlaybackConfiguration implements Parcelable { @Override public String toString() { StringBuilder apcToString = new StringBuilder(); - apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append( - " deviceId:").append(mDeviceId).append(" type:").append( - toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append( - "/").append(mClientPid).append(" state:").append( - toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append( - " sessionId:").append(mSessionId).append(" mutedState:"); - if (mMutedState == 0) { - apcToString.append("none "); - } else { - if ((mMutedState & MUTED_BY_MASTER) != 0) { - apcToString.append("master "); - } - if ((mMutedState & MUTED_BY_STREAM_VOLUME) != 0) { - apcToString.append("streamVolume "); - } - if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) { - apcToString.append("streamMute "); - } - if ((mMutedState & MUTED_BY_APP_OPS) != 0) { - apcToString.append("appOps "); - } - if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) { - apcToString.append("clientVolume "); - } - if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) { - apcToString.append("volumeShaper "); + synchronized (mUpdateablePropLock) { + apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append( + " deviceId:").append(mDeviceId).append(" type:").append( + toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append( + mClientUid).append( + "/").append(mClientPid).append(" state:").append( + toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append( + mPlayerAttr).append( + " sessionId:").append(mSessionId).append(" mutedState:"); + if (mMutedState == 0) { + apcToString.append("none "); + } else { + if ((mMutedState & MUTED_BY_MASTER) != 0) { + apcToString.append("master "); + } + if ((mMutedState & MUTED_BY_STREAM_VOLUME) != 0) { + apcToString.append("streamVolume "); + } + if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) { + apcToString.append("streamMute "); + } + if ((mMutedState & MUTED_BY_APP_OPS) != 0) { + apcToString.append("appOps "); + } + if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) { + apcToString.append("clientVolume "); + } + if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) { + apcToString.append("volumeShaper "); + } } + apcToString.append(" ").append(mFormatInfo); } - return apcToString.toString(); } @@ -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 CREATOR = + new Parcelable.Creator() { + public FormatInfo createFromParcel(Parcel p) { + return new FormatInfo(p); + } + public FormatInfo[] newArray(int size) { + return new FormatInfo[size]; + } + }; + } //===================================================================== // Utilities diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index f35931ca1837c..99d62287f2a99 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -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; @@ -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; @@ -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) { @@ -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)); } @@ -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)); } @@ -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 @@ -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"); @@ -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 @@ -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); @@ -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) { @@ -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"); @@ -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; }