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