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