Playback activity monitoring: API for format and spatialization
Add SystemApi to query sample rate, channel mask and spatialization status from AudioPlaybackConfiguration, information coming from AudioFlinger. Fix synchronization on updateable properties (use a lock instead of this). Bug: 263300713 Test: atest android.media.audio.cts.AudioPlaybackConfigurationTest Change-Id: Iad94f0b25ea89efbfb4db047c68cc98b47e85c86
This commit is contained in:
@@ -6649,6 +6649,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();
|
||||
@@ -6656,9 +6657,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
|
||||
|
||||
@@ -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 <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,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<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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user