• 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 
22 import android.annotation.IntDef;
23 import android.annotation.NonNull;
24 import android.annotation.SystemApi;
25 import android.os.Binder;
26 import android.os.IBinder;
27 import android.os.Parcel;
28 import android.os.Parcelable;
29 import android.os.RemoteException;
30 import android.util.Log;
31 
32 import java.io.PrintWriter;
33 import java.lang.annotation.Retention;
34 import java.lang.annotation.RetentionPolicy;
35 import java.util.Objects;
36 
37 /**
38  * The AudioPlaybackConfiguration class collects the information describing an audio playback
39  * session.
40  */
41 public final class AudioPlaybackConfiguration implements Parcelable {
42     private static final String TAG = new String("AudioPlaybackConfiguration");
43 
44     private static final boolean DEBUG = false;
45 
46     /** @hide */
47     public static final int PLAYER_PIID_INVALID = -1;
48     /** @hide */
49     public static final int PLAYER_UPID_INVALID = -1;
50 
51     // information about the implementation
52     /**
53      * @hide
54      * An unknown type of player
55      */
56     @SystemApi
57     public static final int PLAYER_TYPE_UNKNOWN = -1;
58     /**
59      * @hide
60      * Player backed by a java android.media.AudioTrack player
61      */
62     @SystemApi
63     public static final int PLAYER_TYPE_JAM_AUDIOTRACK = 1;
64     /**
65      * @hide
66      * Player backed by a java android.media.MediaPlayer player
67      */
68     @SystemApi
69     public static final int PLAYER_TYPE_JAM_MEDIAPLAYER = 2;
70     /**
71      * @hide
72      * Player backed by a java android.media.SoundPool player
73      */
74     @SystemApi
75     public static final int PLAYER_TYPE_JAM_SOUNDPOOL = 3;
76     /**
77      * @hide
78      * Player backed by a C OpenSL ES AudioPlayer player with a BufferQueue source
79      */
80     @SystemApi
81     public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE = 11;
82     /**
83      * @hide
84      * Player backed by a C OpenSL ES AudioPlayer player with a URI or FD source
85      */
86     @SystemApi
87     public static final int PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD = 12;
88 
89     /**
90      * @hide
91      * Player backed an AAudio player.
92      * Note this type is not in System API so it will not be returned in public API calls
93      */
94     // TODO unhide for SystemApi, update getPlayerType()
95     public static final int PLAYER_TYPE_AAUDIO = 13;
96 
97     /**
98      * @hide
99      * Player backed a hardware source, whose state is visible in the Android audio policy manager.
100      * Note this type is not in System API so it will not be returned in public API calls
101      */
102     // TODO unhide for SystemApi, update getPlayerType()
103     public static final int PLAYER_TYPE_HW_SOURCE = 14;
104 
105     /**
106      * @hide
107      * Player is a proxy for an audio player whose audio and state doesn't go through the Android
108      * audio framework.
109      * Note this type is not in System API so it will not be returned in public API calls
110      */
111     // TODO unhide for SystemApi, update getPlayerType()
112     public static final int PLAYER_TYPE_EXTERNAL_PROXY = 15;
113 
114     /** @hide */
115     @IntDef({
116         PLAYER_TYPE_UNKNOWN,
117         PLAYER_TYPE_JAM_AUDIOTRACK,
118         PLAYER_TYPE_JAM_MEDIAPLAYER,
119         PLAYER_TYPE_JAM_SOUNDPOOL,
120         PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE,
121         PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD,
122     })
123     @Retention(RetentionPolicy.SOURCE)
124     public @interface PlayerType {}
125 
126     /**
127      * @hide
128      * An unknown player state
129      */
130     @SystemApi
131     public static final int PLAYER_STATE_UNKNOWN = -1;
132     /**
133      * @hide
134      * The resources of the player have been released, it cannot play anymore
135      */
136     @SystemApi
137     public static final int PLAYER_STATE_RELEASED = 0;
138     /**
139      * @hide
140      * The state of a player when it's created
141      */
142     @SystemApi
143     public static final int PLAYER_STATE_IDLE = 1;
144     /**
145      * @hide
146      * The state of a player that is actively playing
147      */
148     @SystemApi
149     public static final int PLAYER_STATE_STARTED = 2;
150     /**
151      * @hide
152      * The state of a player where playback is paused
153      */
154     @SystemApi
155     public static final int PLAYER_STATE_PAUSED = 3;
156     /**
157      * @hide
158      * The state of a player where playback is stopped
159      */
160     @SystemApi
161     public static final int PLAYER_STATE_STOPPED = 4;
162 
163     /** @hide */
164     @IntDef({
165         PLAYER_STATE_UNKNOWN,
166         PLAYER_STATE_RELEASED,
167         PLAYER_STATE_IDLE,
168         PLAYER_STATE_STARTED,
169         PLAYER_STATE_PAUSED,
170         PLAYER_STATE_STOPPED
171     })
172     @Retention(RetentionPolicy.SOURCE)
173     public @interface PlayerState {}
174 
175     // immutable data
176     private final int mPlayerIId;
177 
178     // not final due to anonymization step
179     private int mPlayerType;
180     private int mClientUid;
181     private int mClientPid;
182     // the IPlayer reference and death monitor
183     private IPlayerShell mIPlayerShell;
184 
185     private int mPlayerState;
186     private AudioAttributes mPlayerAttr; // never null
187 
188     /**
189      * Never use without initializing parameters afterwards
190      */
AudioPlaybackConfiguration(int piid)191     private AudioPlaybackConfiguration(int piid) {
192         mPlayerIId = piid;
193         mIPlayerShell = null;
194     }
195 
196     /**
197      * @hide
198      */
AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid)199     public AudioPlaybackConfiguration(PlayerBase.PlayerIdCard pic, int piid, int uid, int pid) {
200         if (DEBUG) { Log.d(TAG, "new: piid=" + piid + " iplayer=" + pic.mIPlayer); }
201         mPlayerIId = piid;
202         mPlayerType = pic.mPlayerType;
203         mClientUid = uid;
204         mClientPid = pid;
205         mPlayerState = PLAYER_STATE_IDLE;
206         mPlayerAttr = pic.mAttributes;
207         if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
208             mIPlayerShell = new IPlayerShell(this, pic.mIPlayer);
209         } else {
210             mIPlayerShell = null;
211         }
212     }
213 
214     /**
215      * @hide
216      */
init()217     public void init() {
218         synchronized (this) {
219             if (mIPlayerShell != null) {
220                 mIPlayerShell.monitorDeath();
221             }
222         }
223     }
224 
225     // Note that this method is called server side, so no "privileged" information is ever sent
226     // to a client that is not supposed to have access to it.
227     /**
228      * @hide
229      * Creates a copy of the playback configuration that is stripped of any data enabling
230      * identification of which application it is associated with ("anonymized").
231      * @param toSanitize
232      */
anonymizedCopy(AudioPlaybackConfiguration in)233     public static AudioPlaybackConfiguration anonymizedCopy(AudioPlaybackConfiguration in) {
234         final AudioPlaybackConfiguration anonymCopy = new AudioPlaybackConfiguration(in.mPlayerIId);
235         anonymCopy.mPlayerState = in.mPlayerState;
236         // do not reuse the full attributes: only usage, content type and public flags are allowed
237         anonymCopy.mPlayerAttr = new AudioAttributes.Builder()
238                 .setUsage(in.mPlayerAttr.getUsage())
239                 .setContentType(in.mPlayerAttr.getContentType())
240                 .setFlags(in.mPlayerAttr.getFlags())
241                 .setAllowedCapturePolicy(
242                         in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL
243                         ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE)
244                 .build();
245         // anonymized data
246         anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN;
247         anonymCopy.mClientUid = PLAYER_UPID_INVALID;
248         anonymCopy.mClientPid = PLAYER_UPID_INVALID;
249         anonymCopy.mIPlayerShell = null;
250         return anonymCopy;
251     }
252 
253     /**
254      * Return the {@link AudioAttributes} of the corresponding player.
255      * @return the audio attributes of the player
256      */
getAudioAttributes()257     public AudioAttributes getAudioAttributes() {
258         return mPlayerAttr;
259     }
260 
261     /**
262      * @hide
263      * Return the uid of the client application that created this player.
264      * @return the uid of the client
265      */
266     @SystemApi
getClientUid()267     public int getClientUid() {
268         return mClientUid;
269     }
270 
271     /**
272      * @hide
273      * Return the pid of the client application that created this player.
274      * @return the pid of the client
275      */
276     @SystemApi
getClientPid()277     public int getClientPid() {
278         return mClientPid;
279     }
280 
281     /**
282      * @hide
283      * Return the type of player linked to this configuration. The return value is one of
284      * {@link #PLAYER_TYPE_JAM_AUDIOTRACK}, {@link #PLAYER_TYPE_JAM_MEDIAPLAYER},
285      * {@link #PLAYER_TYPE_JAM_SOUNDPOOL}, {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE},
286      * {@link #PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD}, or {@link #PLAYER_TYPE_UNKNOWN}.
287      * <br>Note that player types not exposed in the system API will be represented as
288      * {@link #PLAYER_TYPE_UNKNOWN}.
289      * @return the type of the player.
290      */
291     @SystemApi
getPlayerType()292     public @PlayerType int getPlayerType() {
293         switch (mPlayerType) {
294             case PLAYER_TYPE_AAUDIO:
295             case PLAYER_TYPE_HW_SOURCE:
296             case PLAYER_TYPE_EXTERNAL_PROXY:
297                 return PLAYER_TYPE_UNKNOWN;
298             default:
299                 return mPlayerType;
300         }
301     }
302 
303     /**
304      * @hide
305      * Return the current state of the player linked to this configuration. The return value is one
306      * of {@link #PLAYER_STATE_IDLE}, {@link #PLAYER_STATE_PAUSED}, {@link #PLAYER_STATE_STARTED},
307      * {@link #PLAYER_STATE_STOPPED}, {@link #PLAYER_STATE_RELEASED} or
308      * {@link #PLAYER_STATE_UNKNOWN}.
309      * @return the state of the player.
310      */
311     @SystemApi
getPlayerState()312     public @PlayerState int getPlayerState() {
313         return mPlayerState;
314     }
315 
316     /**
317      * @hide
318      * Return an identifier unique for the lifetime of the player.
319      * @return a player interface identifier
320      */
321     @SystemApi
getPlayerInterfaceId()322     public int getPlayerInterfaceId() {
323         return mPlayerIId;
324     }
325 
326     /**
327      * @hide
328      * Return a proxy for the player associated with this playback configuration
329      * @return a proxy player
330      */
331     @SystemApi
getPlayerProxy()332     public PlayerProxy getPlayerProxy() {
333         final IPlayerShell ips;
334         synchronized (this) {
335             ips = mIPlayerShell;
336         }
337         return ips == null ? null : new PlayerProxy(this);
338     }
339 
340     /**
341      * @hide
342      * @return the IPlayer interface for the associated player
343      */
getIPlayer()344     IPlayer getIPlayer() {
345         final IPlayerShell ips;
346         synchronized (this) {
347             ips = mIPlayerShell;
348         }
349         return ips == null ? null : ips.getIPlayer();
350     }
351 
352     /**
353      * @hide
354      * Handle a change of audio attributes
355      * @param attr
356      */
handleAudioAttributesEvent(@onNull AudioAttributes attr)357     public boolean handleAudioAttributesEvent(@NonNull AudioAttributes attr) {
358         final boolean changed = !attr.equals(mPlayerAttr);
359         mPlayerAttr = attr;
360         return changed;
361     }
362 
363     /**
364      * @hide
365      * Handle a player state change
366      * @param event
367      * @return true if the state changed, false otherwise
368      */
handleStateEvent(int event)369     public boolean handleStateEvent(int event) {
370         final boolean changed;
371         synchronized (this) {
372             changed = (mPlayerState != event);
373             mPlayerState = event;
374             if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
375                 mIPlayerShell.release();
376                 mIPlayerShell = null;
377             }
378         }
379         return changed;
380     }
381 
382     // To report IPlayer death from death recipient
383     /** @hide */
384     public interface PlayerDeathMonitor {
playerDeath(int piid)385         public void playerDeath(int piid);
386     }
387     /** @hide */
388     public static PlayerDeathMonitor sPlayerDeathMonitor;
389 
playerDied()390     private void playerDied() {
391         if (sPlayerDeathMonitor != null) {
392             sPlayerDeathMonitor.playerDeath(mPlayerIId);
393         }
394     }
395 
396     /**
397      * @hide
398      * Returns true if the player is considered "active", i.e. actively playing, and thus
399      * in a state that should make it considered for the list public (sanitized) active playback
400      * configurations
401      * @return true if active
402      */
isActive()403     public boolean isActive() {
404         switch (mPlayerState) {
405             case PLAYER_STATE_STARTED:
406                 return true;
407             case PLAYER_STATE_UNKNOWN:
408             case PLAYER_STATE_RELEASED:
409             case PLAYER_STATE_IDLE:
410             case PLAYER_STATE_PAUSED:
411             case PLAYER_STATE_STOPPED:
412             default:
413                 return false;
414         }
415     }
416 
417     /**
418      * @hide
419      * For AudioService dump
420      * @param pw
421      */
dump(PrintWriter pw)422     public void dump(PrintWriter pw) {
423         pw.println("  " + toLogFriendlyString(this));
424     }
425 
426     /**
427      * @hide
428      */
toLogFriendlyString(AudioPlaybackConfiguration apc)429     public static String toLogFriendlyString(AudioPlaybackConfiguration apc) {
430         return new String("ID:" + apc.mPlayerIId
431                 + " -- type:" + toLogFriendlyPlayerType(apc.mPlayerType)
432                 + " -- u/pid:" + apc.mClientUid +"/" + apc.mClientPid
433                 + " -- state:" + toLogFriendlyPlayerState(apc.mPlayerState)
434                 + " -- attr:" + apc.mPlayerAttr);
435     }
436 
437     public static final @android.annotation.NonNull Parcelable.Creator<AudioPlaybackConfiguration> CREATOR
438             = new Parcelable.Creator<AudioPlaybackConfiguration>() {
439         /**
440          * Rebuilds an AudioPlaybackConfiguration previously stored with writeToParcel().
441          * @param p Parcel object to read the AudioPlaybackConfiguration from
442          * @return a new AudioPlaybackConfiguration created from the data in the parcel
443          */
444         public AudioPlaybackConfiguration createFromParcel(Parcel p) {
445             return new AudioPlaybackConfiguration(p);
446         }
447         public AudioPlaybackConfiguration[] newArray(int size) {
448             return new AudioPlaybackConfiguration[size];
449         }
450     };
451 
452     @Override
hashCode()453     public int hashCode() {
454         return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid);
455     }
456 
457     @Override
describeContents()458     public int describeContents() {
459         return 0;
460     }
461 
462     @Override
writeToParcel(Parcel dest, int flags)463     public void writeToParcel(Parcel dest, int flags) {
464         dest.writeInt(mPlayerIId);
465         dest.writeInt(mPlayerType);
466         dest.writeInt(mClientUid);
467         dest.writeInt(mClientPid);
468         dest.writeInt(mPlayerState);
469         mPlayerAttr.writeToParcel(dest, 0);
470         final IPlayerShell ips;
471         synchronized (this) {
472             ips = mIPlayerShell;
473         }
474         dest.writeStrongInterface(ips == null ? null : ips.getIPlayer());
475     }
476 
AudioPlaybackConfiguration(Parcel in)477     private AudioPlaybackConfiguration(Parcel in) {
478         mPlayerIId = in.readInt();
479         mPlayerType = in.readInt();
480         mClientUid = in.readInt();
481         mClientPid = in.readInt();
482         mPlayerState = in.readInt();
483         mPlayerAttr = AudioAttributes.CREATOR.createFromParcel(in);
484         final IPlayer p = IPlayer.Stub.asInterface(in.readStrongBinder());
485         mIPlayerShell = (p == null) ? null : new IPlayerShell(null, p);
486     }
487 
488     @Override
equals(Object o)489     public boolean equals(Object o) {
490         if (this == o) return true;
491         if (o == null || !(o instanceof AudioPlaybackConfiguration)) return false;
492 
493         AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
494 
495         return ((mPlayerIId == that.mPlayerIId)
496                 && (mPlayerType == that.mPlayerType)
497                 && (mClientUid == that.mClientUid)
498                 && (mClientPid == that.mClientPid));
499     }
500 
501     //=====================================================================
502     // Inner class for corresponding IPlayer and its death monitoring
503     static final class IPlayerShell implements IBinder.DeathRecipient {
504 
505         final AudioPlaybackConfiguration mMonitor; // never null
506         private volatile IPlayer mIPlayer;
507 
IPlayerShell(@onNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer)508         IPlayerShell(@NonNull AudioPlaybackConfiguration monitor, @NonNull IPlayer iplayer) {
509             mMonitor = monitor;
510             mIPlayer = iplayer;
511         }
512 
monitorDeath()513         synchronized void monitorDeath() {
514             if (mIPlayer == null) {
515                 return;
516             }
517             try {
518                 mIPlayer.asBinder().linkToDeath(this, 0);
519             } catch (RemoteException e) {
520                 if (mMonitor != null) {
521                     Log.w(TAG, "Could not link to client death for piid=" + mMonitor.mPlayerIId, e);
522                 } else {
523                     Log.w(TAG, "Could not link to client death", e);
524                 }
525             }
526         }
527 
getIPlayer()528         IPlayer getIPlayer() {
529             return mIPlayer;
530         }
531 
binderDied()532         public void binderDied() {
533             if (mMonitor != null) {
534                 if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied for piid=" + mMonitor.mPlayerIId);}
535                 mMonitor.playerDied();
536             } else if (DEBUG) { Log.i(TAG, "IPlayerShell binderDied"); }
537         }
538 
release()539         synchronized void release() {
540             if (mIPlayer == null) {
541                 return;
542             }
543             mIPlayer.asBinder().unlinkToDeath(this, 0);
544             mIPlayer = null;
545             Binder.flushPendingCommands();
546         }
547     }
548 
549     //=====================================================================
550     // Utilities
551 
552     /** @hide */
toLogFriendlyPlayerType(int type)553     public static String toLogFriendlyPlayerType(int type) {
554         switch (type) {
555             case PLAYER_TYPE_UNKNOWN: return "unknown";
556             case PLAYER_TYPE_JAM_AUDIOTRACK: return "android.media.AudioTrack";
557             case PLAYER_TYPE_JAM_MEDIAPLAYER: return "android.media.MediaPlayer";
558             case PLAYER_TYPE_JAM_SOUNDPOOL:   return "android.media.SoundPool";
559             case PLAYER_TYPE_SLES_AUDIOPLAYER_BUFFERQUEUE:
560                 return "OpenSL ES AudioPlayer (Buffer Queue)";
561             case PLAYER_TYPE_SLES_AUDIOPLAYER_URI_FD:
562                 return "OpenSL ES AudioPlayer (URI/FD)";
563             case PLAYER_TYPE_AAUDIO: return "AAudio";
564             case PLAYER_TYPE_HW_SOURCE: return "hardware source";
565             case PLAYER_TYPE_EXTERNAL_PROXY: return "external proxy";
566             default:
567                 return "unknown player type " + type + " - FIXME";
568         }
569     }
570 
571     /** @hide */
toLogFriendlyPlayerState(int state)572     public static String toLogFriendlyPlayerState(int state) {
573         switch (state) {
574             case PLAYER_STATE_UNKNOWN: return "unknown";
575             case PLAYER_STATE_RELEASED: return "released";
576             case PLAYER_STATE_IDLE: return "idle";
577             case PLAYER_STATE_STARTED: return "started";
578             case PLAYER_STATE_PAUSED: return "paused";
579             case PLAYER_STATE_STOPPED: return "stopped";
580             default:
581                 return "unknown player state - FIXME";
582         }
583     }
584 }
585