Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b377c388 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android (Google) Code Review
Browse files

Merge "Playback activity monitoring: API for format and spatialization"

parents 146d9fef ddd02b3d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -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
+257 −60
Original line number Diff line number Diff line
@@ -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

    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;

    /**
@@ -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,8 +468,10 @@ public final class AudioPlaybackConfiguration implements Parcelable {
     */
    @SystemApi
    public @IntRange(from = 0) int getSessionId() {
        synchronized (mUpdateablePropLock) {
            return mSessionId;
        }
    }

    /**
     * @hide
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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,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);
@@ -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) {
@@ -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,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 ");
@@ -735,7 +858,8 @@ public final class AudioPlaybackConfiguration implements Parcelable {
                    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<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

+76 −12
Original line number Diff line number Diff line
@@ -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;
                }