• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.media;
18 
19 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_ALL;
20 import static android.media.AudioAttributes.ALLOW_CAPTURE_BY_NONE;
21 import static android.media.audio.Flags.FLAG_MUTED_BY_PORT_VOLUME_API;
22 import static android.media.audio.Flags.FLAG_ROUTED_DEVICE_IDS;
23 
24 import android.annotation.FlaggedApi;
25 import android.annotation.IntDef;
26 import android.annotation.IntRange;
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.annotation.RequiresPermission;
30 import android.annotation.SystemApi;
31 import android.os.Binder;
32 import android.os.IBinder;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.os.RemoteException;
36 import android.util.Log;
37 
38 import com.android.internal.annotations.GuardedBy;
39 
40 import java.io.PrintWriter;
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.List;
46 import java.util.Objects;
47 
48 /**
49  * The AudioPlaybackConfiguration class collects the information describing an audio playback
50  * session.
51  */
52 public final class AudioPlaybackConfiguration implements Parcelable {
53     private static final String TAG = new String("AudioPlaybackConfiguration");
54 
55     private static final boolean DEBUG = false;
56 
57     /** @hide */
58     public static final int PLAYER_PIID_INVALID = -1;
59     /** @hide */
60     public static final int PLAYER_UPID_INVALID = -1;
61     /** @hide */
62     public static final int PLAYER_DEVICEID_INVALID = 0;
63     /** @hide */
64     public static final int[] PLAYER_DEVICEIDS_INVALID = new int[0];
65 
66     // information about the implementation
67     /**
68      * @hide
69      * An unknown type of player
70      */
71     @SystemApi
72     public static final int PLAYER_TYPE_UNKNOWN = -1;
73     /**
74      * @hide
75      * Player backed by a java android.media.AudioTrack player
76      */
77     @SystemApi
78     public static final int PLAYER_TYPE_JAM_AUDIOTRACK = 1;
79     /**
80      * @hide
81      * Player backed by a java android.media.MediaPlayer player
82      */
83     @SystemApi
84     public static final int PLAYER_TYPE_JAM_MEDIAPLAYER = 2;
85     /**
86      * @hide
87      * Player backed by a java android.media.SoundPool player
88      */
89     @SystemApi
90     public static final int PLAYER_TYPE_JAM_SOUNDPOOL = 3;
91     /**
92      * @hide
93      * Player backed by a C OpenSL ES AudioPlayer player with a BufferQueue source
94      */
95     @SystemApi
96     public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11;
97     /**
98      * @hide
99      * Player backed by a C OpenSL ES AudioPlayer player with a URI or FD source
100      */
101     @SystemApi
102     public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12;
103 
104     /**
105      * @hide
106      * Player backed an AAudio player.
107      */
108     @SystemApi
109     public static final int PLAYER_TYPE_AAUDIO = 13;
110 
111     /**
112      * @hide
113      * Player backed a hardware source, whose state is visible in the Android audio policy manager.
114      * Note this type is not in System API so it will not be returned in public API calls
115      */
116     // TODO unhide for SystemApi, update getPlayerType()
117     public static final int PLAYER_TYPE_HW_SOURCE = 14;
118 
119     /**
120      * @hide
121      * Player is a proxy for an audio player whose audio and state doesn't go through the Android
122      * audio framework.
123      * Note this type is not in System API so it will not be returned in public API calls
124      */
125     // TODO unhide for SystemApi, update getPlayerType()
126     public static final int PLAYER_TYPE_EXTERNAL_PROXY = 15;
127 
128     /** @hide */
129     @IntDef({
130         PLAYER_TYPE_UNKNOWN,
131         PLAYER_TYPE_JAM_AUDIOTRACK,
132         PLAYER_TYPE_JAM_MEDIAPLAYER,
133         PLAYER_TYPE_JAM_SOUNDPOOL,
134         PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE,
135         PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD,
136     })
137     @Retention(RetentionPolicy.SOURCE)
138     public @interface PlayerType {}
139 
140     /**
141      * @hide
142      * An unknown player state
143      */
144     @SystemApi
145     public static final int PLAYER_STATE_UNKNOWN = -1;
146     /**
147      * @hide
148      * The resources of the player have been released, it cannot play anymore
149      */
150     @SystemApi
151     public static final int PLAYER_STATE_RELEASED = 0;
152     /**
153      * @hide
154      * The state of a player when it's created
155      */
156     @SystemApi
157     public static final int PLAYER_STATE_IDLE = 1;
158     /**
159      * @hide
160      * The state of a player that is actively playing
161      */
162     @SystemApi
163     public static final int PLAYER_STATE_STARTED = 2;
164     /**
165      * @hide
166      * The state of a player where playback is paused
167      */
168     @SystemApi
169     public static final int PLAYER_STATE_PAUSED = 3;
170     /**
171      * @hide
172      * The state of a player where playback is stopped
173      */
174     @SystemApi
175     public static final int PLAYER_STATE_STOPPED = 4;
176     /**
177      * @hide
178      * The state used to update device id, does not actually change the state of the player
179      */
180     public static final int PLAYER_UPDATE_DEVICE_ID = 5;
181     /**
182      * @hide
183      * The state used to update port id, does not actually change the state of the player
184      */
185     public static final int PLAYER_UPDATE_PORT_ID = 6;
186     /**
187      * @hide
188      * Used to update the mute state of a player through its port id
189      */
190     public static final int PLAYER_UPDATE_MUTED = 7;
191     /**
192      * @hide
193      * Used to update the spatialization status and format of a player through its port id
194      */
195     public static final int PLAYER_UPDATE_FORMAT = 8;
196 
197     /** @hide */
198     @IntDef({
199         PLAYER_STATE_UNKNOWN,
200         PLAYER_STATE_RELEASED,
201         PLAYER_STATE_IDLE,
202         PLAYER_STATE_STARTED,
203         PLAYER_STATE_PAUSED,
204         PLAYER_STATE_STOPPED,
205         PLAYER_UPDATE_DEVICE_ID,
206         PLAYER_UPDATE_PORT_ID,
207         PLAYER_UPDATE_MUTED,
208         PLAYER_UPDATE_FORMAT,
209     })
210     @Retention(RetentionPolicy.SOURCE)
211     public @interface PlayerState {}
212 
213     /** @hide */
playerStateToString(@layerState int state)214     public static String playerStateToString(@PlayerState int state) {
215         switch (state) {
216             case PLAYER_STATE_UNKNOWN: return "PLAYER_STATE_UNKNOWN";
217             case PLAYER_STATE_RELEASED: return "PLAYER_STATE_RELEASED";
218             case PLAYER_STATE_IDLE: return "PLAYER_STATE_IDLE";
219             case PLAYER_STATE_STARTED: return "PLAYER_STATE_STARTED";
220             case PLAYER_STATE_PAUSED: return "PLAYER_STATE_PAUSED";
221             case PLAYER_STATE_STOPPED: return "PLAYER_STATE_STOPPED";
222             case PLAYER_UPDATE_DEVICE_ID: return "PLAYER_UPDATE_DEVICE_ID";
223             case PLAYER_UPDATE_PORT_ID: return "PLAYER_UPDATE_PORT_ID";
224             case PLAYER_UPDATE_MUTED: return "PLAYER_UPDATE_MUTED";
225             case PLAYER_UPDATE_FORMAT: return "PLAYER_UPDATE_FORMAT";
226             default:
227                 return "invalid state " + state;
228         }
229     }
230 
231     /**
232      * @hide
233      * Used to update the spatialization status of a player through its port ID. Must be kept in
234      * sync with frameworks/native/include/audiomanager/AudioManager.h
235      */
236     public static final String EXTRA_PLAYER_EVENT_SPATIALIZED =
237             "android.media.extra.PLAYER_EVENT_SPATIALIZED";
238     /**
239      * @hide
240      * Used to update the sample rate of a player through its port ID. Must be kept in sync with
241      * frameworks/native/include/audiomanager/AudioManager.h
242      */
243     public static final String EXTRA_PLAYER_EVENT_SAMPLE_RATE =
244             "android.media.extra.PLAYER_EVENT_SAMPLE_RATE";
245     /**
246      * @hide
247      * Used to update the channel mask of a player through its port ID. Must be kept in sync with
248      * frameworks/native/include/audiomanager/AudioManager.h
249      */
250     public static final String EXTRA_PLAYER_EVENT_CHANNEL_MASK =
251             "android.media.extra.PLAYER_EVENT_CHANNEL_MASK";
252     /**
253      * @hide
254      * Used to update the mute state of a player through its port ID. Must be kept in sync with
255      * frameworks/native/include/audiomanager/AudioManager.h
256      */
257     public static final String EXTRA_PLAYER_EVENT_MUTE =
258             "android.media.extra.PLAYER_EVENT_MUTE";
259 
260     /**
261      * @hide
262      * Flag used when muted by master volume.
263      */
264     @SystemApi
265     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
266     public static final int MUTED_BY_MASTER = (1 << 0);
267     /**
268      * @hide
269      * Flag used when muted by stream volume.
270      */
271     @SystemApi
272     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
273     public static final int MUTED_BY_STREAM_VOLUME = (1 << 1);
274     /**
275      * @hide
276      * Flag used when muted by stream mute.
277      */
278     @SystemApi
279     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
280     public static final int MUTED_BY_STREAM_MUTED = (1 << 2);
281     /**
282      * @hide
283      * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO.
284      */
285     @SystemApi
286     @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
287     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
288     public static final int MUTED_BY_OP_PLAY_AUDIO = (1 << 3);
289     /**
290      * @hide
291      * Flag used when playback is muted by AppOpsManager#OP_PLAY_AUDIO.
292      * @deprecated see {@link MUTED_BY_OP_PLAY_AUDIO}
293      */
294     @SystemApi
295     @Deprecated
296     @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
297     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
298     public static final int MUTED_BY_APP_OPS = MUTED_BY_OP_PLAY_AUDIO;
299     /**
300      * @hide
301      * Flag used when muted by client volume.
302      */
303     @SystemApi
304     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
305     public static final int MUTED_BY_CLIENT_VOLUME = (1 << 4);
306     /**
307      * @hide
308      * Flag used when muted by volume shaper.
309      */
310     @SystemApi
311     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
312     public static final int MUTED_BY_VOLUME_SHAPER = (1 << 5);
313     /**
314      * @hide
315      * Flag used when muted by the track's port volume.
316      *
317      * <p>Note: this will replace the stream volume mute when using the AudioFlinger port volume
318      * APIs
319      */
320     @SystemApi
321     @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
322     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
323     public static final int MUTED_BY_PORT_VOLUME = (1 << 6);
324 
325     /**
326      * @hide
327      * Flag used when playback is muted by AppOpsManager#OP_CONTROL_AUDIO.
328      */
329     @SystemApi
330     @FlaggedApi(FLAG_MUTED_BY_PORT_VOLUME_API)
331     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
332     public static final int MUTED_BY_OP_CONTROL_AUDIO = (1 << 7);
333 
334     /** @hide */
335     @IntDef(
336             flag = true,
337             value = {MUTED_BY_MASTER, MUTED_BY_STREAM_VOLUME, MUTED_BY_STREAM_MUTED,
338                     MUTED_BY_OP_PLAY_AUDIO, MUTED_BY_CLIENT_VOLUME, MUTED_BY_VOLUME_SHAPER,
339                     MUTED_BY_PORT_VOLUME, MUTED_BY_OP_CONTROL_AUDIO})
340     @Retention(RetentionPolicy.SOURCE)
341     public @interface PlayerMuteEvent {
342     }
343 
344     // immutable data
345     private final int mPlayerIId;
346 
347     // not final due to anonymization step
348     private int mPlayerType;
349     private int mClientUid;
350     private int mClientPid;
351     // the IPlayer reference and death monitor
352     private IPlayerShell mIPlayerShell;
353 
354     private int mPlayerState;
355     private AudioAttributes mPlayerAttr; // never null
356 
357     // lock for updateable properties
358     private final Object mUpdateablePropLock = new Object();
359 
360     @GuardedBy("mUpdateablePropLock")
361     private @NonNull int[] mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
362     @GuardedBy("mUpdateablePropLock")
363     private int mSessionId;
364     @GuardedBy("mUpdateablePropLock")
365     private @NonNull FormatInfo mFormatInfo;
366     @GuardedBy("mUpdateablePropLock")
367     @PlayerMuteEvent private int mMutedState;
368 
369     /**
370      * Never use without initializing parameters afterwards
371      */
AudioPlaybackConfiguration(int piid)372     private AudioPlaybackConfiguration(int piid) {
373         mPlayerIId = piid;
374         mIPlayerShell = null;
375     }
376 
377     /**
378      * @hide
379      */
AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid)380     public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
381         if (DEBUG) {
382             Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer
383                     + " sessionId=" + pic.mSessionId);
384         }
385         mPlayerIId = piid;
386         mPlayerType = pic.mPlayerType;
387         mClientUid = uid;
388         mClientPid = pid;
389         mMutedState = 0;
390         mDeviceIds = AudioPlaybackConfiguration.PLAYER_DEVICEIDS_INVALID;
391         mPlayerState = PLAYER_STATE_IDLE;
392         mPlayerAttr = pic.mAttributes;
393         if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
394             mIPlayerShell = new IPlayerShell(this, pic.mIPlayer);
395         } else {
396             mIPlayerShell = null;
397         }
398         mSessionId = pic.mSessionId;
399         mFormatInfo = FormatInfo.DEFAULT;
400     }
401 
402     /**
403      * @hide
404      */
init()405     public void init() {
406         synchronized (this) {
407             if (mIPlayerShell != null) {
408                 mIPlayerShell.monitorDeath();
409             }
410         }
411     }
412 
413     // sets the fields that are updateable and require synchronization
setUpdateableFields(int[] deviceIds, int sessionId, int mutedState, FormatInfo format)414     private void setUpdateableFields(int[] deviceIds, int sessionId, int mutedState,
415             FormatInfo format)
416     {
417         synchronized (mUpdateablePropLock) {
418             mDeviceIds = deviceIds;
419             mSessionId = sessionId;
420             mMutedState = mutedState;
421             mFormatInfo = format;
422         }
423     }
424     // Note that this method is called server side, so no "privileged" information is ever sent
425     // to a client that is not supposed to have access to it.
426     /**
427      * @hide
428      * Creates a copy of the playback configuration that is stripped of any data enabling
429      * identification of which application it is associated with ("anonymized").
430      * @param in the instance to copy from
431      */
anonymizedCopy(AudioPlaybackConfiguration in)432     public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) {
433         final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId);
434         anonymCopy.mPlayerState = in.mPlayerState;
435         // do not reuse the full attributes: only usage, content type and public flags are allowed
436         AudioAttributes.Builder builder = new AudioAttributes.Builder()
437                 .setContentType(in.mPlayerAttr.getContentType())
438                 .setFlags(in.mPlayerAttr.getFlags())
439                 .setAllowedCapturePolicy(
440                         in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL
441                                 ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE);
442         if (AudioAttributes.isSystemUsage(in.mPlayerAttr.getSystemUsage())) {
443             builder.setSystemUsage(in.mPlayerAttr.getSystemUsage());
444         } else {
445             builder.setUsage(in.mPlayerAttr.getUsage());
446         }
447         anonymCopy.mPlayerAttr = builder.build();
448         // anonymized data
449         anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN;
450         anonymCopy.mClientUid = PLAYER_UPID_INVALID;
451         anonymCopy.mClientPid = PLAYER_UPID_INVALID;
452         anonymCopy.mIPlayerShell = null;
453         anonymCopy.setUpdateableFields(
454                 /*deviceIds*/ new int[0],
455                 /*sessionId*/ AudioSystem.AUDIO_SESSION_ALLOCATE,
456                 /*mutedState*/ 0,
457                 FormatInfo.DEFAULT);
458         return anonymCopy;
459     }
460 
461     /**
462      * Return the {@link AudioAttributes} of the corresponding player.
463      * @return the audio attributes of the player
464      */
getAudioAttributes()465     public AudioAttributes getAudioAttributes() {
466         return mPlayerAttr;
467     }
468 
469     /**
470      * @hide
471      * Return the uid of the client application that created this player.
472      * @return the uid of the client
473      */
474     @SystemApi
getClientUid()475     public int getClientUid() {
476         return mClientUid;
477     }
478 
479     /**
480      * @hide
481      * Return the pid of the client application that created this player.
482      * @return the pid of the client
483      */
484     @SystemApi
getClientPid()485     public int getClientPid() {
486         return mClientPid;
487     }
488 
489     /**
490      * Returns information about the {@link AudioDeviceInfo} used for this playback.
491      * @return the audio playback device or null if the device is not available at the time of
492      * query.
493      * @deprecated this information was never populated
494      */
495     @Deprecated
496     @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
getAudioDeviceInfo()497     public @Nullable AudioDeviceInfo getAudioDeviceInfo() {
498         final int[] deviceIds;
499         synchronized (mUpdateablePropLock) {
500             deviceIds = mDeviceIds;
501         }
502         if (deviceIds.length == 0) {
503             return null;
504         }
505         return AudioManager.getDeviceForPortId(deviceIds[0], AudioManager.GET_DEVICES_OUTPUTS);
506     }
507 
508     /**
509      * @hide
510      * Returns information about the List of {@link AudioDeviceInfo} used for this playback.
511      * @return the audio playback devices
512      */
513     @SystemApi
514     @FlaggedApi(FLAG_ROUTED_DEVICE_IDS)
515     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
getAudioDeviceInfos()516     public @NonNull List<AudioDeviceInfo> getAudioDeviceInfos() {
517         List<AudioDeviceInfo> audioDeviceInfos = new ArrayList<AudioDeviceInfo>();
518         final int[] deviceIds;
519         synchronized (mUpdateablePropLock) {
520             deviceIds = mDeviceIds;
521         }
522 
523         for (int i = 0; i < deviceIds.length; i++) {
524             AudioDeviceInfo audioDeviceInfo = AudioManager.getDeviceForPortId(deviceIds[i],
525                     AudioManager.GET_DEVICES_OUTPUTS);
526             if (audioDeviceInfo != null) {
527                 audioDeviceInfos.add(audioDeviceInfo);
528             }
529         }
530         return audioDeviceInfos;
531     }
532 
533     /**
534      * @hide
535      * Return the audio session ID associated with this player.
536      * See {@link AudioManager#generateAudioSessionId()}.
537      * @return an audio session ID
538      */
539     @SystemApi
getSessionId()540     public @IntRange(from = 0) int getSessionId() {
541         synchronized (mUpdateablePropLock) {
542             return mSessionId;
543         }
544     }
545 
546     /**
547      * @hide
548      * Used for determining if the current player is muted.
549      * <br>Note that if this result is true then {@link #getMutedBy} will be > 0.
550      * @return {@code true} if the player associated with this configuration has been muted (by any
551      * given MUTED_BY_* source event) or {@code false} otherwise.
552      */
553     @SystemApi
554     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
isMuted()555     public boolean isMuted() {
556         synchronized (mUpdateablePropLock) {
557             return mMutedState != 0;
558         }
559     }
560 
561     /**
562      * @hide
563      * Returns a bitmask expressing the mute state as a combination of MUTED_BY_* flags.
564      * <br>A value of 0 corresponds to an unmuted player.
565      * @return the mute state.
566      */
567     @SystemApi
568     @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
getMutedBy()569     @PlayerMuteEvent public int getMutedBy() {
570         synchronized (mUpdateablePropLock) {
571             return mMutedState;
572         }
573     }
574 
575     /**
576      * @hide
577      * Return the type of player linked to this configuration.
578      * <br>Note that player types not exposed in the system API will be represented as
579      * {@link #PLAYER_TYPE_UNKNOWN}.
580      * @return the type of the player.
581      */
582     @SystemApi
getPlayerType()583     public @PlayerType int getPlayerType() {
584         switch (mPlayerType) {
585             case PLAYER_TYPE_HW_SOURCE:
586             case PLAYER_TYPE_EXTERNAL_PROXY:
587                 return PLAYER_TYPE_UNKNOWN;
588             default:
589                 return mPlayerType;
590         }
591     }
592 
593     /**
594      * @hide
595      * Return the current state of the player linked to this configuration. The return value is one
596      * of {@link #PLAYER_STATE_IDLE}, {@link #PLAYER_STATE_PAUSED}, {@link #PLAYER_STATE_STARTED},
597      * {@link #PLAYER_STATE_STOPPED}, {@link #PLAYER_STATE_RELEASED} or
598      * {@link #PLAYER_STATE_UNKNOWN}.
599      * @return the state of the player.
600      */
601     @SystemApi
getPlayerState()602     public @PlayerState int getPlayerState() {
603         return mPlayerState;
604     }
605 
606     /**
607      * @hide
608      * Return an identifier unique for the lifetime of the player.
609      * @return a player interface identifier
610      */
611     @SystemApi
getPlayerInterfaceId()612     public int getPlayerInterfaceId() {
613         return mPlayerIId;
614     }
615 
616     /**
617      * @hide
618      * Return a proxy for the player associated with this playback configuration
619      * @return a proxy player
620      */
621     @SystemApi
getPlayerProxy()622     public PlayerProxy getPlayerProxy() {
623         final IPlayerShell ips;
624         synchronized (this) {
625             ips = mIPlayerShell;
626         }
627         return ips == null ? null : new PlayerProxy(this);
628     }
629 
630     /**
631      * @hide
632      * Return whether this player's output is being processed by the spatializer effect backing
633      * the {@link android.media.Spatializer} implementation.
634      * @return true if spatialized, false if not or playback hasn't started
635      */
636     @SystemApi
isSpatialized()637     public boolean isSpatialized() {
638         synchronized (mUpdateablePropLock) {
639             return mFormatInfo.mIsSpatialized;
640         }
641     }
642 
643     /**
644      * @hide
645      * Return the sample rate in Hz of the content being played.
646      * @return the sample rate in Hertz, or 0 if playback hasn't started
647      */
648     @SystemApi
getSampleRate()649     public @IntRange(from = 0) int getSampleRate() {
650         synchronized (mUpdateablePropLock) {
651             return mFormatInfo.mSampleRate;
652         }
653     }
654 
655     /**
656      * @hide
657      * Return the player's channel mask
658      * @return the channel mask, or 0 if playback hasn't started. See {@link AudioFormat} and
659      *     the definitions for the <code>CHANNEL_OUT_*</code> values used for the mask's bitfield
660      */
661     @SystemApi
getChannelMask()662     public @AudioFormat.ChannelOut int getChannelMask() {
663         synchronized (mUpdateablePropLock) {
664             return (AudioFormat.convertNativeChannelMaskToOutMask(mFormatInfo.mNativeChannelMask));
665         }
666     }
667 
668     /**
669      * @hide
670      * @return the IPlayer interface for the associated player
671      */
getIPlayer()672     IPlayer getIPlayer() {
673         final IPlayerShell ips;
674         synchronized (this) {
675             ips = mIPlayerShell;
676         }
677         return ips == null ? null : ips.getIPlayer();
678     }
679 
680     /**
681      * @hide
682      * Handle a change of audio attributes
683      * @param attr
684      */
handleAudioAttributesEvent(@onNull AudioAttributes attr)685     public boolean handleAudioAttributesEvent(@NonNull AudioAttributes attr) {
686         final boolean changed = !attr.equals(mPlayerAttr);
687         mPlayerAttr = attr;
688         return changed;
689     }
690 
691     /**
692      * @hide
693      * Handle a change of audio session ID
694      * @param sessionId the audio session ID
695      */
handleSessionIdEvent(int sessionId)696     public boolean handleSessionIdEvent(int sessionId) {
697         synchronized (mUpdateablePropLock) {
698             final boolean changed = sessionId != mSessionId;
699             mSessionId = sessionId;
700             return changed;
701         }
702     }
703 
704     /**
705      * @hide
706      * Handle a change of the muted state
707      * @param mutedState the mute reason as a combination of {@link PlayerMuteEvent} flags
708      * @return true if the state changed, false otherwise
709      */
handleMutedEvent(@layerMuteEvent int mutedState)710     public boolean handleMutedEvent(@PlayerMuteEvent int mutedState) {
711         synchronized (mUpdateablePropLock) {
712             final boolean changed = mMutedState != mutedState;
713             mMutedState = mutedState;
714             return changed;
715         }
716     }
717 
718     /**
719      * @hide
720      * Handle a change of playback format
721      * @param fi the latest format information
722      * @return true if the format changed, false otherwise
723      */
handleFormatEvent(@onNull FormatInfo fi)724     public boolean handleFormatEvent(@NonNull FormatInfo fi) {
725         synchronized (mUpdateablePropLock) {
726             final boolean changed = !mFormatInfo.equals(fi);
727             mFormatInfo = fi;
728             return changed;
729         }
730     }
731 
732     /**
733      * @hide
734      * Handle a player state change
735      * @param event
736      * @param deviceIds an array of device ids. This can be empty.
737      * <br>Note device ids are non-empty for {@code PLAYER_UPDATE_DEVICE_ID} or
738      * <br>{@code PLAYER_STATE_STARTED} events, as the device ids will be emptied when pausing
739      * <br>or stopping playback. It will be set to active devices when playback starts or
740      * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the
741      * <br>devices change in the middle of playback.
742      * @return true if the state changed, false otherwise
743      */
handleStateEvent(int event, int[] deviceIds)744     public boolean handleStateEvent(int event, int[] deviceIds) {
745         boolean changed = false;
746         synchronized (mUpdateablePropLock) {
747 
748             // Do not update if it is only device id update
749             if (event != PLAYER_UPDATE_DEVICE_ID) {
750                 changed = (mPlayerState != event);
751                 mPlayerState = event;
752             }
753 
754             if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) {
755                 changed = changed || !Arrays.equals(mDeviceIds, deviceIds);
756                 mDeviceIds = deviceIds;
757             }
758 
759             if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
760                 mIPlayerShell.release();
761                 mIPlayerShell = null;
762             }
763         }
764         return changed;
765     }
766 
767     // To report IPlayer death from death recipient
768     /** @hide */
769     public interface PlayerDeathMonitor {
playerDeath(int piid)770         public void playerDeath(int piid);
771     }
772     /** @hide */
773     public static PlayerDeathMonitor sPlayerDeathMonitor;
774 
playerDied()775     private void playerDied() {
776         if (sPlayerDeathMonitor != null) {
777             sPlayerDeathMonitor.playerDeath(mPlayerIId);
778         }
779     }
780 
isMuteAffectingActiveState()781     private boolean isMuteAffectingActiveState() {
782         return (mMutedState & MUTED_BY_CLIENT_VOLUME) != 0
783                 || (mMutedState & MUTED_BY_VOLUME_SHAPER) != 0
784                 || (mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0;
785     }
786 
787     /**
788      * @hide
789      * Returns true if the player is considered "active", i.e. actively playing with unmuted
790      * volume, and thus in a state that should make it considered for the list public (sanitized)
791      * active playback configurations
792      * @return true if active
793      */
794     @SystemApi
isActive()795     public boolean isActive() {
796         switch (mPlayerState) {
797             case PLAYER_STATE_STARTED:
798                 return !isMuteAffectingActiveState();
799             case PLAYER_STATE_UNKNOWN:
800             case PLAYER_STATE_RELEASED:
801             case PLAYER_STATE_IDLE:
802             case PLAYER_STATE_PAUSED:
803             case PLAYER_STATE_STOPPED:
804             default:
805                 return false;
806         }
807     }
808 
809     /**
810      * @hide
811      * For AudioService dump
812      * @param pw
813      */
dump(PrintWriter pw)814     public void dump(PrintWriter pw) {
815         pw.println("  " + this);
816     }
817 
818     public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR
819             = new Parcelable.Creator<AudioPlaybackConfiguration>() {
820         /**
821          * Rebuilds an AudioPlaybackConfiguration previously stored with writeToParcel().
822          * @param p Parcel object to read the AudioPlaybackConfiguration from
823          * @return a new AudioPlaybackConfiguration created from the data in the parcel
824          */
825         public AudioPlaybackConfiguration createFromParcel(Parcel p) {
826             return new AudioPlaybackConfiguration(p);
827         }
828         public AudioPlaybackConfiguration[] newArray(int size) {
829             return new AudioPlaybackConfiguration[size];
830         }
831     };
832 
833     @Override
hashCode()834     public int hashCode() {
835         synchronized (mUpdateablePropLock) {
836             return Objects.hash(mPlayerIId, Arrays.toString(mDeviceIds), mMutedState, mPlayerType,
837                     mClientUid, mClientPid, mSessionId);
838         }
839     }
840 
841     @Override
describeContents()842     public int describeContents() {
843         return 0;
844     }
845 
846     @Override
writeToParcel(Parcel dest, int flags)847     public void writeToParcel(Parcel dest, int flags) {
848         synchronized (mUpdateablePropLock) {
849             dest.writeInt(mPlayerIId);
850             dest.writeIntArray(mDeviceIds);
851             dest.writeInt(mMutedState);
852             dest.writeInt(mPlayerType);
853             dest.writeInt(mClientUid);
854             dest.writeInt(mClientPid);
855             dest.writeInt(mPlayerState);
856             mPlayerAttr.writeToParcel(dest, 0);
857             final IPlayerShell ips;
858             synchronized (this) {
859                 ips = mIPlayerShell;
860             }
861             dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
862             dest.writeInt(mSessionId);
863             mFormatInfo.writeToParcel(dest, 0);
864         }
865     }
866 
AudioPlaybackConfiguration(Parcel in)867     private AudioPlaybackConfiguration(Parcel in) {
868         mPlayerIId = in.readInt();
869         mDeviceIds = new int[in.readInt()];
870         for (int i = 0; i < mDeviceIds.length; i++) {
871             mDeviceIds[i] = in.readInt();
872         }
873         mMutedState = in.readInt();
874         mPlayerType = in.readInt();
875         mClientUid = in.readInt();
876         mClientPid = in.readInt();
877         mPlayerState = in.readInt();
878         mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
879         final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
880         mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
881         mSessionId = in.readInt();
882         mFormatInfo = FormatInfo.CREATOR.createFromParcel(in);
883     }
884 
885     @Override
equals(Object o)886     public boolean equals(Object o) {
887         if (this == o) return true;
888         if (o == null || !(o instanceof AudioPlaybackConfiguration)) return false;
889 
890         AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
891 
892         return ((mPlayerIId == that.mPlayerIId)
893                 && Arrays.equals(mDeviceIds, that.mDeviceIds)
894                 && (mMutedState == that.mMutedState)
895                 && (mPlayerType == that.mPlayerType)
896                 && (mClientUid == that.mClientUid)
897                 && (mClientPid == that.mClientPid))
898                 && (mSessionId == that.mSessionId);
899     }
900 
901     @Override
toString()902     public String toString() {
903         StringBuilder apcToString = new StringBuilder();
904         synchronized (mUpdateablePropLock) {
905             apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
906                     " deviceIds:").append(Arrays.toString(mDeviceIds)).append(" type:").append(
907                     toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(
908                     mClientUid).append(
909                     "/").append(mClientPid).append(" state:").append(
910                     toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(
911                     mPlayerAttr).append(
912                     " sessionId:").append(mSessionId).append(" mutedState:");
913             if (mMutedState == 0) {
914                 apcToString.append("none ");
915             } else {
916                 if ((mMutedState & MUTED_BY_MASTER) != 0) {
917                     apcToString.append("master ");
918                 }
919                 if ((mMutedState & MUTED_BY_STREAM_VOLUME) != 0) {
920                     apcToString.append("streamVolume ");
921                 }
922                 if ((mMutedState & MUTED_BY_STREAM_MUTED) != 0) {
923                     apcToString.append("streamMute ");
924                 }
925                 if ((mMutedState & MUTED_BY_OP_PLAY_AUDIO) != 0) {
926                     apcToString.append("opPlayAudio ");
927                 }
928                 if ((mMutedState & MUTED_BY_CLIENT_VOLUME) != 0) {
929                     apcToString.append("clientVolume ");
930                 }
931                 if ((mMutedState & MUTED_BY_VOLUME_SHAPER) != 0) {
932                     apcToString.append("volumeShaper ");
933                 }
934                 if ((mMutedState & MUTED_BY_PORT_VOLUME) != 0) {
935                     apcToString.append("portVolume ");
936                 }
937                 if ((mMutedState & MUTED_BY_OP_CONTROL_AUDIO) != 0) {
938                     apcToString.append("opControlAudio ");
939                 }
940             }
941             apcToString.append(" ").append(mFormatInfo);
942         }
943         return apcToString.toString();
944     }
945 
946     //=====================================================================
947     // Inner class for corresponding IPlayer and its death monitoring
948     static final class IPlayerShell implements IBinder.DeathRecipient {
949 
950         final AudioPlaybackConfiguration mMonitor; // never null
951         private volatile IPlayer mIPlayer;
952 
IPlayerShell(@onNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer)953         IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) {
954             mMonitor = monitor;
955             mIPlayer = iplayer;
956         }
957 
monitorDeath()958         synchronized void monitorDeath() {
959             if (mIPlayer == null) {
960                 return;
961             }
962             try {
963                 mIPlayer.asBinder().linkToDeath(this, 0);
964             } catch (RemoteException e) {
965                 if (mMonitor != null) {
966                     Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e);
967                 } else {
968                     Log.w(TAG, "Could not link to client death", e);
969                 }
970             }
971         }
972 
getIPlayer()973         IPlayer getIPlayer() {
974             return mIPlayer;
975         }
976 
binderDied()977         public void binderDied() {
978             if (mMonitor != null) {
979                 if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);}
980                 mMonitor.playerDied();
981             } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); }
982         }
983 
release()984         synchronized void release() {
985             if (mIPlayer == null) {
986                 return;
987             }
988             mIPlayer.asBinder().unlinkToDeath(this, 0);
989             mIPlayer = null;
990             Binder.flushPendingCommands();
991         }
992     }
993 
994     //=====================================================================
995 
996     /**
997      * @hide
998      * Class to store sample rate, channel mask, and spatialization status
999      */
1000     public static final class FormatInfo implements Parcelable {
1001         static final FormatInfo DEFAULT = new FormatInfo(
1002                 /*spatialized*/ false, /*channel mask*/ 0, /*sample rate*/ 0);
1003         final boolean mIsSpatialized;
1004         final int mNativeChannelMask;
1005         final int mSampleRate;
1006 
FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate)1007         public FormatInfo(boolean isSpatialized, int nativeChannelMask, int sampleRate) {
1008             mIsSpatialized = isSpatialized;
1009             mNativeChannelMask = nativeChannelMask;
1010             mSampleRate = sampleRate;
1011         }
1012 
1013         @Override
toString()1014         public String toString() {
1015             return "FormatInfo{"
1016                     + "isSpatialized=" + mIsSpatialized
1017                     + ", channelMask=0x" + Integer.toHexString(mNativeChannelMask)
1018                     + ", sampleRate=" + mSampleRate
1019                     + '}';
1020         }
1021 
1022         @Override
equals(Object o)1023         public boolean equals(Object o) {
1024             if (this == o) return true;
1025             if (!(o instanceof FormatInfo)) return false;
1026             FormatInfo that = (FormatInfo) o;
1027             return mIsSpatialized == that.mIsSpatialized
1028                     && mNativeChannelMask == that.mNativeChannelMask
1029                     && mSampleRate == that.mSampleRate;
1030         }
1031 
1032         @Override
hashCode()1033         public int hashCode() {
1034             return Objects.hash(mIsSpatialized, mNativeChannelMask, mSampleRate);
1035         }
1036 
1037         @Override
describeContents()1038         public int describeContents() {
1039             return 0;
1040         }
1041 
1042         @Override
writeToParcel(@ndroidx.annotation.NonNull Parcel dest, int flags)1043         public void writeToParcel(@androidx.annotation.NonNull Parcel dest, int flags) {
1044             dest.writeBoolean(mIsSpatialized);
1045             dest.writeInt(mNativeChannelMask);
1046             dest.writeInt(mSampleRate);
1047         }
1048 
FormatInfo(Parcel in)1049         private FormatInfo(Parcel in) {
1050             this(
1051                     in.readBoolean(), // isSpatialized
1052                     in.readInt(),     // channelMask
1053                     in.readInt()      // sampleRate
1054             );
1055         }
1056 
1057         public static final @NonNull Parcelable.Creator<FormatInfo> CREATOR =
1058                 new Parcelable.Creator<FormatInfo>() {
1059             public FormatInfo createFromParcel(Parcel p) {
1060                 return new FormatInfo(p);
1061             }
1062             public FormatInfo[] newArray(int size) {
1063                 return new FormatInfo[size];
1064             }
1065         };
1066     }
1067     //=====================================================================
1068     // Utilities
1069 
1070     /** @hide */
toLogFriendlyPlayerType(int type)1071     public static String toLogFriendlyPlayerType(int type) {
1072         switch (type) {
1073             case PLAYER_TYPE_UNKNOWN: return "unknown";
1074             case PLAYER_TYPE_JAM_AUDIOTRACK: return "android.media.AudioTrack";
1075             case PLAYER_TYPE_JAM_MEDIAPLAYER: return "android.media.MediaPlayer";
1076             case PLAYER_TYPE_JAM_SOUNDPOOL:   return "android.media.SoundPool";
1077             case PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE:
1078                 return "OpenSL ES AudioPlayer (Buffer Queue)";
1079             case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD:
1080                 return "OpenSL ES AudioPlayer (URI/FD)";
1081             case PLAYER_TYPE_AAUDIO: return "AAudio";
1082             case PLAYER_TYPE_HW_SOURCE: return "hardware source";
1083             case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy";
1084             default:
1085                 return "unknown player type " + type + " - FIXME";
1086         }
1087     }
1088 
1089     /** @hide */
toLogFriendlyPlayerState(int state)1090     public static String toLogFriendlyPlayerState(int state) {
1091         switch (state) {
1092             case PLAYER_STATE_UNKNOWN: return "unknown";
1093             case PLAYER_STATE_RELEASED: return "released";
1094             case PLAYER_STATE_IDLE: return "idle";
1095             case PLAYER_STATE_STARTED: return "started";
1096             case PLAYER_STATE_PAUSED: return "paused";
1097             case PLAYER_STATE_STOPPED: return "stopped";
1098             case PLAYER_UPDATE_DEVICE_ID: return "device updated";
1099             case PLAYER_UPDATE_PORT_ID: return "port updated";
1100             case PLAYER_UPDATE_MUTED: return "muted updated";
1101             default:
1102                 return "unknown player state - FIXME";
1103         }
1104     }
1105 }
1106