Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2006 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.Nullable;
     22 import android.app.ActivityThread;
     23 import android.content.ContentProvider;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.res.AssetFileDescriptor;
     27 import android.net.Uri;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.os.HandlerThread;
     31 import android.os.IBinder;
     32 import android.os.Looper;
     33 import android.os.Message;
     34 import android.os.Parcel;
     35 import android.os.Parcelable;
     36 import android.os.PersistableBundle;
     37 import android.os.Process;
     38 import android.os.PowerManager;
     39 import android.os.SystemProperties;
     40 import android.provider.Settings;
     41 import android.system.ErrnoException;
     42 import android.system.OsConstants;
     43 import android.util.Log;
     44 import android.util.Pair;
     45 import android.view.Surface;
     46 import android.view.SurfaceHolder;
     47 import android.widget.VideoView;
     48 import android.graphics.SurfaceTexture;
     49 import android.media.AudioManager;
     50 import android.media.MediaDrm;
     51 import android.media.MediaFormat;
     52 import android.media.MediaTimeProvider;
     53 import android.media.PlaybackParams;
     54 import android.media.SubtitleController;
     55 import android.media.SubtitleController.Anchor;
     56 import android.media.SubtitleData;
     57 import android.media.SubtitleTrack.RenderingWidget;
     58 import android.media.SyncParams;
     59 
     60 import com.android.internal.util.Preconditions;
     61 
     62 import libcore.io.IoBridge;
     63 import libcore.io.Libcore;
     64 import libcore.io.Streams;
     65 
     66 import java.io.ByteArrayOutputStream;
     67 import java.io.File;
     68 import java.io.FileDescriptor;
     69 import java.io.FileInputStream;
     70 import java.io.IOException;
     71 import java.io.InputStream;
     72 import java.lang.Runnable;
     73 import java.lang.annotation.Retention;
     74 import java.lang.annotation.RetentionPolicy;
     75 import java.lang.ref.WeakReference;
     76 import java.net.HttpCookie;
     77 import java.net.HttpURLConnection;
     78 import java.net.InetSocketAddress;
     79 import java.net.URL;
     80 import java.nio.ByteOrder;
     81 import java.util.Arrays;
     82 import java.util.BitSet;
     83 import java.util.HashMap;
     84 import java.util.List;
     85 import java.util.Map;
     86 import java.util.Scanner;
     87 import java.util.Set;
     88 import java.util.UUID;
     89 import java.util.Vector;
     90 
     91 
     92 /**
     93  * MediaPlayer class can be used to control playback
     94  * of audio/video files and streams. An example on how to use the methods in
     95  * this class can be found in {@link android.widget.VideoView}.
     96  *
     97  * <p>Topics covered here are:
     98  * <ol>
     99  * <li><a href="#StateDiagram">State Diagram</a>
    100  * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
    101  * <li><a href="#Permissions">Permissions</a>
    102  * <li><a href="#Callbacks">Register informational and error callbacks</a>
    103  * </ol>
    104  *
    105  * <div class="special reference">
    106  * <h3>Developer Guides</h3>
    107  * <p>For more information about how to use MediaPlayer, read the
    108  * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
    109  * </div>
    110  *
    111  * <a name="StateDiagram"></a>
    112  * <h3>State Diagram</h3>
    113  *
    114  * <p>Playback control of audio/video files and streams is managed as a state
    115  * machine. The following diagram shows the life cycle and the states of a
    116  * MediaPlayer object driven by the supported playback control operations.
    117  * The ovals represent the states a MediaPlayer object may reside
    118  * in. The arcs represent the playback control operations that drive the object
    119  * state transition. There are two types of arcs. The arcs with a single arrow
    120  * head represent synchronous method calls, while those with
    121  * a double arrow head represent asynchronous method calls.</p>
    122  *
    123  * <p><img src="../../../images/mediaplayer_state_diagram.gif"
    124  *         alt="MediaPlayer State diagram"
    125  *         border="0" /></p>
    126  *
    127  * <p>From this state diagram, one can see that a MediaPlayer object has the
    128  *    following states:</p>
    129  * <ul>
    130  *     <li>When a MediaPlayer object is just created using <code>new</code> or
    131  *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
    132  *         {@link #release()} is called, it is in the <em>End</em> state. Between these
    133  *         two states is the life cycle of the MediaPlayer object.
    134  *         <ul>
    135  *         <li>There is a subtle but important difference between a newly constructed
    136  *         MediaPlayer object and the MediaPlayer object after {@link #reset()}
    137  *         is called. It is a programming error to invoke methods such
    138  *         as {@link #getCurrentPosition()},
    139  *         {@link #getDuration()}, {@link #getVideoHeight()},
    140  *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
    141  *         {@link #setLooping(boolean)},
    142  *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()},
    143  *         {@link #stop()}, {@link #seekTo(long, int)}, {@link #prepare()} or
    144  *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
    145  *         methods is called right after a MediaPlayer object is constructed,
    146  *         the user supplied callback method OnErrorListener.onError() won't be
    147  *         called by the internal player engine and the object state remains
    148  *         unchanged; but if these methods are called right after {@link #reset()},
    149  *         the user supplied callback method OnErrorListener.onError() will be
    150  *         invoked by the internal player engine and the object will be
    151  *         transfered to the <em>Error</em> state. </li>
    152  *         <li>It is also recommended that once
    153  *         a MediaPlayer object is no longer being used, call {@link #release()} immediately
    154  *         so that resources used by the internal player engine associated with the
    155  *         MediaPlayer object can be released immediately. Resource may include
    156  *         singleton resources such as hardware acceleration components and
    157  *         failure to call {@link #release()} may cause subsequent instances of
    158  *         MediaPlayer objects to fallback to software implementations or fail
    159  *         altogether. Once the MediaPlayer
    160  *         object is in the <em>End</em> state, it can no longer be used and
    161  *         there is no way to bring it back to any other state. </li>
    162  *         <li>Furthermore,
    163  *         the MediaPlayer objects created using <code>new</code> is in the
    164  *         <em>Idle</em> state, while those created with one
    165  *         of the overloaded convenient <code>create</code> methods are <em>NOT</em>
    166  *         in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em>
    167  *         state if the creation using <code>create</code> method is successful.
    168  *         </li>
    169  *         </ul>
    170  *         </li>
    171  *     <li>In general, some playback control operation may fail due to various
    172  *         reasons, such as unsupported audio/video format, poorly interleaved
    173  *         audio/video, resolution too high, streaming timeout, and the like.
    174  *         Thus, error reporting and recovery is an important concern under
    175  *         these circumstances. Sometimes, due to programming errors, invoking a playback
    176  *         control operation in an invalid state may also occur. Under all these
    177  *         error conditions, the internal player engine invokes a user supplied
    178  *         OnErrorListener.onError() method if an OnErrorListener has been
    179  *         registered beforehand via
    180  *         {@link #setOnErrorListener(android.media.MediaPlayer.OnErrorListener)}.
    181  *         <ul>
    182  *         <li>It is important to note that once an error occurs, the
    183  *         MediaPlayer object enters the <em>Error</em> state (except as noted
    184  *         above), even if an error listener has not been registered by the application.</li>
    185  *         <li>In order to reuse a MediaPlayer object that is in the <em>
    186  *         Error</em> state and recover from the error,
    187  *         {@link #reset()} can be called to restore the object to its <em>Idle</em>
    188  *         state.</li>
    189  *         <li>It is good programming practice to have your application
    190  *         register a OnErrorListener to look out for error notifications from
    191  *         the internal player engine.</li>
    192  *         <li>IllegalStateException is
    193  *         thrown to prevent programming errors such as calling {@link #prepare()},
    194  *         {@link #prepareAsync()}, or one of the overloaded <code>setDataSource
    195  *         </code> methods in an invalid state. </li>
    196  *         </ul>
    197  *         </li>
    198  *     <li>Calling
    199  *         {@link #setDataSource(FileDescriptor)}, or
    200  *         {@link #setDataSource(String)}, or
    201  *         {@link #setDataSource(Context, Uri)}, or
    202  *         {@link #setDataSource(FileDescriptor, long, long)}, or
    203  *         {@link #setDataSource(MediaDataSource)} transfers a
    204  *         MediaPlayer object in the <em>Idle</em> state to the
    205  *         <em>Initialized</em> state.
    206  *         <ul>
    207  *         <li>An IllegalStateException is thrown if
    208  *         setDataSource() is called in any other state.</li>
    209  *         <li>It is good programming
    210  *         practice to always look out for <code>IllegalArgumentException</code>
    211  *         and <code>IOException</code> that may be thrown from the overloaded
    212  *         <code>setDataSource</code> methods.</li>
    213  *         </ul>
    214  *         </li>
    215  *     <li>A MediaPlayer object must first enter the <em>Prepared</em> state
    216  *         before playback can be started.
    217  *         <ul>
    218  *         <li>There are two ways (synchronous vs.
    219  *         asynchronous) that the <em>Prepared</em> state can be reached:
    220  *         either a call to {@link #prepare()} (synchronous) which
    221  *         transfers the object to the <em>Prepared</em> state once the method call
    222  *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
    223  *         first transfers the object to the <em>Preparing</em> state after the
    224  *         call returns (which occurs almost right way) while the internal
    225  *         player engine continues working on the rest of preparation work
    226  *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
    227  *         the internal player engine then calls a user supplied callback method,
    228  *         onPrepared() of the OnPreparedListener interface, if an
    229  *         OnPreparedListener is registered beforehand via {@link
    230  *         #setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)}.</li>
    231  *         <li>It is important to note that
    232  *         the <em>Preparing</em> state is a transient state, and the behavior
    233  *         of calling any method with side effect while a MediaPlayer object is
    234  *         in the <em>Preparing</em> state is undefined.</li>
    235  *         <li>An IllegalStateException is
    236  *         thrown if {@link #prepare()} or {@link #prepareAsync()} is called in
    237  *         any other state.</li>
    238  *         <li>While in the <em>Prepared</em> state, properties
    239  *         such as audio/sound volume, screenOnWhilePlaying, looping can be
    240  *         adjusted by invoking the corresponding set methods.</li>
    241  *         </ul>
    242  *         </li>
    243  *     <li>To start the playback, {@link #start()} must be called. After
    244  *         {@link #start()} returns successfully, the MediaPlayer object is in the
    245  *         <em>Started</em> state. {@link #isPlaying()} can be called to test
    246  *         whether the MediaPlayer object is in the <em>Started</em> state.
    247  *         <ul>
    248  *         <li>While in the <em>Started</em> state, the internal player engine calls
    249  *         a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback
    250  *         method if a OnBufferingUpdateListener has been registered beforehand
    251  *         via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}.
    252  *         This callback allows applications to keep track of the buffering status
    253  *         while streaming audio/video.</li>
    254  *         <li>Calling {@link #start()} has not effect
    255  *         on a MediaPlayer object that is already in the <em>Started</em> state.</li>
    256  *         </ul>
    257  *         </li>
    258  *     <li>Playback can be paused and stopped, and the current playback position
    259  *         can be adjusted. Playback can be paused via {@link #pause()}. When the call to
    260  *         {@link #pause()} returns, the MediaPlayer object enters the
    261  *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
    262  *         state to the <em>Paused</em> state and vice versa happens
    263  *         asynchronously in the player engine. It may take some time before
    264  *         the state is updated in calls to {@link #isPlaying()}, and it can be
    265  *         a number of seconds in the case of streamed content.
    266  *         <ul>
    267  *         <li>Calling {@link #start()} to resume playback for a paused
    268  *         MediaPlayer object, and the resumed playback
    269  *         position is the same as where it was paused. When the call to
    270  *         {@link #start()} returns, the paused MediaPlayer object goes back to
    271  *         the <em>Started</em> state.</li>
    272  *         <li>Calling {@link #pause()} has no effect on
    273  *         a MediaPlayer object that is already in the <em>Paused</em> state.</li>
    274  *         </ul>
    275  *         </li>
    276  *     <li>Calling  {@link #stop()} stops playback and causes a
    277  *         MediaPlayer in the <em>Started</em>, <em>Paused</em>, <em>Prepared
    278  *         </em> or <em>PlaybackCompleted</em> state to enter the
    279  *         <em>Stopped</em> state.
    280  *         <ul>
    281  *         <li>Once in the <em>Stopped</em> state, playback cannot be started
    282  *         until {@link #prepare()} or {@link #prepareAsync()} are called to set
    283  *         the MediaPlayer object to the <em>Prepared</em> state again.</li>
    284  *         <li>Calling {@link #stop()} has no effect on a MediaPlayer
    285  *         object that is already in the <em>Stopped</em> state.</li>
    286  *         </ul>
    287  *         </li>
    288  *     <li>The playback position can be adjusted with a call to
    289  *         {@link #seekTo(long, int)}.
    290  *         <ul>
    291  *         <li>Although the asynchronuous {@link #seekTo(long, int)}
    292  *         call returns right away, the actual seek operation may take a while to
    293  *         finish, especially for audio/video being streamed. When the actual
    294  *         seek operation completes, the internal player engine calls a user
    295  *         supplied OnSeekComplete.onSeekComplete() if an OnSeekCompleteListener
    296  *         has been registered beforehand via
    297  *         {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}.</li>
    298  *         <li>Please
    299  *         note that {@link #seekTo(long, int)} can also be called in the other states,
    300  *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
    301  *         </em> state. When {@link #seekTo(long, int)} is called in those states,
    302  *         one video frame will be displayed if the stream has video and the requested
    303  *         position is valid.
    304  *         </li>
    305  *         <li>Furthermore, the actual current playback position
    306  *         can be retrieved with a call to {@link #getCurrentPosition()}, which
    307  *         is helpful for applications such as a Music player that need to keep
    308  *         track of the playback progress.</li>
    309  *         </ul>
    310  *         </li>
    311  *     <li>When the playback reaches the end of stream, the playback completes.
    312  *         <ul>
    313  *         <li>If the looping mode was being set to <var>true</var>with
    314  *         {@link #setLooping(boolean)}, the MediaPlayer object shall remain in
    315  *         the <em>Started</em> state.</li>
    316  *         <li>If the looping mode was set to <var>false
    317  *         </var>, the player engine calls a user supplied callback method,
    318  *         OnCompletion.onCompletion(), if a OnCompletionListener is registered
    319  *         beforehand via {@link #setOnCompletionListener(OnCompletionListener)}.
    320  *         The invoke of the callback signals that the object is now in the <em>
    321  *         PlaybackCompleted</em> state.</li>
    322  *         <li>While in the <em>PlaybackCompleted</em>
    323  *         state, calling {@link #start()} can restart the playback from the
    324  *         beginning of the audio/video source.</li>
    325  * </ul>
    326  *
    327  *
    328  * <a name="Valid_and_Invalid_States"></a>
    329  * <h3>Valid and invalid states</h3>
    330  *
    331  * <table border="0" cellspacing="0" cellpadding="0">
    332  * <tr><td>Method Name </p></td>
    333  *     <td>Valid Sates </p></td>
    334  *     <td>Invalid States </p></td>
    335  *     <td>Comments </p></td></tr>
    336  * <tr><td>attachAuxEffect </p></td>
    337  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    338  *     <td>{Idle, Error} </p></td>
    339  *     <td>This method must be called after setDataSource.
    340  *     Calling it does not change the object state. </p></td></tr>
    341  * <tr><td>getAudioSessionId </p></td>
    342  *     <td>any </p></td>
    343  *     <td>{} </p></td>
    344  *     <td>This method can be called in any state and calling it does not change
    345  *         the object state. </p></td></tr>
    346  * <tr><td>getCurrentPosition </p></td>
    347  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    348  *         PlaybackCompleted} </p></td>
    349  *     <td>{Error}</p></td>
    350  *     <td>Successful invoke of this method in a valid state does not change the
    351  *         state. Calling this method in an invalid state transfers the object
    352  *         to the <em>Error</em> state. </p></td></tr>
    353  * <tr><td>getDuration </p></td>
    354  *     <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    355  *     <td>{Idle, Initialized, Error} </p></td>
    356  *     <td>Successful invoke of this method in a valid state does not change the
    357  *         state. Calling this method in an invalid state transfers the object
    358  *         to the <em>Error</em> state. </p></td></tr>
    359  * <tr><td>getVideoHeight </p></td>
    360  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    361  *         PlaybackCompleted}</p></td>
    362  *     <td>{Error}</p></td>
    363  *     <td>Successful invoke of this method in a valid state does not change the
    364  *         state. Calling this method in an invalid state transfers the object
    365  *         to the <em>Error</em> state.  </p></td></tr>
    366  * <tr><td>getVideoWidth </p></td>
    367  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    368  *         PlaybackCompleted}</p></td>
    369  *     <td>{Error}</p></td>
    370  *     <td>Successful invoke of this method in a valid state does not change
    371  *         the state. Calling this method in an invalid state transfers the
    372  *         object to the <em>Error</em> state. </p></td></tr>
    373  * <tr><td>isPlaying </p></td>
    374  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    375  *          PlaybackCompleted}</p></td>
    376  *     <td>{Error}</p></td>
    377  *     <td>Successful invoke of this method in a valid state does not change
    378  *         the state. Calling this method in an invalid state transfers the
    379  *         object to the <em>Error</em> state. </p></td></tr>
    380  * <tr><td>pause </p></td>
    381  *     <td>{Started, Paused, PlaybackCompleted}</p></td>
    382  *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
    383  *     <td>Successful invoke of this method in a valid state transfers the
    384  *         object to the <em>Paused</em> state. Calling this method in an
    385  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    386  * <tr><td>prepare </p></td>
    387  *     <td>{Initialized, Stopped} </p></td>
    388  *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
    389  *     <td>Successful invoke of this method in a valid state transfers the
    390  *         object to the <em>Prepared</em> state. Calling this method in an
    391  *         invalid state throws an IllegalStateException.</p></td></tr>
    392  * <tr><td>prepareAsync </p></td>
    393  *     <td>{Initialized, Stopped} </p></td>
    394  *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
    395  *     <td>Successful invoke of this method in a valid state transfers the
    396  *         object to the <em>Preparing</em> state. Calling this method in an
    397  *         invalid state throws an IllegalStateException.</p></td></tr>
    398  * <tr><td>release </p></td>
    399  *     <td>any </p></td>
    400  *     <td>{} </p></td>
    401  *     <td>After {@link #release()}, the object is no longer available. </p></td></tr>
    402  * <tr><td>reset </p></td>
    403  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    404  *         PlaybackCompleted, Error}</p></td>
    405  *     <td>{}</p></td>
    406  *     <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
    407  * <tr><td>seekTo </p></td>
    408  *     <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
    409  *     <td>{Idle, Initialized, Stopped, Error}</p></td>
    410  *     <td>Successful invoke of this method in a valid state does not change
    411  *         the state. Calling this method in an invalid state transfers the
    412  *         object to the <em>Error</em> state. </p></td></tr>
    413  * <tr><td>setAudioAttributes </p></td>
    414  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    415  *          PlaybackCompleted}</p></td>
    416  *     <td>{Error}</p></td>
    417  *     <td>Successful invoke of this method does not change the state. In order for the
    418  *         target audio attributes type to become effective, this method must be called before
    419  *         prepare() or prepareAsync().</p></td></tr>
    420  * <tr><td>setAudioSessionId </p></td>
    421  *     <td>{Idle} </p></td>
    422  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
    423  *          Error} </p></td>
    424  *     <td>This method must be called in idle state as the audio session ID must be known before
    425  *         calling setDataSource. Calling it does not change the object state. </p></td></tr>
    426  * <tr><td>setAudioStreamType (deprecated)</p></td>
    427  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    428  *          PlaybackCompleted}</p></td>
    429  *     <td>{Error}</p></td>
    430  *     <td>Successful invoke of this method does not change the state. In order for the
    431  *         target audio stream type to become effective, this method must be called before
    432  *         prepare() or prepareAsync().</p></td></tr>
    433  * <tr><td>setAuxEffectSendLevel </p></td>
    434  *     <td>any</p></td>
    435  *     <td>{} </p></td>
    436  *     <td>Calling this method does not change the object state. </p></td></tr>
    437  * <tr><td>setDataSource </p></td>
    438  *     <td>{Idle} </p></td>
    439  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
    440  *          Error} </p></td>
    441  *     <td>Successful invoke of this method in a valid state transfers the
    442  *         object to the <em>Initialized</em> state. Calling this method in an
    443  *         invalid state throws an IllegalStateException.</p></td></tr>
    444  * <tr><td>setDisplay </p></td>
    445  *     <td>any </p></td>
    446  *     <td>{} </p></td>
    447  *     <td>This method can be called in any state and calling it does not change
    448  *         the object state. </p></td></tr>
    449  * <tr><td>setSurface </p></td>
    450  *     <td>any </p></td>
    451  *     <td>{} </p></td>
    452  *     <td>This method can be called in any state and calling it does not change
    453  *         the object state. </p></td></tr>
    454  * <tr><td>setVideoScalingMode </p></td>
    455  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    456  *     <td>{Idle, Error}</p></td>
    457  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    458  * <tr><td>setLooping </p></td>
    459  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    460  *         PlaybackCompleted}</p></td>
    461  *     <td>{Error}</p></td>
    462  *     <td>Successful invoke of this method in a valid state does not change
    463  *         the state. Calling this method in an
    464  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    465  * <tr><td>isLooping </p></td>
    466  *     <td>any </p></td>
    467  *     <td>{} </p></td>
    468  *     <td>This method can be called in any state and calling it does not change
    469  *         the object state. </p></td></tr>
    470  * <tr><td>setOnBufferingUpdateListener </p></td>
    471  *     <td>any </p></td>
    472  *     <td>{} </p></td>
    473  *     <td>This method can be called in any state and calling it does not change
    474  *         the object state. </p></td></tr>
    475  * <tr><td>setOnCompletionListener </p></td>
    476  *     <td>any </p></td>
    477  *     <td>{} </p></td>
    478  *     <td>This method can be called in any state and calling it does not change
    479  *         the object state. </p></td></tr>
    480  * <tr><td>setOnErrorListener </p></td>
    481  *     <td>any </p></td>
    482  *     <td>{} </p></td>
    483  *     <td>This method can be called in any state and calling it does not change
    484  *         the object state. </p></td></tr>
    485  * <tr><td>setOnPreparedListener </p></td>
    486  *     <td>any </p></td>
    487  *     <td>{} </p></td>
    488  *     <td>This method can be called in any state and calling it does not change
    489  *         the object state. </p></td></tr>
    490  * <tr><td>setOnSeekCompleteListener </p></td>
    491  *     <td>any </p></td>
    492  *     <td>{} </p></td>
    493  *     <td>This method can be called in any state and calling it does not change
    494  *         the object state. </p></td></tr>
    495  * <tr><td>setPlaybackParams</p></td>
    496  *     <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
    497  *     <td>{Idle, Stopped} </p></td>
    498  *     <td>This method will change state in some cases, depending on when it's called.
    499  *         </p></td></tr>
    500  * <tr><td>setScreenOnWhilePlaying</></td>
    501  *     <td>any </p></td>
    502  *     <td>{} </p></td>
    503  *     <td>This method can be called in any state and calling it does not change
    504  *         the object state.  </p></td></tr>
    505  * <tr><td>setVolume </p></td>
    506  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    507  *          PlaybackCompleted}</p></td>
    508  *     <td>{Error}</p></td>
    509  *     <td>Successful invoke of this method does not change the state.
    510  * <tr><td>setWakeMode </p></td>
    511  *     <td>any </p></td>
    512  *     <td>{} </p></td>
    513  *     <td>This method can be called in any state and calling it does not change
    514  *         the object state.</p></td></tr>
    515  * <tr><td>start </p></td>
    516  *     <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
    517  *     <td>{Idle, Initialized, Stopped, Error}</p></td>
    518  *     <td>Successful invoke of this method in a valid state transfers the
    519  *         object to the <em>Started</em> state. Calling this method in an
    520  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    521  * <tr><td>stop </p></td>
    522  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    523  *     <td>{Idle, Initialized, Error}</p></td>
    524  *     <td>Successful invoke of this method in a valid state transfers the
    525  *         object to the <em>Stopped</em> state. Calling this method in an
    526  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    527  * <tr><td>getTrackInfo </p></td>
    528  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    529  *     <td>{Idle, Initialized, Error}</p></td>
    530  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    531  * <tr><td>addTimedTextSource </p></td>
    532  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    533  *     <td>{Idle, Initialized, Error}</p></td>
    534  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    535  * <tr><td>selectTrack </p></td>
    536  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    537  *     <td>{Idle, Initialized, Error}</p></td>
    538  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    539  * <tr><td>deselectTrack </p></td>
    540  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    541  *     <td>{Idle, Initialized, Error}</p></td>
    542  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    543  *
    544  * </table>
    545  *
    546  * <a name="Permissions"></a>
    547  * <h3>Permissions</h3>
    548  * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
    549  * android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
    550  * element.
    551  *
    552  * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
    553  * when used with network-based content.
    554  *
    555  * <a name="Callbacks"></a>
    556  * <h3>Callbacks</h3>
    557  * <p>Applications may want to register for informational and error
    558  * events in order to be informed of some internal state update and
    559  * possible runtime errors during playback or streaming. Registration for
    560  * these events is done by properly setting the appropriate listeners (via calls
    561  * to
    562  * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener,
    563  * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener,
    564  * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener,
    565  * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener,
    566  * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener,
    567  * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener,
    568  * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc).
    569  * In order to receive the respective callback
    570  * associated with these listeners, applications are required to create
    571  * MediaPlayer objects on a thread with its own Looper running (main UI
    572  * thread by default has a Looper running).
    573  *
    574  */
    575 public class MediaPlayer extends PlayerBase
    576                          implements SubtitleController.Listener
    577                                   , VolumeAutomation
    578 {
    579     /**
    580        Constant to retrieve only the new metadata since the last
    581        call.
    582        // FIXME: unhide.
    583        // FIXME: add link to getMetadata(boolean, boolean)
    584        {@hide}
    585      */
    586     public static final boolean METADATA_UPDATE_ONLY = true;
    587 
    588     /**
    589        Constant to retrieve all the metadata.
    590        // FIXME: unhide.
    591        // FIXME: add link to getMetadata(boolean, boolean)
    592        {@hide}
    593      */
    594     public static final boolean METADATA_ALL = false;
    595 
    596     /**
    597        Constant to enable the metadata filter during retrieval.
    598        // FIXME: unhide.
    599        // FIXME: add link to getMetadata(boolean, boolean)
    600        {@hide}
    601      */
    602     public static final boolean APPLY_METADATA_FILTER = true;
    603 
    604     /**
    605        Constant to disable the metadata filter during retrieval.
    606        // FIXME: unhide.
    607        // FIXME: add link to getMetadata(boolean, boolean)
    608        {@hide}
    609      */
    610     public static final boolean BYPASS_METADATA_FILTER = false;
    611 
    612     static {
    613         System.loadLibrary("media_jni");
    614         native_init();
    615     }
    616 
    617     private final static String TAG = "MediaPlayer";
    618     // Name of the remote interface for the media player. Must be kept
    619     // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE
    620     // macro invocation in IMediaPlayer.cpp
    621     private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";
    622 
    623     private long mNativeContext; // accessed by native methods
    624     private long mNativeSurfaceTexture;  // accessed by native methods
    625     private int mListenerContext; // accessed by native methods
    626     private SurfaceHolder mSurfaceHolder;
    627     private EventHandler mEventHandler;
    628     private PowerManager.WakeLock mWakeLock = null;
    629     private boolean mScreenOnWhilePlaying;
    630     private boolean mStayAwake;
    631     private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
    632     private int mUsage = -1;
    633     private boolean mBypassInterruptionPolicy;
    634 
    635     // Modular DRM
    636     private UUID mDrmUUID;
    637     private final Object mDrmLock = new Object();
    638     private DrmInfo mDrmInfo;
    639     private MediaDrm mDrmObj;
    640     private byte[] mDrmSessionId;
    641     private boolean mDrmInfoResolved;
    642     private boolean mActiveDrmScheme;
    643     private boolean mDrmConfigAllowed;
    644     private boolean mDrmProvisioningInProgress;
    645     private boolean mPrepareDrmInProgress;
    646     private ProvisioningThread mDrmProvisioningThread;
    647 
    648     /**
    649      * Default constructor. Consider using one of the create() methods for
    650      * synchronously instantiating a MediaPlayer from a Uri or resource.
    651      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    652      * to free the resources. If not released, too many MediaPlayer instances may
    653      * result in an exception.</p>
    654      */
    655     public MediaPlayer() {
    656         super(new AudioAttributes.Builder().build(),
    657                 AudioPlaybackConfiguration.PLAYER_TYPE_JAM_MEDIAPLAYER);
    658 
    659         Looper looper;
    660         if ((looper = Looper.myLooper()) != null) {
    661             mEventHandler = new EventHandler(this, looper);
    662         } else if ((looper = Looper.getMainLooper()) != null) {
    663             mEventHandler = new EventHandler(this, looper);
    664         } else {
    665             mEventHandler = null;
    666         }
    667 
    668         mTimeProvider = new TimeProvider(this);
    669         mOpenSubtitleSources = new Vector<InputStream>();
    670 
    671         /* Native setup requires a weak reference to our object.
    672          * It's easier to create it here than in C++.
    673          */
    674         native_setup(new WeakReference<MediaPlayer>(this));
    675 
    676         baseRegisterPlayer();
    677     }
    678 
    679     /*
    680      * Update the MediaPlayer SurfaceTexture.
    681      * Call after setting a new display surface.
    682      */
    683     private native void _setVideoSurface(Surface surface);
    684 
    685     /* Do not change these values (starting with INVOKE_ID) without updating
    686      * their counterparts in include/media/mediaplayer.h!
    687      */
    688     private static final int INVOKE_ID_GET_TRACK_INFO = 1;
    689     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
    690     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
    691     private static final int INVOKE_ID_SELECT_TRACK = 4;
    692     private static final int INVOKE_ID_DESELECT_TRACK = 5;
    693     private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
    694     private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
    695 
    696     /**
    697      * Create a request parcel which can be routed to the native media
    698      * player using {@link #invoke(Parcel, Parcel)}. The Parcel
    699      * returned has the proper InterfaceToken set. The caller should
    700      * not overwrite that token, i.e it can only append data to the
    701      * Parcel.
    702      *
    703      * @return A parcel suitable to hold a request for the native
    704      * player.
    705      * {@hide}
    706      */
    707     public Parcel newRequest() {
    708         Parcel parcel = Parcel.obtain();
    709         parcel.writeInterfaceToken(IMEDIA_PLAYER);
    710         return parcel;
    711     }
    712 
    713     /**
    714      * Invoke a generic method on the native player using opaque
    715      * parcels for the request and reply. Both payloads' format is a
    716      * convention between the java caller and the native player.
    717      * Must be called after setDataSource to make sure a native player
    718      * exists. On failure, a RuntimeException is thrown.
    719      *
    720      * @param request Parcel with the data for the extension. The
    721      * caller must use {@link #newRequest()} to get one.
    722      *
    723      * @param reply Output parcel with the data returned by the
    724      * native player.
    725      * {@hide}
    726      */
    727     public void invoke(Parcel request, Parcel reply) {
    728         int retcode = native_invoke(request, reply);
    729         reply.setDataPosition(0);
    730         if (retcode != 0) {
    731             throw new RuntimeException("failure code: " + retcode);
    732         }
    733     }
    734 
    735     /**
    736      * Sets the {@link SurfaceHolder} to use for displaying the video
    737      * portion of the media.
    738      *
    739      * Either a surface holder or surface must be set if a display or video sink
    740      * is needed.  Not calling this method or {@link #setSurface(Surface)}
    741      * when playing back a video will result in only the audio track being played.
    742      * A null surface holder or surface will result in only the audio track being
    743      * played.
    744      *
    745      * @param sh the SurfaceHolder to use for video display
    746      * @throws IllegalStateException if the internal player engine has not been
    747      * initialized or has been released.
    748      */
    749     public void setDisplay(SurfaceHolder sh) {
    750         mSurfaceHolder = sh;
    751         Surface surface;
    752         if (sh != null) {
    753             surface = sh.getSurface();
    754         } else {
    755             surface = null;
    756         }
    757         _setVideoSurface(surface);
    758         updateSurfaceScreenOn();
    759     }
    760 
    761     /**
    762      * Sets the {@link Surface} to be used as the sink for the video portion of
    763      * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
    764      * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
    765      * Surface will un-set any Surface or SurfaceHolder that was previously set.
    766      * A null surface will result in only the audio track being played.
    767      *
    768      * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
    769      * returned from {@link SurfaceTexture#getTimestamp()} will have an
    770      * unspecified zero point.  These timestamps cannot be directly compared
    771      * between different media sources, different instances of the same media
    772      * source, or multiple runs of the same program.  The timestamp is normally
    773      * monotonically increasing and is unaffected by time-of-day adjustments,
    774      * but it is reset when the position is set.
    775      *
    776      * @param surface The {@link Surface} to be used for the video portion of
    777      * the media.
    778      * @throws IllegalStateException if the internal player engine has not been
    779      * initialized or has been released.
    780      */
    781     public void setSurface(Surface surface) {
    782         if (mScreenOnWhilePlaying && surface != null) {
    783             Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
    784         }
    785         mSurfaceHolder = null;
    786         _setVideoSurface(surface);
    787         updateSurfaceScreenOn();
    788     }
    789 
    790     /* Do not change these video scaling mode values below without updating
    791      * their counterparts in system/window.h! Please do not forget to update
    792      * {@link #isVideoScalingModeSupported} when new video scaling modes
    793      * are added.
    794      */
    795     /**
    796      * Specifies a video scaling mode. The content is stretched to the
    797      * surface rendering area. When the surface has the same aspect ratio
    798      * as the content, the aspect ratio of the content is maintained;
    799      * otherwise, the aspect ratio of the content is not maintained when video
    800      * is being rendered. Unlike {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING},
    801      * there is no content cropping with this video scaling mode.
    802      */
    803     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
    804 
    805     /**
    806      * Specifies a video scaling mode. The content is scaled, maintaining
    807      * its aspect ratio. The whole surface area is always used. When the
    808      * aspect ratio of the content is the same as the surface, no content
    809      * is cropped; otherwise, content is cropped to fit the surface.
    810      */
    811     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
    812     /**
    813      * Sets video scaling mode. To make the target video scaling mode
    814      * effective during playback, this method must be called after
    815      * data source is set. If not called, the default video
    816      * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
    817      *
    818      * <p> The supported video scaling modes are:
    819      * <ul>
    820      * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
    821      * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
    822      * </ul>
    823      *
    824      * @param mode target video scaling mode. Must be one of the supported
    825      * video scaling modes; otherwise, IllegalArgumentException will be thrown.
    826      *
    827      * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT
    828      * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
    829      */
    830     public void setVideoScalingMode(int mode) {
    831         if (!isVideoScalingModeSupported(mode)) {
    832             final String msg = "Scaling mode " + mode + " is not supported";
    833             throw new IllegalArgumentException(msg);
    834         }
    835         Parcel request = Parcel.obtain();
    836         Parcel reply = Parcel.obtain();
    837         try {
    838             request.writeInterfaceToken(IMEDIA_PLAYER);
    839             request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
    840             request.writeInt(mode);
    841             invoke(request, reply);
    842         } finally {
    843             request.recycle();
    844             reply.recycle();
    845         }
    846     }
    847 
    848     /**
    849      * Convenience method to create a MediaPlayer for a given Uri.
    850      * On success, {@link #prepare()} will already have been called and must not be called again.
    851      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    852      * to free the resources. If not released, too many MediaPlayer instances will
    853      * result in an exception.</p>
    854      * <p>Note that since {@link #prepare()} is called automatically in this method,
    855      * you cannot change the audio
    856      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    857      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    858      *
    859      * @param context the Context to use
    860      * @param uri the Uri from which to get the datasource
    861      * @return a MediaPlayer object, or null if creation failed
    862      */
    863     public static MediaPlayer create(Context context, Uri uri) {
    864         return create (context, uri, null);
    865     }
    866 
    867     /**
    868      * Convenience method to create a MediaPlayer for a given Uri.
    869      * On success, {@link #prepare()} will already have been called and must not be called again.
    870      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    871      * to free the resources. If not released, too many MediaPlayer instances will
    872      * result in an exception.</p>
    873      * <p>Note that since {@link #prepare()} is called automatically in this method,
    874      * you cannot change the audio
    875      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    876      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    877      *
    878      * @param context the Context to use
    879      * @param uri the Uri from which to get the datasource
    880      * @param holder the SurfaceHolder to use for displaying the video
    881      * @return a MediaPlayer object, or null if creation failed
    882      */
    883     public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) {
    884         int s = AudioSystem.newAudioSessionId();
    885         return create(context, uri, holder, null, s > 0 ? s : 0);
    886     }
    887 
    888     /**
    889      * Same factory method as {@link #create(Context, Uri, SurfaceHolder)} but that lets you specify
    890      * the audio attributes and session ID to be used by the new MediaPlayer instance.
    891      * @param context the Context to use
    892      * @param uri the Uri from which to get the datasource
    893      * @param holder the SurfaceHolder to use for displaying the video, may be null.
    894      * @param audioAttributes the {@link AudioAttributes} to be used by the media player.
    895      * @param audioSessionId the audio session ID to be used by the media player,
    896      *     see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
    897      * @return a MediaPlayer object, or null if creation failed
    898      */
    899     public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
    900             AudioAttributes audioAttributes, int audioSessionId) {
    901 
    902         try {
    903             MediaPlayer mp = new MediaPlayer();
    904             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    905                 new AudioAttributes.Builder().build();
    906             mp.setAudioAttributes(aa);
    907             mp.setAudioSessionId(audioSessionId);
    908             mp.setDataSource(context, uri);
    909             if (holder != null) {
    910                 mp.setDisplay(holder);
    911             }
    912             mp.prepare();
    913             return mp;
    914         } catch (IOException ex) {
    915             Log.d(TAG, "create failed:", ex);
    916             // fall through
    917         } catch (IllegalArgumentException ex) {
    918             Log.d(TAG, "create failed:", ex);
    919             // fall through
    920         } catch (SecurityException ex) {
    921             Log.d(TAG, "create failed:", ex);
    922             // fall through
    923         }
    924 
    925         return null;
    926     }
    927 
    928     // Note no convenience method to create a MediaPlayer with SurfaceTexture sink.
    929 
    930     /**
    931      * Convenience method to create a MediaPlayer for a given resource id.
    932      * On success, {@link #prepare()} will already have been called and must not be called again.
    933      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    934      * to free the resources. If not released, too many MediaPlayer instances will
    935      * result in an exception.</p>
    936      * <p>Note that since {@link #prepare()} is called automatically in this method,
    937      * you cannot change the audio
    938      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    939      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    940      *
    941      * @param context the Context to use
    942      * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
    943      *              the resource to use as the datasource
    944      * @return a MediaPlayer object, or null if creation failed
    945      */
    946     public static MediaPlayer create(Context context, int resid) {
    947         int s = AudioSystem.newAudioSessionId();
    948         return create(context, resid, null, s > 0 ? s : 0);
    949     }
    950 
    951     /**
    952      * Same factory method as {@link #create(Context, int)} but that lets you specify the audio
    953      * attributes and session ID to be used by the new MediaPlayer instance.
    954      * @param context the Context to use
    955      * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
    956      *              the resource to use as the datasource
    957      * @param audioAttributes the {@link AudioAttributes} to be used by the media player.
    958      * @param audioSessionId the audio session ID to be used by the media player,
    959      *     see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
    960      * @return a MediaPlayer object, or null if creation failed
    961      */
    962     public static MediaPlayer create(Context context, int resid,
    963             AudioAttributes audioAttributes, int audioSessionId) {
    964         try {
    965             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
    966             if (afd == null) return null;
    967 
    968             MediaPlayer mp = new MediaPlayer();
    969 
    970             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    971                 new AudioAttributes.Builder().build();
    972             mp.setAudioAttributes(aa);
    973             mp.setAudioSessionId(audioSessionId);
    974 
    975             mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    976             afd.close();
    977             mp.prepare();
    978             return mp;
    979         } catch (IOException ex) {
    980             Log.d(TAG, "create failed:", ex);
    981             // fall through
    982         } catch (IllegalArgumentException ex) {
    983             Log.d(TAG, "create failed:", ex);
    984            // fall through
    985         } catch (SecurityException ex) {
    986             Log.d(TAG, "create failed:", ex);
    987             // fall through
    988         }
    989         return null;
    990     }
    991 
    992     /**
    993      * Sets the data source as a content Uri.
    994      *
    995      * @param context the Context to use when resolving the Uri
    996      * @param uri the Content URI of the data you want to play
    997      * @throws IllegalStateException if it is called in an invalid state
    998      */
    999     public void setDataSource(@NonNull Context context, @NonNull Uri uri)
   1000             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1001         setDataSource(context, uri, null, null);
   1002     }
   1003 
   1004     /**
   1005      * Sets the data source as a content Uri.
   1006      *
   1007      * @param context the Context to use when resolving the Uri
   1008      * @param uri the Content URI of the data you want to play
   1009      * @param headers the headers to be sent together with the request for the data
   1010      *                The headers must not include cookies. Instead, use the cookies param.
   1011      * @param cookies the cookies to be sent together with the request
   1012      * @throws IllegalStateException if it is called in an invalid state
   1013      * @throws NullPointerException  if context or uri is null
   1014      * @throws IOException           if uri has a file scheme and an I/O error occurs
   1015      *
   1016      * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
   1017      * but that can be changed with key/value pairs through the headers parameter with
   1018      * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
   1019      * disallow or allow cross domain redirection.
   1020      */
   1021     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
   1022             @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
   1023             throws IOException {
   1024         if (context == null) {
   1025             throw new NullPointerException("context param can not be null.");
   1026         }
   1027 
   1028         if (uri == null) {
   1029             throw new NullPointerException("uri param can not be null.");
   1030         }
   1031 
   1032         // The context and URI usually belong to the calling user. Get a resolver for that user
   1033         // and strip out the userId from the URI if present.
   1034         final ContentResolver resolver = context.getContentResolver();
   1035         final String scheme = uri.getScheme();
   1036         final String authority = ContentProvider.getAuthorityWithoutUserId(uri.getAuthority());
   1037         if (ContentResolver.SCHEME_FILE.equals(scheme)) {
   1038             setDataSource(uri.getPath());
   1039             return;
   1040         } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
   1041                 && Settings.AUTHORITY.equals(authority)) {
   1042             // Try cached ringtone first since the actual provider may not be
   1043             // encryption aware, or it may be stored on CE media storage
   1044             final int type = RingtoneManager.getDefaultType(uri);
   1045             final Uri cacheUri = RingtoneManager.getCacheForType(type, context.getUserId());
   1046             final Uri actualUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
   1047             if (attemptDataSource(resolver, cacheUri)) {
   1048                 return;
   1049             } else if (attemptDataSource(resolver, actualUri)) {
   1050                 return;
   1051             } else {
   1052                 setDataSource(uri.toString(), headers, cookies);
   1053             }
   1054         } else {
   1055             // Try requested Uri locally first, or fallback to media server
   1056             if (attemptDataSource(resolver, uri)) {
   1057                 return;
   1058             } else {
   1059                 setDataSource(uri.toString(), headers, cookies);
   1060             }
   1061         }
   1062     }
   1063 
   1064     /**
   1065      * Sets the data source as a content Uri.
   1066      *
   1067      * @param context the Context to use when resolving the Uri
   1068      * @param uri the Content URI of the data you want to play
   1069      * @param headers the headers to be sent together with the request for the data
   1070      * @throws IllegalStateException if it is called in an invalid state
   1071      *
   1072      * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
   1073      * but that can be changed with key/value pairs through the headers parameter with
   1074      * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
   1075      * disallow or allow cross domain redirection.
   1076      */
   1077     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
   1078             @Nullable Map<String, String> headers)
   1079             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1080         setDataSource(context, uri, headers, null);
   1081     }
   1082 
   1083     private boolean attemptDataSource(ContentResolver resolver, Uri uri) {
   1084         try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) {
   1085             setDataSource(afd);
   1086             return true;
   1087         } catch (NullPointerException | SecurityException | IOException ex) {
   1088             Log.w(TAG, "Couldn't open " + uri + ": " + ex);
   1089             return false;
   1090         }
   1091     }
   1092 
   1093     /**
   1094      * Sets the data source (file-path or http/rtsp URL) to use.
   1095      *
   1096      * @param path the path of the file, or the http/rtsp URL of the stream you want to play
   1097      * @throws IllegalStateException if it is called in an invalid state
   1098      *
   1099      * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
   1100      * process other than the calling application.  This implies that the pathname
   1101      * should be an absolute path (as any other process runs with unspecified current working
   1102      * directory), and that the pathname should reference a world-readable file.
   1103      * As an alternative, the application could first open the file for reading,
   1104      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
   1105      */
   1106     public void setDataSource(String path)
   1107             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1108         setDataSource(path, null, null);
   1109     }
   1110 
   1111     /**
   1112      * Sets the data source (file-path or http/rtsp URL) to use.
   1113      *
   1114      * @param path the path of the file, or the http/rtsp URL of the stream you want to play
   1115      * @param headers the headers associated with the http request for the stream you want to play
   1116      * @throws IllegalStateException if it is called in an invalid state
   1117      * @hide pending API council
   1118      */
   1119     public void setDataSource(String path, Map<String, String> headers)
   1120             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1121         setDataSource(path, headers, null);
   1122     }
   1123 
   1124     private void setDataSource(String path, Map<String, String> headers, List<HttpCookie> cookies)
   1125             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
   1126     {
   1127         String[] keys = null;
   1128         String[] values = null;
   1129 
   1130         if (headers != null) {
   1131             keys = new String[headers.size()];
   1132             values = new String[headers.size()];
   1133 
   1134             int i = 0;
   1135             for (Map.Entry<String, String> entry: headers.entrySet()) {
   1136                 keys[i] = entry.getKey();
   1137                 values[i] = entry.getValue();
   1138                 ++i;
   1139             }
   1140         }
   1141         setDataSource(path, keys, values, cookies);
   1142     }
   1143 
   1144     private void setDataSource(String path, String[] keys, String[] values,
   1145             List<HttpCookie> cookies)
   1146             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1147         final Uri uri = Uri.parse(path);
   1148         final String scheme = uri.getScheme();
   1149         if ("file".equals(scheme)) {
   1150             path = uri.getPath();
   1151         } else if (scheme != null) {
   1152             // handle non-file sources
   1153             nativeSetDataSource(
   1154                 MediaHTTPService.createHttpServiceBinderIfNecessary(path, cookies),
   1155                 path,
   1156                 keys,
   1157                 values);
   1158             return;
   1159         }
   1160 
   1161         final File file = new File(path);
   1162         if (file.exists()) {
   1163             FileInputStream is = new FileInputStream(file);
   1164             FileDescriptor fd = is.getFD();
   1165             setDataSource(fd);
   1166             is.close();
   1167         } else {
   1168             throw new IOException("setDataSource failed.");
   1169         }
   1170     }
   1171 
   1172     private native void nativeSetDataSource(
   1173         IBinder httpServiceBinder, String path, String[] keys, String[] values)
   1174         throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
   1175 
   1176     /**
   1177      * Sets the data source (AssetFileDescriptor) to use. It is the caller's
   1178      * responsibility to close the file descriptor. It is safe to do so as soon
   1179      * as this call returns.
   1180      *
   1181      * @param afd the AssetFileDescriptor for the file you want to play
   1182      * @throws IllegalStateException if it is called in an invalid state
   1183      * @throws IllegalArgumentException if afd is not a valid AssetFileDescriptor
   1184      * @throws IOException if afd can not be read
   1185      */
   1186     public void setDataSource(@NonNull AssetFileDescriptor afd)
   1187             throws IOException, IllegalArgumentException, IllegalStateException {
   1188         Preconditions.checkNotNull(afd);
   1189         // Note: using getDeclaredLength so that our behavior is the same
   1190         // as previous versions when the content provider is returning
   1191         // a full file.
   1192         if (afd.getDeclaredLength() < 0) {
   1193             setDataSource(afd.getFileDescriptor());
   1194         } else {
   1195             setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
   1196         }
   1197     }
   1198 
   1199     /**
   1200      * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
   1201      * to close the file descriptor. It is safe to do so as soon as this call returns.
   1202      *
   1203      * @param fd the FileDescriptor for the file you want to play
   1204      * @throws IllegalStateException if it is called in an invalid state
   1205      * @throws IllegalArgumentException if fd is not a valid FileDescriptor
   1206      * @throws IOException if fd can not be read
   1207      */
   1208     public void setDataSource(FileDescriptor fd)
   1209             throws IOException, IllegalArgumentException, IllegalStateException {
   1210         // intentionally less than LONG_MAX
   1211         setDataSource(fd, 0, 0x7ffffffffffffffL);
   1212     }
   1213 
   1214     /**
   1215      * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be
   1216      * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
   1217      * to close the file descriptor. It is safe to do so as soon as this call returns.
   1218      *
   1219      * @param fd the FileDescriptor for the file you want to play
   1220      * @param offset the offset into the file where the data to be played starts, in bytes
   1221      * @param length the length in bytes of the data to be played
   1222      * @throws IllegalStateException if it is called in an invalid state
   1223      * @throws IllegalArgumentException if fd is not a valid FileDescriptor
   1224      * @throws IOException if fd can not be read
   1225      */
   1226     public void setDataSource(FileDescriptor fd, long offset, long length)
   1227             throws IOException, IllegalArgumentException, IllegalStateException {
   1228         _setDataSource(fd, offset, length);
   1229     }
   1230 
   1231     private native void _setDataSource(FileDescriptor fd, long offset, long length)
   1232             throws IOException, IllegalArgumentException, IllegalStateException;
   1233 
   1234     /**
   1235      * Sets the data source (MediaDataSource) to use.
   1236      *
   1237      * @param dataSource the MediaDataSource for the media you want to play
   1238      * @throws IllegalStateException if it is called in an invalid state
   1239      * @throws IllegalArgumentException if dataSource is not a valid MediaDataSource
   1240      */
   1241     public void setDataSource(MediaDataSource dataSource)
   1242             throws IllegalArgumentException, IllegalStateException {
   1243         _setDataSource(dataSource);
   1244     }
   1245 
   1246     private native void _setDataSource(MediaDataSource dataSource)
   1247           throws IllegalArgumentException, IllegalStateException;
   1248 
   1249     /**
   1250      * Prepares the player for playback, synchronously.
   1251      *
   1252      * After setting the datasource and the display surface, you need to either
   1253      * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
   1254      * which blocks until MediaPlayer is ready for playback.
   1255      *
   1256      * @throws IllegalStateException if it is called in an invalid state
   1257      */
   1258     public void prepare() throws IOException, IllegalStateException {
   1259         _prepare();
   1260         scanInternalSubtitleTracks();
   1261 
   1262         // DrmInfo, if any, has been resolved by now.
   1263         synchronized (mDrmLock) {
   1264             mDrmInfoResolved = true;
   1265         }
   1266     }
   1267 
   1268     private native void _prepare() throws IOException, IllegalStateException;
   1269 
   1270     /**
   1271      * Prepares the player for playback, asynchronously.
   1272      *
   1273      * After setting the datasource and the display surface, you need to either
   1274      * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
   1275      * which returns immediately, rather than blocking until enough data has been
   1276      * buffered.
   1277      *
   1278      * @throws IllegalStateException if it is called in an invalid state
   1279      */
   1280     public native void prepareAsync() throws IllegalStateException;
   1281 
   1282     /**
   1283      * Starts or resumes playback. If playback had previously been paused,
   1284      * playback will continue from where it was paused. If playback had
   1285      * been stopped, or never started before, playback will start at the
   1286      * beginning.
   1287      *
   1288      * @throws IllegalStateException if it is called in an invalid state
   1289      */
   1290     public void start() throws IllegalStateException {
   1291         //FIXME use lambda to pass startImpl to superclass
   1292         final int delay = getStartDelayMs();
   1293         if (delay == 0) {
   1294             startImpl();
   1295         } else {
   1296             new Thread() {
   1297                 public void run() {
   1298                     try {
   1299                         Thread.sleep(delay);
   1300                     } catch (InterruptedException e) {
   1301                         e.printStackTrace();
   1302                     }
   1303                     baseSetStartDelayMs(0);
   1304                     try {
   1305                         startImpl();
   1306                     } catch (IllegalStateException e) {
   1307                         // fail silently for a state exception when it is happening after
   1308                         // a delayed start, as the player state could have changed between the
   1309                         // call to start() and the execution of startImpl()
   1310                     }
   1311                 }
   1312             }.start();
   1313         }
   1314     }
   1315 
   1316     private void startImpl() {
   1317         baseStart();
   1318         stayAwake(true);
   1319         _start();
   1320     }
   1321 
   1322     private native void _start() throws IllegalStateException;
   1323 
   1324 
   1325     private int getAudioStreamType() {
   1326         if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   1327             mStreamType = _getAudioStreamType();
   1328         }
   1329         return mStreamType;
   1330     }
   1331 
   1332     private native int _getAudioStreamType() throws IllegalStateException;
   1333 
   1334     /**
   1335      * Stops playback after playback has been stopped or paused.
   1336      *
   1337      * @throws IllegalStateException if the internal player engine has not been
   1338      * initialized.
   1339      */
   1340     public void stop() throws IllegalStateException {
   1341         stayAwake(false);
   1342         _stop();
   1343         baseStop();
   1344     }
   1345 
   1346     private native void _stop() throws IllegalStateException;
   1347 
   1348     /**
   1349      * Pauses playback. Call start() to resume.
   1350      *
   1351      * @throws IllegalStateException if the internal player engine has not been
   1352      * initialized.
   1353      */
   1354     public void pause() throws IllegalStateException {
   1355         stayAwake(false);
   1356         _pause();
   1357         basePause();
   1358     }
   1359 
   1360     private native void _pause() throws IllegalStateException;
   1361 
   1362     @Override
   1363     void playerStart() {
   1364         start();
   1365     }
   1366 
   1367     @Override
   1368     void playerPause() {
   1369         pause();
   1370     }
   1371 
   1372     @Override
   1373     void playerStop() {
   1374         stop();
   1375     }
   1376 
   1377     @Override
   1378     /* package */ int playerApplyVolumeShaper(
   1379             @NonNull VolumeShaper.Configuration configuration,
   1380             @NonNull VolumeShaper.Operation operation) {
   1381         return native_applyVolumeShaper(configuration, operation);
   1382     }
   1383 
   1384     @Override
   1385     /* package */ @Nullable VolumeShaper.State playerGetVolumeShaperState(int id) {
   1386         return native_getVolumeShaperState(id);
   1387     }
   1388 
   1389     @Override
   1390     public @NonNull VolumeShaper createVolumeShaper(
   1391             @NonNull VolumeShaper.Configuration configuration) {
   1392         return new VolumeShaper(configuration, this);
   1393     }
   1394 
   1395     private native int native_applyVolumeShaper(
   1396             @NonNull VolumeShaper.Configuration configuration,
   1397             @NonNull VolumeShaper.Operation operation);
   1398 
   1399     private native @Nullable VolumeShaper.State native_getVolumeShaperState(int id);
   1400 
   1401     /**
   1402      * Set the low-level power management behavior for this MediaPlayer.  This
   1403      * can be used when the MediaPlayer is not playing through a SurfaceHolder
   1404      * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
   1405      * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
   1406      *
   1407      * <p>This function has the MediaPlayer access the low-level power manager
   1408      * service to control the device's power usage while playing is occurring.
   1409      * The parameter is a combination of {@link android.os.PowerManager} wake flags.
   1410      * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
   1411      * permission.
   1412      * By default, no attempt is made to keep the device awake during playback.
   1413      *
   1414      * @param context the Context to use
   1415      * @param mode    the power/wake mode to set
   1416      * @see android.os.PowerManager
   1417      */
   1418     public void setWakeMode(Context context, int mode) {
   1419         boolean washeld = false;
   1420 
   1421         /* Disable persistant wakelocks in media player based on property */
   1422         if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
   1423             Log.w(TAG, "IGNORING setWakeMode " + mode);
   1424             return;
   1425         }
   1426 
   1427         if (mWakeLock != null) {
   1428             if (mWakeLock.isHeld()) {
   1429                 washeld = true;
   1430                 mWakeLock.release();
   1431             }
   1432             mWakeLock = null;
   1433         }
   1434 
   1435         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
   1436         mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
   1437         mWakeLock.setReferenceCounted(false);
   1438         if (washeld) {
   1439             mWakeLock.acquire();
   1440         }
   1441     }
   1442 
   1443     /**
   1444      * Control whether we should use the attached SurfaceHolder to keep the
   1445      * screen on while video playback is occurring.  This is the preferred
   1446      * method over {@link #setWakeMode} where possible, since it doesn't
   1447      * require that the application have permission for low-level wake lock
   1448      * access.
   1449      *
   1450      * @param screenOn Supply true to keep the screen on, false to allow it
   1451      * to turn off.
   1452      */
   1453     public void setScreenOnWhilePlaying(boolean screenOn) {
   1454         if (mScreenOnWhilePlaying != screenOn) {
   1455             if (screenOn && mSurfaceHolder == null) {
   1456                 Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
   1457             }
   1458             mScreenOnWhilePlaying = screenOn;
   1459             updateSurfaceScreenOn();
   1460         }
   1461     }
   1462 
   1463     private void stayAwake(boolean awake) {
   1464         if (mWakeLock != null) {
   1465             if (awake && !mWakeLock.isHeld()) {
   1466                 mWakeLock.acquire();
   1467             } else if (!awake && mWakeLock.isHeld()) {
   1468                 mWakeLock.release();
   1469             }
   1470         }
   1471         mStayAwake = awake;
   1472         updateSurfaceScreenOn();
   1473     }
   1474 
   1475     private void updateSurfaceScreenOn() {
   1476         if (mSurfaceHolder != null) {
   1477             mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
   1478         }
   1479     }
   1480 
   1481     /**
   1482      * Returns the width of the video.
   1483      *
   1484      * @return the width of the video, or 0 if there is no video,
   1485      * no display surface was set, or the width has not been determined
   1486      * yet. The OnVideoSizeChangedListener can be registered via
   1487      * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
   1488      * to provide a notification when the width is available.
   1489      */
   1490     public native int getVideoWidth();
   1491 
   1492     /**
   1493      * Returns the height of the video.
   1494      *
   1495      * @return the height of the video, or 0 if there is no video,
   1496      * no display surface was set, or the height has not been determined
   1497      * yet. The OnVideoSizeChangedListener can be registered via
   1498      * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
   1499      * to provide a notification when the height is available.
   1500      */
   1501     public native int getVideoHeight();
   1502 
   1503     /**
   1504      * Return Metrics data about the current player.
   1505      *
   1506      * @return a {@link PersistableBundle} containing the set of attributes and values
   1507      * available for the media being handled by this instance of MediaPlayer
   1508      * The attributes are descibed in {@link MetricsConstants}.
   1509      *
   1510      *  Additional vendor-specific fields may also be present in
   1511      *  the return value.
   1512      */
   1513     public PersistableBundle getMetrics() {
   1514         PersistableBundle bundle = native_getMetrics();
   1515         return bundle;
   1516     }
   1517 
   1518     private native PersistableBundle native_getMetrics();
   1519 
   1520     /**
   1521      * Checks whether the MediaPlayer is playing.
   1522      *
   1523      * @return true if currently playing, false otherwise
   1524      * @throws IllegalStateException if the internal player engine has not been
   1525      * initialized or has been released.
   1526      */
   1527     public native boolean isPlaying();
   1528 
   1529     /**
   1530      * Gets the default buffering management params.
   1531      * Calling it only after {@code setDataSource} has been called.
   1532      * Each type of data source might have different set of default params.
   1533      *
   1534      * @return the default buffering management params supported by the source component.
   1535      * @throws IllegalStateException if the internal player engine has not been
   1536      * initialized, or {@code setDataSource} has not been called.
   1537      * @hide
   1538      */
   1539     @NonNull
   1540     public native BufferingParams getDefaultBufferingParams();
   1541 
   1542     /**
   1543      * Gets the current buffering management params used by the source component.
   1544      * Calling it only after {@code setDataSource} has been called.
   1545      *
   1546      * @return the current buffering management params used by the source component.
   1547      * @throws IllegalStateException if the internal player engine has not been
   1548      * initialized, or {@code setDataSource} has not been called.
   1549      * @hide
   1550      */
   1551     @NonNull
   1552     public native BufferingParams getBufferingParams();
   1553 
   1554     /**
   1555      * Sets buffering management params.
   1556      * The object sets its internal BufferingParams to the input, except that the input is
   1557      * invalid or not supported.
   1558      * Call it only after {@code setDataSource} has been called.
   1559      * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
   1560      * or its downsized version as described in {@link BufferingParams}.
   1561      *
   1562      * @param params the buffering management params.
   1563      *
   1564      * @throws IllegalStateException if the internal player engine has not been
   1565      * initialized or has been released, or {@code setDataSource} has not been called.
   1566      * @throws IllegalArgumentException if params is invalid or not supported.
   1567      * @hide
   1568      */
   1569     public native void setBufferingParams(@NonNull BufferingParams params);
   1570 
   1571     /**
   1572      * Change playback speed of audio by resampling the audio.
   1573      * <p>
   1574      * Specifies resampling as audio mode for variable rate playback, i.e.,
   1575      * resample the waveform based on the requested playback rate to get
   1576      * a new waveform, and play back the new waveform at the original sampling
   1577      * frequency.
   1578      * When rate is larger than 1.0, pitch becomes higher.
   1579      * When rate is smaller than 1.0, pitch becomes lower.
   1580      *
   1581      * @hide
   1582      */
   1583     public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
   1584 
   1585     /**
   1586      * Change playback speed of audio without changing its pitch.
   1587      * <p>
   1588      * Specifies time stretching as audio mode for variable rate playback.
   1589      * Time stretching changes the duration of the audio samples without
   1590      * affecting its pitch.
   1591      * <p>
   1592      * This mode is only supported for a limited range of playback speed factors,
   1593      * e.g. between 1/2x and 2x.
   1594      *
   1595      * @hide
   1596      */
   1597     public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
   1598 
   1599     /**
   1600      * Change playback speed of audio without changing its pitch, and
   1601      * possibly mute audio if time stretching is not supported for the playback
   1602      * speed.
   1603      * <p>
   1604      * Try to keep audio pitch when changing the playback rate, but allow the
   1605      * system to determine how to change audio playback if the rate is out
   1606      * of range.
   1607      *
   1608      * @hide
   1609      */
   1610     public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
   1611 
   1612     /** @hide */
   1613     @IntDef(
   1614         value = {
   1615             PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
   1616             PLAYBACK_RATE_AUDIO_MODE_STRETCH,
   1617             PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
   1618     })
   1619     @Retention(RetentionPolicy.SOURCE)
   1620     public @interface PlaybackRateAudioMode {}
   1621 
   1622     /**
   1623      * Sets playback rate and audio mode.
   1624      *
   1625      * @param rate the ratio between desired playback rate and normal one.
   1626      * @param audioMode audio playback mode. Must be one of the supported
   1627      * audio modes.
   1628      *
   1629      * @throws IllegalStateException if the internal player engine has not been
   1630      * initialized.
   1631      * @throws IllegalArgumentException if audioMode is not supported.
   1632      *
   1633      * @hide
   1634      */
   1635     @NonNull
   1636     public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
   1637         PlaybackParams params = new PlaybackParams();
   1638         params.allowDefaults();
   1639         switch (audioMode) {
   1640         case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
   1641             params.setSpeed(rate).setPitch(1.0f);
   1642             break;
   1643         case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
   1644             params.setSpeed(rate).setPitch(1.0f)
   1645                     .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
   1646             break;
   1647         case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
   1648             params.setSpeed(rate).setPitch(rate);
   1649             break;
   1650         default:
   1651             final String msg = "Audio playback mode " + audioMode + " is not supported";
   1652             throw new IllegalArgumentException(msg);
   1653         }
   1654         return params;
   1655     }
   1656 
   1657     /**
   1658      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
   1659      * PlaybackParams to the input, except that the object remembers previous speed
   1660      * when input speed is zero. This allows the object to resume at previous speed
   1661      * when start() is called. Calling it before the object is prepared does not change
   1662      * the object state. After the object is prepared, calling it with zero speed is
   1663      * equivalent to calling pause(). After the object is prepared, calling it with
   1664      * non-zero speed is equivalent to calling start().
   1665      *
   1666      * @param params the playback params.
   1667      *
   1668      * @throws IllegalStateException if the internal player engine has not been
   1669      * initialized or has been released.
   1670      * @throws IllegalArgumentException if params is not supported.
   1671      */
   1672     public native void setPlaybackParams(@NonNull PlaybackParams params);
   1673 
   1674     /**
   1675      * Gets the playback params, containing the current playback rate.
   1676      *
   1677      * @return the playback params.
   1678      * @throws IllegalStateException if the internal player engine has not been
   1679      * initialized.
   1680      */
   1681     @NonNull
   1682     public native PlaybackParams getPlaybackParams();
   1683 
   1684     /**
   1685      * Sets A/V sync mode.
   1686      *
   1687      * @param params the A/V sync params to apply
   1688      *
   1689      * @throws IllegalStateException if the internal player engine has not been
   1690      * initialized.
   1691      * @throws IllegalArgumentException if params are not supported.
   1692      */
   1693     public native void setSyncParams(@NonNull SyncParams params);
   1694 
   1695     /**
   1696      * Gets the A/V sync mode.
   1697      *
   1698      * @return the A/V sync params
   1699      *
   1700      * @throws IllegalStateException if the internal player engine has not been
   1701      * initialized.
   1702      */
   1703     @NonNull
   1704     public native SyncParams getSyncParams();
   1705 
   1706     /**
   1707      * Seek modes used in method seekTo(long, int) to move media position
   1708      * to a specified location.
   1709      *
   1710      * Do not change these mode values without updating their counterparts
   1711      * in include/media/IMediaSource.h!
   1712      */
   1713     /**
   1714      * This mode is used with {@link #seekTo(long, int)} to move media position to
   1715      * a sync (or key) frame associated with a data source that is located
   1716      * right before or at the given time.
   1717      *
   1718      * @see #seekTo(long, int)
   1719      */
   1720     public static final int SEEK_PREVIOUS_SYNC    = 0x00;
   1721     /**
   1722      * This mode is used with {@link #seekTo(long, int)} to move media position to
   1723      * a sync (or key) frame associated with a data source that is located
   1724      * right after or at the given time.
   1725      *
   1726      * @see #seekTo(long, int)
   1727      */
   1728     public static final int SEEK_NEXT_SYNC        = 0x01;
   1729     /**
   1730      * This mode is used with {@link #seekTo(long, int)} to move media position to
   1731      * a sync (or key) frame associated with a data source that is located
   1732      * closest to (in time) or at the given time.
   1733      *
   1734      * @see #seekTo(long, int)
   1735      */
   1736     public static final int SEEK_CLOSEST_SYNC     = 0x02;
   1737     /**
   1738      * This mode is used with {@link #seekTo(long, int)} to move media position to
   1739      * a frame (not necessarily a key frame) associated with a data source that
   1740      * is located closest to or at the given time.
   1741      *
   1742      * @see #seekTo(long, int)
   1743      */
   1744     public static final int SEEK_CLOSEST          = 0x03;
   1745 
   1746     /** @hide */
   1747     @IntDef(
   1748         value = {
   1749             SEEK_PREVIOUS_SYNC,
   1750             SEEK_NEXT_SYNC,
   1751             SEEK_CLOSEST_SYNC,
   1752             SEEK_CLOSEST,
   1753     })
   1754     @Retention(RetentionPolicy.SOURCE)
   1755     public @interface SeekMode {}
   1756 
   1757     private native final void _seekTo(long msec, int mode);
   1758 
   1759     /**
   1760      * Moves the media to specified time position by considering the given mode.
   1761      * <p>
   1762      * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
   1763      * There is at most one active seekTo processed at any time. If there is a to-be-completed
   1764      * seekTo, new seekTo requests will be queued in such a way that only the last request
   1765      * is kept. When current seekTo is completed, the queued request will be processed if
   1766      * that request is different from just-finished seekTo operation, i.e., the requested
   1767      * position or mode is different.
   1768      *
   1769      * @param msec the offset in milliseconds from the start to seek to.
   1770      * When seeking to the given time position, there is no guarantee that the data source
   1771      * has a frame located at the position. When this happens, a frame nearby will be rendered.
   1772      * If msec is negative, time position zero will be used.
   1773      * If msec is larger than duration, duration will be used.
   1774      * @param mode the mode indicating where exactly to seek to.
   1775      * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
   1776      * that has a timestamp earlier than or the same as msec. Use
   1777      * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
   1778      * that has a timestamp later than or the same as msec. Use
   1779      * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
   1780      * that has a timestamp closest to or the same as msec. Use
   1781      * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
   1782      * or may not be a sync frame but is closest to or the same as msec.
   1783      * {@link #SEEK_CLOSEST} often has larger performance overhead compared
   1784      * to the other options if there is no sync frame located at msec.
   1785      * @throws IllegalStateException if the internal player engine has not been
   1786      * initialized
   1787      * @throws IllegalArgumentException if the mode is invalid.
   1788      */
   1789     public void seekTo(long msec, @SeekMode int mode) {
   1790         if (mode < SEEK_PREVIOUS_SYNC || mode > SEEK_CLOSEST) {
   1791             final String msg = "Illegal seek mode: " + mode;
   1792             throw new IllegalArgumentException(msg);
   1793         }
   1794         // TODO: pass long to native, instead of truncating here.
   1795         if (msec > Integer.MAX_VALUE) {
   1796             Log.w(TAG, "seekTo offset " + msec + " is too large, cap to " + Integer.MAX_VALUE);
   1797             msec = Integer.MAX_VALUE;
   1798         } else if (msec < Integer.MIN_VALUE) {
   1799             Log.w(TAG, "seekTo offset " + msec + " is too small, cap to " + Integer.MIN_VALUE);
   1800             msec = Integer.MIN_VALUE;
   1801         }
   1802         _seekTo(msec, mode);
   1803     }
   1804 
   1805     /**
   1806      * Seeks to specified time position.
   1807      * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
   1808      *
   1809      * @param msec the offset in milliseconds from the start to seek to
   1810      * @throws IllegalStateException if the internal player engine has not been
   1811      * initialized
   1812      */
   1813     public void seekTo(int msec) throws IllegalStateException {
   1814         seekTo(msec, SEEK_PREVIOUS_SYNC /* mode */);
   1815     }
   1816 
   1817     /**
   1818      * Get current playback position as a {@link MediaTimestamp}.
   1819      * <p>
   1820      * The MediaTimestamp represents how the media time correlates to the system time in
   1821      * a linear fashion using an anchor and a clock rate. During regular playback, the media
   1822      * time moves fairly constantly (though the anchor frame may be rebased to a current
   1823      * system time, the linear correlation stays steady). Therefore, this method does not
   1824      * need to be called often.
   1825      * <p>
   1826      * To help users get current playback position, this method always anchors the timestamp
   1827      * to the current {@link System#nanoTime system time}, so
   1828      * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position.
   1829      *
   1830      * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
   1831      *         is available, e.g. because the media player has not been initialized.
   1832      *
   1833      * @see MediaTimestamp
   1834      */
   1835     @Nullable
   1836     public MediaTimestamp getTimestamp()
   1837     {
   1838         try {
   1839             // TODO: get the timestamp from native side
   1840             return new MediaTimestamp(
   1841                     getCurrentPosition() * 1000L,
   1842                     System.nanoTime(),
   1843                     isPlaying() ? getPlaybackParams().getSpeed() : 0.f);
   1844         } catch (IllegalStateException e) {
   1845             return null;
   1846         }
   1847     }
   1848 
   1849     /**
   1850      * Gets the current playback position.
   1851      *
   1852      * @return the current position in milliseconds
   1853      */
   1854     public native int getCurrentPosition();
   1855 
   1856     /**
   1857      * Gets the duration of the file.
   1858      *
   1859      * @return the duration in milliseconds, if no duration is available
   1860      *         (for example, if streaming live content), -1 is returned.
   1861      */
   1862     public native int getDuration();
   1863 
   1864     /**
   1865      * Gets the media metadata.
   1866      *
   1867      * @param update_only controls whether the full set of available
   1868      * metadata is returned or just the set that changed since the
   1869      * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
   1870      * #METADATA_ALL}.
   1871      *
   1872      * @param apply_filter if true only metadata that matches the
   1873      * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
   1874      * #BYPASS_METADATA_FILTER}.
   1875      *
   1876      * @return The metadata, possibly empty. null if an error occured.
   1877      // FIXME: unhide.
   1878      * {@hide}
   1879      */
   1880     public Metadata getMetadata(final boolean update_only,
   1881                                 final boolean apply_filter) {
   1882         Parcel reply = Parcel.obtain();
   1883         Metadata data = new Metadata();
   1884 
   1885         if (!native_getMetadata(update_only, apply_filter, reply)) {
   1886             reply.recycle();
   1887             return null;
   1888         }
   1889 
   1890         // Metadata takes over the parcel, don't recycle it unless
   1891         // there is an error.
   1892         if (!data.parse(reply)) {
   1893             reply.recycle();
   1894             return null;
   1895         }
   1896         return data;
   1897     }
   1898 
   1899     /**
   1900      * Set a filter for the metadata update notification and update
   1901      * retrieval. The caller provides 2 set of metadata keys, allowed
   1902      * and blocked. The blocked set always takes precedence over the
   1903      * allowed one.
   1904      * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
   1905      * shorthands to allow/block all or no metadata.
   1906      *
   1907      * By default, there is no filter set.
   1908      *
   1909      * @param allow Is the set of metadata the client is interested
   1910      *              in receiving new notifications for.
   1911      * @param block Is the set of metadata the client is not interested
   1912      *              in receiving new notifications for.
   1913      * @return The call status code.
   1914      *
   1915      // FIXME: unhide.
   1916      * {@hide}
   1917      */
   1918     public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
   1919         // Do our serialization manually instead of calling
   1920         // Parcel.writeArray since the sets are made of the same type
   1921         // we avoid paying the price of calling writeValue (used by
   1922         // writeArray) which burns an extra int per element to encode
   1923         // the type.
   1924         Parcel request =  newRequest();
   1925 
   1926         // The parcel starts already with an interface token. There
   1927         // are 2 filters. Each one starts with a 4bytes number to
   1928         // store the len followed by a number of int (4 bytes as well)
   1929         // representing the metadata type.
   1930         int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
   1931 
   1932         if (request.dataCapacity() < capacity) {
   1933             request.setDataCapacity(capacity);
   1934         }
   1935 
   1936         request.writeInt(allow.size());
   1937         for(Integer t: allow) {
   1938             request.writeInt(t);
   1939         }
   1940         request.writeInt(block.size());
   1941         for(Integer t: block) {
   1942             request.writeInt(t);
   1943         }
   1944         return native_setMetadataFilter(request);
   1945     }
   1946 
   1947     /**
   1948      * Set the MediaPlayer to start when this MediaPlayer finishes playback
   1949      * (i.e. reaches the end of the stream).
   1950      * The media framework will attempt to transition from this player to
   1951      * the next as seamlessly as possible. The next player can be set at
   1952      * any time before completion, but shall be after setDataSource has been
   1953      * called successfully. The next player must be prepared by the
   1954      * app, and the application should not call start() on it.
   1955      * The next MediaPlayer must be different from 'this'. An exception
   1956      * will be thrown if next == this.
   1957      * The application may call setNextMediaPlayer(null) to indicate no
   1958      * next player should be started at the end of playback.
   1959      * If the current player is looping, it will keep looping and the next
   1960      * player will not be started.
   1961      *
   1962      * @param next the player to start after this one completes playback.
   1963      *
   1964      */
   1965     public native void setNextMediaPlayer(MediaPlayer next);
   1966 
   1967     /**
   1968      * Releases resources associated with this MediaPlayer object.
   1969      * It is considered good practice to call this method when you're
   1970      * done using the MediaPlayer. In particular, whenever an Activity
   1971      * of an application is paused (its onPause() method is called),
   1972      * or stopped (its onStop() method is called), this method should be
   1973      * invoked to release the MediaPlayer object, unless the application
   1974      * has a special need to keep the object around. In addition to
   1975      * unnecessary resources (such as memory and instances of codecs)
   1976      * being held, failure to call this method immediately if a
   1977      * MediaPlayer object is no longer needed may also lead to
   1978      * continuous battery consumption for mobile devices, and playback
   1979      * failure for other applications if no multiple instances of the
   1980      * same codec are supported on a device. Even if multiple instances
   1981      * of the same codec are supported, some performance degradation
   1982      * may be expected when unnecessary multiple instances are used
   1983      * at the same time.
   1984      */
   1985     public void release() {
   1986         baseRelease();
   1987         stayAwake(false);
   1988         updateSurfaceScreenOn();
   1989         mOnPreparedListener = null;
   1990         mOnBufferingUpdateListener = null;
   1991         mOnCompletionListener = null;
   1992         mOnSeekCompleteListener = null;
   1993         mOnErrorListener = null;
   1994         mOnInfoListener = null;
   1995         mOnVideoSizeChangedListener = null;
   1996         mOnTimedTextListener = null;
   1997         if (mTimeProvider != null) {
   1998             mTimeProvider.close();
   1999             mTimeProvider = null;
   2000         }
   2001         mOnSubtitleDataListener = null;
   2002 
   2003         // Modular DRM clean up
   2004         mOnDrmConfigHelper = null;
   2005         mOnDrmInfoHandlerDelegate = null;
   2006         mOnDrmPreparedHandlerDelegate = null;
   2007         resetDrmState();
   2008 
   2009         _release();
   2010     }
   2011 
   2012     private native void _release();
   2013 
   2014     /**
   2015      * Resets the MediaPlayer to its uninitialized state. After calling
   2016      * this method, you will have to initialize it again by setting the
   2017      * data source and calling prepare().
   2018      */
   2019     public void reset() {
   2020         mSelectedSubtitleTrackIndex = -1;
   2021         synchronized(mOpenSubtitleSources) {
   2022             for (final InputStream is: mOpenSubtitleSources) {
   2023                 try {
   2024                     is.close();
   2025                 } catch (IOException e) {
   2026                 }
   2027             }
   2028             mOpenSubtitleSources.clear();
   2029         }
   2030         if (mSubtitleController != null) {
   2031             mSubtitleController.reset();
   2032         }
   2033         if (mTimeProvider != null) {
   2034             mTimeProvider.close();
   2035             mTimeProvider = null;
   2036         }
   2037 
   2038         stayAwake(false);
   2039         _reset();
   2040         // make sure none of the listeners get called anymore
   2041         if (mEventHandler != null) {
   2042             mEventHandler.removeCallbacksAndMessages(null);
   2043         }
   2044 
   2045         synchronized (mIndexTrackPairs) {
   2046             mIndexTrackPairs.clear();
   2047             mInbandTrackIndices.clear();
   2048         };
   2049 
   2050         resetDrmState();
   2051     }
   2052 
   2053     private native void _reset();
   2054 
   2055     /**
   2056      * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
   2057      * for a list of stream types. Must call this method before prepare() or
   2058      * prepareAsync() in order for the target stream type to become effective
   2059      * thereafter.
   2060      *
   2061      * @param streamtype the audio stream type
   2062      * @deprecated use {@link #setAudioAttributes(AudioAttributes)}
   2063      * @see android.media.AudioManager
   2064      */
   2065     public void setAudioStreamType(int streamtype) {
   2066         deprecateStreamTypeForPlayback(streamtype, "MediaPlayer", "setAudioStreamType()");
   2067         baseUpdateAudioAttributes(
   2068                 new AudioAttributes.Builder().setInternalLegacyStreamType(streamtype).build());
   2069         _setAudioStreamType(streamtype);
   2070         mStreamType = streamtype;
   2071     }
   2072 
   2073     private native void _setAudioStreamType(int streamtype);
   2074 
   2075     // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h
   2076     private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
   2077     /**
   2078      * Sets the parameter indicated by key.
   2079      * @param key key indicates the parameter to be set.
   2080      * @param value value of the parameter to be set.
   2081      * @return true if the parameter is set successfully, false otherwise
   2082      * {@hide}
   2083      */
   2084     private native boolean setParameter(int key, Parcel value);
   2085 
   2086     /**
   2087      * Sets the audio attributes for this MediaPlayer.
   2088      * See {@link AudioAttributes} for how to build and configure an instance of this class.
   2089      * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order
   2090      * for the audio attributes to become effective thereafter.
   2091      * @param attributes a non-null set of audio attributes
   2092      */
   2093     public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {
   2094         if (attributes == null) {
   2095             final String msg = "Cannot set AudioAttributes to null";
   2096             throw new IllegalArgumentException(msg);
   2097         }
   2098         baseUpdateAudioAttributes(attributes);
   2099         mUsage = attributes.getUsage();
   2100         mBypassInterruptionPolicy = (attributes.getAllFlags()
   2101                 & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
   2102         Parcel pattributes = Parcel.obtain();
   2103         attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
   2104         setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
   2105         pattributes.recycle();
   2106     }
   2107 
   2108     /**
   2109      * Sets the player to be looping or non-looping.
   2110      *
   2111      * @param looping whether to loop or not
   2112      */
   2113     public native void setLooping(boolean looping);
   2114 
   2115     /**
   2116      * Checks whether the MediaPlayer is looping or non-looping.
   2117      *
   2118      * @return true if the MediaPlayer is currently looping, false otherwise
   2119      */
   2120     public native boolean isLooping();
   2121 
   2122     /**
   2123      * Sets the volume on this player.
   2124      * This API is recommended for balancing the output of audio streams
   2125      * within an application. Unless you are writing an application to
   2126      * control user settings, this API should be used in preference to
   2127      * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
   2128      * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
   2129      * UI controls should be scaled logarithmically.
   2130      *
   2131      * @param leftVolume left volume scalar
   2132      * @param rightVolume right volume scalar
   2133      */
   2134     /*
   2135      * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
   2136      * The single parameter form below is preferred if the channel volumes don't need
   2137      * to be set independently.
   2138      */
   2139     public void setVolume(float leftVolume, float rightVolume) {
   2140         baseSetVolume(leftVolume, rightVolume);
   2141     }
   2142 
   2143     @Override
   2144     void playerSetVolume(boolean muting, float leftVolume, float rightVolume) {
   2145         _setVolume(muting ? 0.0f : leftVolume, muting ? 0.0f : rightVolume);
   2146     }
   2147 
   2148     private native void _setVolume(float leftVolume, float rightVolume);
   2149 
   2150     /**
   2151      * Similar, excepts sets volume of all channels to same value.
   2152      * @hide
   2153      */
   2154     public void setVolume(float volume) {
   2155         setVolume(volume, volume);
   2156     }
   2157 
   2158     /**
   2159      * Sets the audio session ID.
   2160      *
   2161      * @param sessionId the audio session ID.
   2162      * The audio session ID is a system wide unique identifier for the audio stream played by
   2163      * this MediaPlayer instance.
   2164      * The primary use of the audio session ID  is to associate audio effects to a particular
   2165      * instance of MediaPlayer: if an audio session ID is provided when creating an audio effect,
   2166      * this effect will be applied only to the audio content of media players within the same
   2167      * audio session and not to the output mix.
   2168      * When created, a MediaPlayer instance automatically generates its own audio session ID.
   2169      * However, it is possible to force this player to be part of an already existing audio session
   2170      * by calling this method.
   2171      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
   2172      * @throws IllegalStateException if it is called in an invalid state
   2173      */
   2174     public native void setAudioSessionId(int sessionId)  throws IllegalArgumentException, IllegalStateException;
   2175 
   2176     /**
   2177      * Returns the audio session ID.
   2178      *
   2179      * @return the audio session ID. {@see #setAudioSessionId(int)}
   2180      * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer was contructed.
   2181      */
   2182     public native int getAudioSessionId();
   2183 
   2184     /**
   2185      * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
   2186      * effect which can be applied on any sound source that directs a certain amount of its
   2187      * energy to this effect. This amount is defined by setAuxEffectSendLevel().
   2188      * See {@link #setAuxEffectSendLevel(float)}.
   2189      * <p>After creating an auxiliary effect (e.g.
   2190      * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
   2191      * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
   2192      * to attach the player to the effect.
   2193      * <p>To detach the effect from the player, call this method with a null effect id.
   2194      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
   2195      * methods.
   2196      * @param effectId system wide unique id of the effect to attach
   2197      */
   2198     public native void attachAuxEffect(int effectId);
   2199 
   2200 
   2201     /**
   2202      * Sets the send level of the player to the attached auxiliary effect.
   2203      * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
   2204      * <p>By default the send level is 0, so even if an effect is attached to the player
   2205      * this method must be called for the effect to be applied.
   2206      * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
   2207      * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
   2208      * so an appropriate conversion from linear UI input x to level is:
   2209      * x == 0 -> level = 0
   2210      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
   2211      * @param level send level scalar
   2212      */
   2213     public void setAuxEffectSendLevel(float level) {
   2214         baseSetAuxEffectSendLevel(level);
   2215     }
   2216 
   2217     @Override
   2218     int playerSetAuxEffectSendLevel(boolean muting, float level) {
   2219         _setAuxEffectSendLevel(muting ? 0.0f : level);
   2220         return AudioSystem.SUCCESS;
   2221     }
   2222 
   2223     private native void _setAuxEffectSendLevel(float level);
   2224 
   2225     /*
   2226      * @param request Parcel destinated to the media player. The
   2227      *                Interface token must be set to the IMediaPlayer
   2228      *                one to be routed correctly through the system.
   2229      * @param reply[out] Parcel that will contain the reply.
   2230      * @return The status code.
   2231      */
   2232     private native final int native_invoke(Parcel request, Parcel reply);
   2233 
   2234 
   2235     /*
   2236      * @param update_only If true fetch only the set of metadata that have
   2237      *                    changed since the last invocation of getMetadata.
   2238      *                    The set is built using the unfiltered
   2239      *                    notifications the native player sent to the
   2240      *                    MediaPlayerService during that period of
   2241      *                    time. If false, all the metadatas are considered.
   2242      * @param apply_filter  If true, once the metadata set has been built based on
   2243      *                     the value update_only, the current filter is applied.
   2244      * @param reply[out] On return contains the serialized
   2245      *                   metadata. Valid only if the call was successful.
   2246      * @return The status code.
   2247      */
   2248     private native final boolean native_getMetadata(boolean update_only,
   2249                                                     boolean apply_filter,
   2250                                                     Parcel reply);
   2251 
   2252     /*
   2253      * @param request Parcel with the 2 serialized lists of allowed
   2254      *                metadata types followed by the one to be
   2255      *                dropped. Each list starts with an integer
   2256      *                indicating the number of metadata type elements.
   2257      * @return The status code.
   2258      */
   2259     private native final int native_setMetadataFilter(Parcel request);
   2260 
   2261     private static native final void native_init();
   2262     private native final void native_setup(Object mediaplayer_this);
   2263     private native final void native_finalize();
   2264 
   2265     /**
   2266      * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
   2267      *
   2268      * @see android.media.MediaPlayer#getTrackInfo
   2269      */
   2270     static public class TrackInfo implements Parcelable {
   2271         /**
   2272          * Gets the track type.
   2273          * @return TrackType which indicates if the track is video, audio, timed text.
   2274          */
   2275         public int getTrackType() {
   2276             return mTrackType;
   2277         }
   2278 
   2279         /**
   2280          * Gets the language code of the track.
   2281          * @return a language code in either way of ISO-639-1 or ISO-639-2.
   2282          * When the language is unknown or could not be determined,
   2283          * ISO-639-2 language code, "und", is returned.
   2284          */
   2285         public String getLanguage() {
   2286             String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
   2287             return language == null ? "und" : language;
   2288         }
   2289 
   2290         /**
   2291          * Gets the {@link MediaFormat} of the track.  If the format is
   2292          * unknown or could not be determined, null is returned.
   2293          */
   2294         public MediaFormat getFormat() {
   2295             if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
   2296                     || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   2297                 return mFormat;
   2298             }
   2299             return null;
   2300         }
   2301 
   2302         public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
   2303         public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
   2304         public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
   2305         public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
   2306         public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
   2307         public static final int MEDIA_TRACK_TYPE_METADATA = 5;
   2308 
   2309         final int mTrackType;
   2310         final MediaFormat mFormat;
   2311 
   2312         TrackInfo(Parcel in) {
   2313             mTrackType = in.readInt();
   2314             // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat
   2315             // even for audio/video tracks, meaning we only set the mime and language.
   2316             String mime = in.readString();
   2317             String language = in.readString();
   2318             mFormat = MediaFormat.createSubtitleFormat(mime, language);
   2319 
   2320             if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   2321                 mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
   2322                 mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
   2323                 mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
   2324             }
   2325         }
   2326 
   2327         /** @hide */
   2328         TrackInfo(int type, MediaFormat format) {
   2329             mTrackType = type;
   2330             mFormat = format;
   2331         }
   2332 
   2333         /**
   2334          * {@inheritDoc}
   2335          */
   2336         @Override
   2337         public int describeContents() {
   2338             return 0;
   2339         }
   2340 
   2341         /**
   2342          * {@inheritDoc}
   2343          */
   2344         @Override
   2345         public void writeToParcel(Parcel dest, int flags) {
   2346             dest.writeInt(mTrackType);
   2347             dest.writeString(getLanguage());
   2348 
   2349             if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   2350                 dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
   2351                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
   2352                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
   2353                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
   2354             }
   2355         }
   2356 
   2357         @Override
   2358         public String toString() {
   2359             StringBuilder out = new StringBuilder(128);
   2360             out.append(getClass().getName());
   2361             out.append('{');
   2362             switch (mTrackType) {
   2363             case MEDIA_TRACK_TYPE_VIDEO:
   2364                 out.append("VIDEO");
   2365                 break;
   2366             case MEDIA_TRACK_TYPE_AUDIO:
   2367                 out.append("AUDIO");
   2368                 break;
   2369             case MEDIA_TRACK_TYPE_TIMEDTEXT:
   2370                 out.append("TIMEDTEXT");
   2371                 break;
   2372             case MEDIA_TRACK_TYPE_SUBTITLE:
   2373                 out.append("SUBTITLE");
   2374                 break;
   2375             default:
   2376                 out.append("UNKNOWN");
   2377                 break;
   2378             }
   2379             out.append(", " + mFormat.toString());
   2380             out.append("}");
   2381             return out.toString();
   2382         }
   2383 
   2384         /**
   2385          * Used to read a TrackInfo from a Parcel.
   2386          */
   2387         static final Parcelable.Creator<TrackInfo> CREATOR
   2388                 = new Parcelable.Creator<TrackInfo>() {
   2389                     @Override
   2390                     public TrackInfo createFromParcel(Parcel in) {
   2391                         return new TrackInfo(in);
   2392                     }
   2393 
   2394                     @Override
   2395                     public TrackInfo[] newArray(int size) {
   2396                         return new TrackInfo[size];
   2397                     }
   2398                 };
   2399 
   2400     };
   2401 
   2402     // We would like domain specific classes with more informative names than the `first` and `second`
   2403     // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise
   2404     // we document the meanings of `first` and `second` here:
   2405     //
   2406     // Pair.first - inband track index; non-null iff representing an inband track.
   2407     // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing
   2408     //               an inband subtitle track or any out-of-band track (subtitle or timedtext).
   2409     private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>();
   2410     private BitSet mInbandTrackIndices = new BitSet();
   2411 
   2412     /**
   2413      * Returns an array of track information.
   2414      *
   2415      * @return Array of track info. The total number of tracks is the array length.
   2416      * Must be called again if an external timed text source has been added after any of the
   2417      * addTimedTextSource methods are called.
   2418      * @throws IllegalStateException if it is called in an invalid state.
   2419      */
   2420     public TrackInfo[] getTrackInfo() throws IllegalStateException {
   2421         TrackInfo trackInfo[] = getInbandTrackInfo();
   2422         // add out-of-band tracks
   2423         synchronized (mIndexTrackPairs) {
   2424             TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()];
   2425             for (int i = 0; i < allTrackInfo.length; i++) {
   2426                 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2427                 if (p.first != null) {
   2428                     // inband track
   2429                     allTrackInfo[i] = trackInfo[p.first];
   2430                 } else {
   2431                     SubtitleTrack track = p.second;
   2432                     allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat());
   2433                 }
   2434             }
   2435             return allTrackInfo;
   2436         }
   2437     }
   2438 
   2439     private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
   2440         Parcel request = Parcel.obtain();
   2441         Parcel reply = Parcel.obtain();
   2442         try {
   2443             request.writeInterfaceToken(IMEDIA_PLAYER);
   2444             request.writeInt(INVOKE_ID_GET_TRACK_INFO);
   2445             invoke(request, reply);
   2446             TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
   2447             return trackInfo;
   2448         } finally {
   2449             request.recycle();
   2450             reply.recycle();
   2451         }
   2452     }
   2453 
   2454     /* Do not change these values without updating their counterparts
   2455      * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
   2456      */
   2457     /**
   2458      * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
   2459      */
   2460     public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
   2461 
   2462     /**
   2463      * MIME type for WebVTT subtitle data.
   2464      * @hide
   2465      */
   2466     public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
   2467 
   2468     /**
   2469      * MIME type for CEA-608 closed caption data.
   2470      * @hide
   2471      */
   2472     public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
   2473 
   2474     /**
   2475      * MIME type for CEA-708 closed caption data.
   2476      * @hide
   2477      */
   2478     public static final String MEDIA_MIMETYPE_TEXT_CEA_708 = "text/cea-708";
   2479 
   2480     /*
   2481      * A helper function to check if the mime type is supported by media framework.
   2482      */
   2483     private static boolean availableMimeTypeForExternalSource(String mimeType) {
   2484         if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) {
   2485             return true;
   2486         }
   2487         return false;
   2488     }
   2489 
   2490     private SubtitleController mSubtitleController;
   2491 
   2492     /** @hide */
   2493     public void setSubtitleAnchor(
   2494             SubtitleController controller,
   2495             SubtitleController.Anchor anchor) {
   2496         // TODO: create SubtitleController in MediaPlayer
   2497         mSubtitleController = controller;
   2498         mSubtitleController.setAnchor(anchor);
   2499     }
   2500 
   2501     /**
   2502      * The private version of setSubtitleAnchor is used internally to set mSubtitleController if
   2503      * necessary when clients don't provide their own SubtitleControllers using the public version
   2504      * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
   2505      */
   2506     private synchronized void setSubtitleAnchor() {
   2507         if ((mSubtitleController == null) && (ActivityThread.currentApplication() != null)) {
   2508             final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
   2509             thread.start();
   2510             Handler handler = new Handler(thread.getLooper());
   2511             handler.post(new Runnable() {
   2512                 @Override
   2513                 public void run() {
   2514                     Context context = ActivityThread.currentApplication();
   2515                     mSubtitleController = new SubtitleController(context, mTimeProvider, MediaPlayer.this);
   2516                     mSubtitleController.setAnchor(new Anchor() {
   2517                         @Override
   2518                         public void setSubtitleWidget(RenderingWidget subtitleWidget) {
   2519                         }
   2520 
   2521                         @Override
   2522                         public Looper getSubtitleLooper() {
   2523                             return Looper.getMainLooper();
   2524                         }
   2525                     });
   2526                     thread.getLooper().quitSafely();
   2527                 }
   2528             });
   2529             try {
   2530                 thread.join();
   2531             } catch (InterruptedException e) {
   2532                 Thread.currentThread().interrupt();
   2533                 Log.w(TAG, "failed to join SetSubtitleAnchorThread");
   2534             }
   2535         }
   2536     }
   2537 
   2538     private int mSelectedSubtitleTrackIndex = -1;
   2539     private Vector<InputStream> mOpenSubtitleSources;
   2540 
   2541     private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
   2542         @Override
   2543         public void onSubtitleData(MediaPlayer mp, SubtitleData data) {
   2544             int index = data.getTrackIndex();
   2545             synchronized (mIndexTrackPairs) {
   2546                 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
   2547                     if (p.first != null && p.first == index && p.second != null) {
   2548                         // inband subtitle track that owns data
   2549                         SubtitleTrack track = p.second;
   2550                         track.onData(data);
   2551                     }
   2552                 }
   2553             }
   2554         }
   2555     };
   2556 
   2557     /** @hide */
   2558     @Override
   2559     public void onSubtitleTrackSelected(SubtitleTrack track) {
   2560         if (mSelectedSubtitleTrackIndex >= 0) {
   2561             try {
   2562                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, false);
   2563             } catch (IllegalStateException e) {
   2564             }
   2565             mSelectedSubtitleTrackIndex = -1;
   2566         }
   2567         setOnSubtitleDataListener(null);
   2568         if (track == null) {
   2569             return;
   2570         }
   2571 
   2572         synchronized (mIndexTrackPairs) {
   2573             for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
   2574                 if (p.first != null && p.second == track) {
   2575                     // inband subtitle track that is selected
   2576                     mSelectedSubtitleTrackIndex = p.first;
   2577                     break;
   2578                 }
   2579             }
   2580         }
   2581 
   2582         if (mSelectedSubtitleTrackIndex >= 0) {
   2583             try {
   2584                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
   2585             } catch (IllegalStateException e) {
   2586             }
   2587             setOnSubtitleDataListener(mSubtitleDataListener);
   2588         }
   2589         // no need to select out-of-band tracks
   2590     }
   2591 
   2592     /** @hide */
   2593     public void addSubtitleSource(InputStream is, MediaFormat format)
   2594             throws IllegalStateException
   2595     {
   2596         final InputStream fIs = is;
   2597         final MediaFormat fFormat = format;
   2598 
   2599         if (is != null) {
   2600             // Ensure all input streams are closed.  It is also a handy
   2601             // way to implement timeouts in the future.
   2602             synchronized(mOpenSubtitleSources) {
   2603                 mOpenSubtitleSources.add(is);
   2604             }
   2605         } else {
   2606             Log.w(TAG, "addSubtitleSource called with null InputStream");
   2607         }
   2608 
   2609         getMediaTimeProvider();
   2610 
   2611         // process each subtitle in its own thread
   2612         final HandlerThread thread = new HandlerThread("SubtitleReadThread",
   2613               Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
   2614         thread.start();
   2615         Handler handler = new Handler(thread.getLooper());
   2616         handler.post(new Runnable() {
   2617             private int addTrack() {
   2618                 if (fIs == null || mSubtitleController == null) {
   2619                     return MEDIA_INFO_UNSUPPORTED_SUBTITLE;
   2620                 }
   2621 
   2622                 SubtitleTrack track = mSubtitleController.addTrack(fFormat);
   2623                 if (track == null) {
   2624                     return MEDIA_INFO_UNSUPPORTED_SUBTITLE;
   2625                 }
   2626 
   2627                 // TODO: do the conversion in the subtitle track
   2628                 Scanner scanner = new Scanner(fIs, "UTF-8");
   2629                 String contents = scanner.useDelimiter("\\A").next();
   2630                 synchronized(mOpenSubtitleSources) {
   2631                     mOpenSubtitleSources.remove(fIs);
   2632                 }
   2633                 scanner.close();
   2634                 synchronized (mIndexTrackPairs) {
   2635                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
   2636                 }
   2637                 Handler h = mTimeProvider.mEventHandler;
   2638                 int what = TimeProvider.NOTIFY;
   2639                 int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
   2640                 Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, contents.getBytes());
   2641                 Message m = h.obtainMessage(what, arg1, 0, trackData);
   2642                 h.sendMessage(m);
   2643                 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
   2644             }
   2645 
   2646             public void run() {
   2647                 int res = addTrack();
   2648                 if (mEventHandler != null) {
   2649                     Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
   2650                     mEventHandler.sendMessage(m);
   2651                 }
   2652                 thread.getLooper().quitSafely();
   2653             }
   2654         });
   2655     }
   2656 
   2657     private void scanInternalSubtitleTracks() {
   2658         setSubtitleAnchor();
   2659 
   2660         populateInbandTracks();
   2661 
   2662         if (mSubtitleController != null) {
   2663             mSubtitleController.selectDefaultTrack();
   2664         }
   2665     }
   2666 
   2667     private void populateInbandTracks() {
   2668         TrackInfo[] tracks = getInbandTrackInfo();
   2669         synchronized (mIndexTrackPairs) {
   2670             for (int i = 0; i < tracks.length; i++) {
   2671                 if (mInbandTrackIndices.get(i)) {
   2672                     continue;
   2673                 } else {
   2674                     mInbandTrackIndices.set(i);
   2675                 }
   2676 
   2677                 // newly appeared inband track
   2678                 if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
   2679                     SubtitleTrack track = mSubtitleController.addTrack(
   2680                             tracks[i].getFormat());
   2681                     mIndexTrackPairs.add(Pair.create(i, track));
   2682                 } else {
   2683                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null));
   2684                 }
   2685             }
   2686         }
   2687     }
   2688 
   2689     /* TODO: Limit the total number of external timed text source to a reasonable number.
   2690      */
   2691     /**
   2692      * Adds an external timed text source file.
   2693      *
   2694      * Currently supported format is SubRip with the file extension .srt, case insensitive.
   2695      * Note that a single external timed text source may contain multiple tracks in it.
   2696      * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
   2697      * additional tracks become available after this method call.
   2698      *
   2699      * @param path The file path of external timed text source file.
   2700      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2701      * @throws IOException if the file cannot be accessed or is corrupted.
   2702      * @throws IllegalArgumentException if the mimeType is not supported.
   2703      * @throws IllegalStateException if called in an invalid state.
   2704      */
   2705     public void addTimedTextSource(String path, String mimeType)
   2706             throws IOException, IllegalArgumentException, IllegalStateException {
   2707         if (!availableMimeTypeForExternalSource(mimeType)) {
   2708             final String msg = "Illegal mimeType for timed text source: " + mimeType;
   2709             throw new IllegalArgumentException(msg);
   2710         }
   2711 
   2712         File file = new File(path);
   2713         if (file.exists()) {
   2714             FileInputStream is = new FileInputStream(file);
   2715             FileDescriptor fd = is.getFD();
   2716             addTimedTextSource(fd, mimeType);
   2717             is.close();
   2718         } else {
   2719             // We do not support the case where the path is not a file.
   2720             throw new IOException(path);
   2721         }
   2722     }
   2723 
   2724     /**
   2725      * Adds an external timed text source file (Uri).
   2726      *
   2727      * Currently supported format is SubRip with the file extension .srt, case insensitive.
   2728      * Note that a single external timed text source may contain multiple tracks in it.
   2729      * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
   2730      * additional tracks become available after this method call.
   2731      *
   2732      * @param context the Context to use when resolving the Uri
   2733      * @param uri the Content URI of the data you want to play
   2734      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2735      * @throws IOException if the file cannot be accessed or is corrupted.
   2736      * @throws IllegalArgumentException if the mimeType is not supported.
   2737      * @throws IllegalStateException if called in an invalid state.
   2738      */
   2739     public void addTimedTextSource(Context context, Uri uri, String mimeType)
   2740             throws IOException, IllegalArgumentException, IllegalStateException {
   2741         String scheme = uri.getScheme();
   2742         if(scheme == null || scheme.equals("file")) {
   2743             addTimedTextSource(uri.getPath(), mimeType);
   2744             return;
   2745         }
   2746 
   2747         AssetFileDescriptor fd = null;
   2748         try {
   2749             ContentResolver resolver = context.getContentResolver();
   2750             fd = resolver.openAssetFileDescriptor(uri, "r");
   2751             if (fd == null) {
   2752                 return;
   2753             }
   2754             addTimedTextSource(fd.getFileDescriptor(), mimeType);
   2755             return;
   2756         } catch (SecurityException ex) {
   2757         } catch (IOException ex) {
   2758         } finally {
   2759             if (fd != null) {
   2760                 fd.close();
   2761             }
   2762         }
   2763     }
   2764 
   2765     /**
   2766      * Adds an external timed text source file (FileDescriptor).
   2767      *
   2768      * It is the caller's responsibility to close the file descriptor.
   2769      * It is safe to do so as soon as this call returns.
   2770      *
   2771      * Currently supported format is SubRip. Note that a single external timed text source may
   2772      * contain multiple tracks in it. One can find the total number of available tracks
   2773      * using {@link #getTrackInfo()} to see what additional tracks become available
   2774      * after this method call.
   2775      *
   2776      * @param fd the FileDescriptor for the file you want to play
   2777      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2778      * @throws IllegalArgumentException if the mimeType is not supported.
   2779      * @throws IllegalStateException if called in an invalid state.
   2780      */
   2781     public void addTimedTextSource(FileDescriptor fd, String mimeType)
   2782             throws IllegalArgumentException, IllegalStateException {
   2783         // intentionally less than LONG_MAX
   2784         addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType);
   2785     }
   2786 
   2787     /**
   2788      * Adds an external timed text file (FileDescriptor).
   2789      *
   2790      * It is the caller's responsibility to close the file descriptor.
   2791      * It is safe to do so as soon as this call returns.
   2792      *
   2793      * Currently supported format is SubRip. Note that a single external timed text source may
   2794      * contain multiple tracks in it. One can find the total number of available tracks
   2795      * using {@link #getTrackInfo()} to see what additional tracks become available
   2796      * after this method call.
   2797      *
   2798      * @param fd the FileDescriptor for the file you want to play
   2799      * @param offset the offset into the file where the data to be played starts, in bytes
   2800      * @param length the length in bytes of the data to be played
   2801      * @param mime The mime type of the file. Must be one of the mime types listed above.
   2802      * @throws IllegalArgumentException if the mimeType is not supported.
   2803      * @throws IllegalStateException if called in an invalid state.
   2804      */
   2805     public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime)
   2806             throws IllegalArgumentException, IllegalStateException {
   2807         if (!availableMimeTypeForExternalSource(mime)) {
   2808             throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mime);
   2809         }
   2810 
   2811         final FileDescriptor dupedFd;
   2812         try {
   2813             dupedFd = Libcore.os.dup(fd);
   2814         } catch (ErrnoException ex) {
   2815             Log.e(TAG, ex.getMessage(), ex);
   2816             throw new RuntimeException(ex);
   2817         }
   2818 
   2819         final MediaFormat fFormat = new MediaFormat();
   2820         fFormat.setString(MediaFormat.KEY_MIME, mime);
   2821         fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1);
   2822 
   2823         // A MediaPlayer created by a VideoView should already have its mSubtitleController set.
   2824         if (mSubtitleController == null) {
   2825             setSubtitleAnchor();
   2826         }
   2827 
   2828         if (!mSubtitleController.hasRendererFor(fFormat)) {
   2829             // test and add not atomic
   2830             Context context = ActivityThread.currentApplication();
   2831             mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
   2832         }
   2833         final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
   2834         synchronized (mIndexTrackPairs) {
   2835             mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
   2836         }
   2837 
   2838         getMediaTimeProvider();
   2839 
   2840         final long offset2 = offset;
   2841         final long length2 = length;
   2842         final HandlerThread thread = new HandlerThread(
   2843                 "TimedTextReadThread",
   2844                 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
   2845         thread.start();
   2846         Handler handler = new Handler(thread.getLooper());
   2847         handler.post(new Runnable() {
   2848             private int addTrack() {
   2849                 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
   2850                 try {
   2851                     Libcore.os.lseek(dupedFd, offset2, OsConstants.SEEK_SET);
   2852                     byte[] buffer = new byte[4096];
   2853                     for (long total = 0; total < length2;) {
   2854                         int bytesToRead = (int) Math.min(buffer.length, length2 - total);
   2855                         int bytes = IoBridge.read(dupedFd, buffer, 0, bytesToRead);
   2856                         if (bytes < 0) {
   2857                             break;
   2858                         } else {
   2859                             bos.write(buffer, 0, bytes);
   2860                             total += bytes;
   2861                         }
   2862                     }
   2863                     Handler h = mTimeProvider.mEventHandler;
   2864                     int what = TimeProvider.NOTIFY;
   2865                     int arg1 = TimeProvider.NOTIFY_TRACK_DATA;
   2866                     Pair<SubtitleTrack, byte[]> trackData = Pair.create(track, bos.toByteArray());
   2867                     Message m = h.obtainMessage(what, arg1, 0, trackData);
   2868                     h.sendMessage(m);
   2869                     return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
   2870                 } catch (Exception e) {
   2871                     Log.e(TAG, e.getMessage(), e);
   2872                     return MEDIA_INFO_TIMED_TEXT_ERROR;
   2873                 } finally {
   2874                     try {
   2875                         Libcore.os.close(dupedFd);
   2876                     } catch (ErrnoException e) {
   2877                         Log.e(TAG, e.getMessage(), e);
   2878                     }
   2879                 }
   2880             }
   2881 
   2882             public void run() {
   2883                 int res = addTrack();
   2884                 if (mEventHandler != null) {
   2885                     Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
   2886                     mEventHandler.sendMessage(m);
   2887                 }
   2888                 thread.getLooper().quitSafely();
   2889             }
   2890         });
   2891     }
   2892 
   2893     /**
   2894      * Returns the index of the audio, video, or subtitle track currently selected for playback,
   2895      * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
   2896      * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
   2897      *
   2898      * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
   2899      * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
   2900      * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
   2901      * @return index of the audio, video, or subtitle track currently selected for playback;
   2902      * a negative integer is returned when there is no selected track for {@code trackType} or
   2903      * when {@code trackType} is not one of audio, video, or subtitle.
   2904      * @throws IllegalStateException if called after {@link #release()}
   2905      *
   2906      * @see #getTrackInfo()
   2907      * @see #selectTrack(int)
   2908      * @see #deselectTrack(int)
   2909      */
   2910     public int getSelectedTrack(int trackType) throws IllegalStateException {
   2911         if (mSubtitleController != null
   2912                 && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
   2913                 || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) {
   2914             SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack();
   2915             if (subtitleTrack != null) {
   2916                 synchronized (mIndexTrackPairs) {
   2917                     for (int i = 0; i < mIndexTrackPairs.size(); i++) {
   2918                         Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2919                         if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) {
   2920                             return i;
   2921                         }
   2922                     }
   2923                 }
   2924             }
   2925         }
   2926 
   2927         Parcel request = Parcel.obtain();
   2928         Parcel reply = Parcel.obtain();
   2929         try {
   2930             request.writeInterfaceToken(IMEDIA_PLAYER);
   2931             request.writeInt(INVOKE_ID_GET_SELECTED_TRACK);
   2932             request.writeInt(trackType);
   2933             invoke(request, reply);
   2934             int inbandTrackIndex = reply.readInt();
   2935             synchronized (mIndexTrackPairs) {
   2936                 for (int i = 0; i < mIndexTrackPairs.size(); i++) {
   2937                     Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2938                     if (p.first != null && p.first == inbandTrackIndex) {
   2939                         return i;
   2940                     }
   2941                 }
   2942             }
   2943             return -1;
   2944         } finally {
   2945             request.recycle();
   2946             reply.recycle();
   2947         }
   2948     }
   2949 
   2950     /**
   2951      * Selects a track.
   2952      * <p>
   2953      * If a MediaPlayer is in invalid state, it throws an IllegalStateException exception.
   2954      * If a MediaPlayer is in <em>Started</em> state, the selected track is presented immediately.
   2955      * If a MediaPlayer is not in Started state, it just marks the track to be played.
   2956      * </p>
   2957      * <p>
   2958      * In any valid state, if it is called multiple times on the same type of track (ie. Video,
   2959      * Audio, Timed Text), the most recent one will be chosen.
   2960      * </p>
   2961      * <p>
   2962      * The first audio and video tracks are selected by default if available, even though
   2963      * this method is not called. However, no timed text track will be selected until
   2964      * this function is called.
   2965      * </p>
   2966      * <p>
   2967      * Currently, only timed text tracks or audio tracks can be selected via this method.
   2968      * In addition, the support for selecting an audio track at runtime is pretty limited
   2969      * in that an audio track can only be selected in the <em>Prepared</em> state.
   2970      * </p>
   2971      * @param index the index of the track to be selected. The valid range of the index
   2972      * is 0..total number of track - 1. The total number of tracks as well as the type of
   2973      * each individual track can be found by calling {@link #getTrackInfo()} method.
   2974      * @throws IllegalStateException if called in an invalid state.
   2975      *
   2976      * @see android.media.MediaPlayer#getTrackInfo
   2977      */
   2978     public void selectTrack(int index) throws IllegalStateException {
   2979         selectOrDeselectTrack(index, true /* select */);
   2980     }
   2981 
   2982     /**
   2983      * Deselect a track.
   2984      * <p>
   2985      * Currently, the track must be a timed text track and no audio or video tracks can be
   2986      * deselected. If the timed text track identified by index has not been
   2987      * selected before, it throws an exception.
   2988      * </p>
   2989      * @param index the index of the track to be deselected. The valid range of the index
   2990      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
   2991      * each individual track can be found by calling {@link #getTrackInfo()} method.
   2992      * @throws IllegalStateException if called in an invalid state.
   2993      *
   2994      * @see android.media.MediaPlayer#getTrackInfo
   2995      */
   2996     public void deselectTrack(int index) throws IllegalStateException {
   2997         selectOrDeselectTrack(index, false /* select */);
   2998     }
   2999 
   3000     private void selectOrDeselectTrack(int index, boolean select)
   3001             throws IllegalStateException {
   3002         // handle subtitle track through subtitle controller
   3003         populateInbandTracks();
   3004 
   3005         Pair<Integer,SubtitleTrack> p = null;
   3006         try {
   3007             p = mIndexTrackPairs.get(index);
   3008         } catch (ArrayIndexOutOfBoundsException e) {
   3009             // ignore bad index
   3010             return;
   3011         }
   3012 
   3013         SubtitleTrack track = p.second;
   3014         if (track == null) {
   3015             // inband (de)select
   3016             selectOrDeselectInbandTrack(p.first, select);
   3017             return;
   3018         }
   3019 
   3020         if (mSubtitleController == null) {
   3021             return;
   3022         }
   3023 
   3024         if (!select) {
   3025             // out-of-band deselect
   3026             if (mSubtitleController.getSelectedTrack() == track) {
   3027                 mSubtitleController.selectTrack(null);
   3028             } else {
   3029                 Log.w(TAG, "trying to deselect track that was not selected");
   3030             }
   3031             return;
   3032         }
   3033 
   3034         // out-of-band select
   3035         if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
   3036             int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
   3037             synchronized (mIndexTrackPairs) {
   3038                 if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) {
   3039                     Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex);
   3040                     if (p2.first != null && p2.second == null) {
   3041                         // deselect inband counterpart
   3042                         selectOrDeselectInbandTrack(p2.first, false);
   3043                     }
   3044                 }
   3045             }
   3046         }
   3047         mSubtitleController.selectTrack(track);
   3048     }
   3049 
   3050     private void selectOrDeselectInbandTrack(int index, boolean select)
   3051             throws IllegalStateException {
   3052         Parcel request = Parcel.obtain();
   3053         Parcel reply = Parcel.obtain();
   3054         try {
   3055             request.writeInterfaceToken(IMEDIA_PLAYER);
   3056             request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK);
   3057             request.writeInt(index);
   3058             invoke(request, reply);
   3059         } finally {
   3060             request.recycle();
   3061             reply.recycle();
   3062         }
   3063     }
   3064 
   3065 
   3066     /**
   3067      * @param reply Parcel with audio/video duration info for battery
   3068                     tracking usage
   3069      * @return The status code.
   3070      * {@hide}
   3071      */
   3072     public native static int native_pullBatteryData(Parcel reply);
   3073 
   3074     /**
   3075      * Sets the target UDP re-transmit endpoint for the low level player.
   3076      * Generally, the address portion of the endpoint is an IP multicast
   3077      * address, although a unicast address would be equally valid.  When a valid
   3078      * retransmit endpoint has been set, the media player will not decode and
   3079      * render the media presentation locally.  Instead, the player will attempt
   3080      * to re-multiplex its media data using the Android@Home RTP profile and
   3081      * re-transmit to the target endpoint.  Receiver devices (which may be
   3082      * either the same as the transmitting device or different devices) may
   3083      * instantiate, prepare, and start a receiver player using a setDataSource
   3084      * URL of the form...
   3085      *
   3086      * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
   3087      *
   3088      * to receive, decode and render the re-transmitted content.
   3089      *
   3090      * setRetransmitEndpoint may only be called before setDataSource has been
   3091      * called; while the player is in the Idle state.
   3092      *
   3093      * @param endpoint the address and UDP port of the re-transmission target or
   3094      * null if no re-transmission is to be performed.
   3095      * @throws IllegalStateException if it is called in an invalid state
   3096      * @throws IllegalArgumentException if the retransmit endpoint is supplied,
   3097      * but invalid.
   3098      *
   3099      * {@hide} pending API council
   3100      */
   3101     public void setRetransmitEndpoint(InetSocketAddress endpoint)
   3102             throws IllegalStateException, IllegalArgumentException
   3103     {
   3104         String addrString = null;
   3105         int port = 0;
   3106 
   3107         if (null != endpoint) {
   3108             addrString = endpoint.getAddress().getHostAddress();
   3109             port = endpoint.getPort();
   3110         }
   3111 
   3112         int ret = native_setRetransmitEndpoint(addrString, port);
   3113         if (ret != 0) {
   3114             throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
   3115         }
   3116     }
   3117 
   3118     private native final int native_setRetransmitEndpoint(String addrString, int port);
   3119 
   3120     @Override
   3121     protected void finalize() {
   3122         baseRelease();
   3123         native_finalize();
   3124     }
   3125 
   3126     /* Do not change these values without updating their counterparts
   3127      * in include/media/mediaplayer.h!
   3128      */
   3129     private static final int MEDIA_NOP = 0; // interface test message
   3130     private static final int MEDIA_PREPARED = 1;
   3131     private static final int MEDIA_PLAYBACK_COMPLETE = 2;
   3132     private static final int MEDIA_BUFFERING_UPDATE = 3;
   3133     private static final int MEDIA_SEEK_COMPLETE = 4;
   3134     private static final int MEDIA_SET_VIDEO_SIZE = 5;
   3135     private static final int MEDIA_STARTED = 6;
   3136     private static final int MEDIA_PAUSED = 7;
   3137     private static final int MEDIA_STOPPED = 8;
   3138     private static final int MEDIA_SKIPPED = 9;
   3139     private static final int MEDIA_TIMED_TEXT = 99;
   3140     private static final int MEDIA_ERROR = 100;
   3141     private static final int MEDIA_INFO = 200;
   3142     private static final int MEDIA_SUBTITLE_DATA = 201;
   3143     private static final int MEDIA_META_DATA = 202;
   3144     private static final int MEDIA_DRM_INFO = 210;
   3145 
   3146     private TimeProvider mTimeProvider;
   3147 
   3148     /** @hide */
   3149     public MediaTimeProvider getMediaTimeProvider() {
   3150         if (mTimeProvider == null) {
   3151             mTimeProvider = new TimeProvider(this);
   3152         }
   3153         return mTimeProvider;
   3154     }
   3155 
   3156     private class EventHandler extends Handler
   3157     {
   3158         private MediaPlayer mMediaPlayer;
   3159 
   3160         public EventHandler(MediaPlayer mp, Looper looper) {
   3161             super(looper);
   3162             mMediaPlayer = mp;
   3163         }
   3164 
   3165         @Override
   3166         public void handleMessage(Message msg) {
   3167             if (mMediaPlayer.mNativeContext == 0) {
   3168                 Log.w(TAG, "mediaplayer went away with unhandled events");
   3169                 return;
   3170             }
   3171             switch(msg.what) {
   3172             case MEDIA_PREPARED:
   3173                 try {
   3174                     scanInternalSubtitleTracks();
   3175                 } catch (RuntimeException e) {
   3176                     // send error message instead of crashing;
   3177                     // send error message instead of inlining a call to onError
   3178                     // to avoid code duplication.
   3179                     Message msg2 = obtainMessage(
   3180                             MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
   3181                     sendMessage(msg2);
   3182                 }
   3183 
   3184                 OnPreparedListener onPreparedListener = mOnPreparedListener;
   3185                 if (onPreparedListener != null)
   3186                     onPreparedListener.onPrepared(mMediaPlayer);
   3187                 return;
   3188 
   3189             case MEDIA_DRM_INFO:
   3190                 Log.v(TAG, "MEDIA_DRM_INFO " + mOnDrmInfoHandlerDelegate);
   3191 
   3192                 if (msg.obj == null) {
   3193                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj=NULL");
   3194                 } else if (msg.obj instanceof Parcel) {
   3195                     // The parcel was parsed already in postEventFromNative
   3196                     DrmInfo drmInfo = null;
   3197 
   3198                     OnDrmInfoHandlerDelegate onDrmInfoHandlerDelegate;
   3199                     synchronized (mDrmLock) {
   3200                         if (mOnDrmInfoHandlerDelegate != null && mDrmInfo != null) {
   3201                             drmInfo = mDrmInfo.makeCopy();
   3202                         }
   3203                         // local copy while keeping the lock
   3204                         onDrmInfoHandlerDelegate = mOnDrmInfoHandlerDelegate;
   3205                     }
   3206 
   3207                     // notifying the client outside the lock
   3208                     if (onDrmInfoHandlerDelegate != null) {
   3209                         onDrmInfoHandlerDelegate.notifyClient(drmInfo);
   3210                     }
   3211                 } else {
   3212                     Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + msg.obj);
   3213                 }
   3214                 return;
   3215 
   3216             case MEDIA_PLAYBACK_COMPLETE:
   3217                 {
   3218                     mOnCompletionInternalListener.onCompletion(mMediaPlayer);
   3219                     OnCompletionListener onCompletionListener = mOnCompletionListener;
   3220                     if (onCompletionListener != null)
   3221                         onCompletionListener.onCompletion(mMediaPlayer);
   3222                 }
   3223                 stayAwake(false);
   3224                 return;
   3225 
   3226             case MEDIA_STOPPED:
   3227                 {
   3228                     TimeProvider timeProvider = mTimeProvider;
   3229                     if (timeProvider != null) {
   3230                         timeProvider.onStopped();
   3231                     }
   3232                 }
   3233                 break;
   3234 
   3235             case MEDIA_STARTED:
   3236             case MEDIA_PAUSED:
   3237                 {
   3238                     TimeProvider timeProvider = mTimeProvider;
   3239                     if (timeProvider != null) {
   3240                         timeProvider.onPaused(msg.what == MEDIA_PAUSED);
   3241                     }
   3242                 }
   3243                 break;
   3244 
   3245             case MEDIA_BUFFERING_UPDATE:
   3246                 OnBufferingUpdateListener onBufferingUpdateListener = mOnBufferingUpdateListener;
   3247                 if (onBufferingUpdateListener != null)
   3248                     onBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
   3249                 return;
   3250 
   3251             case MEDIA_SEEK_COMPLETE:
   3252                 OnSeekCompleteListener onSeekCompleteListener = mOnSeekCompleteListener;
   3253                 if (onSeekCompleteListener != null) {
   3254                     onSeekCompleteListener.onSeekComplete(mMediaPlayer);
   3255                 }
   3256                 // fall through
   3257 
   3258             case MEDIA_SKIPPED:
   3259                 {
   3260                     TimeProvider timeProvider = mTimeProvider;
   3261                     if (timeProvider != null) {
   3262                         timeProvider.onSeekComplete(mMediaPlayer);
   3263                     }
   3264                 }
   3265                 return;
   3266 
   3267             case MEDIA_SET_VIDEO_SIZE:
   3268                 OnVideoSizeChangedListener onVideoSizeChangedListener = mOnVideoSizeChangedListener;
   3269                 if (onVideoSizeChangedListener != null) {
   3270                     onVideoSizeChangedListener.onVideoSizeChanged(
   3271                         mMediaPlayer, msg.arg1, msg.arg2);
   3272                 }
   3273                 return;
   3274 
   3275             case MEDIA_ERROR:
   3276                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
   3277                 boolean error_was_handled = false;
   3278                 OnErrorListener onErrorListener = mOnErrorListener;
   3279                 if (onErrorListener != null) {
   3280                     error_was_handled = onErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
   3281                 }
   3282                 {
   3283                     mOnCompletionInternalListener.onCompletion(mMediaPlayer);
   3284                     OnCompletionListener onCompletionListener = mOnCompletionListener;
   3285                     if (onCompletionListener != null && ! error_was_handled) {
   3286                         onCompletionListener.onCompletion(mMediaPlayer);
   3287                     }
   3288                 }
   3289                 stayAwake(false);
   3290                 return;
   3291 
   3292             case MEDIA_INFO:
   3293                 switch (msg.arg1) {
   3294                 case MEDIA_INFO_VIDEO_TRACK_LAGGING:
   3295                     Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
   3296                     break;
   3297                 case MEDIA_INFO_METADATA_UPDATE:
   3298                     try {
   3299                         scanInternalSubtitleTracks();
   3300                     } catch (RuntimeException e) {
   3301                         Message msg2 = obtainMessage(
   3302                                 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
   3303                         sendMessage(msg2);
   3304                     }
   3305                     // fall through
   3306 
   3307                 case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
   3308                     msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
   3309                     // update default track selection
   3310                     if (mSubtitleController != null) {
   3311                         mSubtitleController.selectDefaultTrack();
   3312                     }
   3313                     break;
   3314                 case MEDIA_INFO_BUFFERING_START:
   3315                 case MEDIA_INFO_BUFFERING_END:
   3316                     TimeProvider timeProvider = mTimeProvider;
   3317                     if (timeProvider != null) {
   3318                         timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
   3319                     }
   3320                     break;
   3321                 }
   3322 
   3323                 OnInfoListener onInfoListener = mOnInfoListener;
   3324                 if (onInfoListener != null) {
   3325                     onInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
   3326                 }
   3327                 // No real default action so far.
   3328                 return;
   3329             case MEDIA_TIMED_TEXT:
   3330                 OnTimedTextListener onTimedTextListener = mOnTimedTextListener;
   3331                 if (onTimedTextListener == null)
   3332                     return;
   3333                 if (msg.obj == null) {
   3334                     onTimedTextListener.onTimedText(mMediaPlayer, null);
   3335                 } else {
   3336                     if (msg.obj instanceof Parcel) {
   3337                         Parcel parcel = (Parcel)msg.obj;
   3338                         TimedText text = new TimedText(parcel);
   3339                         parcel.recycle();
   3340                         onTimedTextListener.onTimedText(mMediaPlayer, text);
   3341                     }
   3342                 }
   3343                 return;
   3344 
   3345             case MEDIA_SUBTITLE_DATA:
   3346                 OnSubtitleDataListener onSubtitleDataListener = mOnSubtitleDataListener;
   3347                 if (onSubtitleDataListener == null) {
   3348                     return;
   3349                 }
   3350                 if (msg.obj instanceof Parcel) {
   3351                     Parcel parcel = (Parcel) msg.obj;
   3352                     SubtitleData data = new SubtitleData(parcel);
   3353                     parcel.recycle();
   3354                     onSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
   3355                 }
   3356                 return;
   3357 
   3358             case MEDIA_META_DATA:
   3359                 OnTimedMetaDataAvailableListener onTimedMetaDataAvailableListener =
   3360                     mOnTimedMetaDataAvailableListener;
   3361                 if (onTimedMetaDataAvailableListener == null) {
   3362                     return;
   3363                 }
   3364                 if (msg.obj instanceof Parcel) {
   3365                     Parcel parcel = (Parcel) msg.obj;
   3366                     TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
   3367                     parcel.recycle();
   3368                     onTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
   3369                 }
   3370                 return;
   3371 
   3372             case MEDIA_NOP: // interface test message - ignore
   3373                 break;
   3374 
   3375             default:
   3376                 Log.e(TAG, "Unknown message type " + msg.what);
   3377                 return;
   3378             }
   3379         }
   3380     }
   3381 
   3382     /*
   3383      * Called from native code when an interesting event happens.  This method
   3384      * just uses the EventHandler system to post the event back to the main app thread.
   3385      * We use a weak reference to the original MediaPlayer object so that the native
   3386      * code is safe from the object disappearing from underneath it.  (This is
   3387      * the cookie passed to native_setup().)
   3388      */
   3389     private static void postEventFromNative(Object mediaplayer_ref,
   3390                                             int what, int arg1, int arg2, Object obj)
   3391     {
   3392         MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
   3393         if (mp == null) {
   3394             return;
   3395         }
   3396 
   3397         switch (what) {
   3398         case MEDIA_INFO:
   3399             if (arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
   3400                 // this acquires the wakelock if needed, and sets the client side state
   3401                 mp.start();
   3402             }
   3403             break;
   3404 
   3405         case MEDIA_DRM_INFO:
   3406             // We need to derive mDrmInfo before prepare() returns so processing it here
   3407             // before the notification is sent to EventHandler below. EventHandler runs in the
   3408             // notification looper so its handleMessage might process the event after prepare()
   3409             // has returned.
   3410             Log.v(TAG, "postEventFromNative MEDIA_DRM_INFO");
   3411             if (obj instanceof Parcel) {
   3412                 Parcel parcel = (Parcel)obj;
   3413                 DrmInfo drmInfo = new DrmInfo(parcel);
   3414                 synchronized (mp.mDrmLock) {
   3415                     mp.mDrmInfo = drmInfo;
   3416                 }
   3417             } else {
   3418                 Log.w(TAG, "MEDIA_DRM_INFO msg.obj of unexpected type " + obj);
   3419             }
   3420             break;
   3421 
   3422         case MEDIA_PREPARED:
   3423             // By this time, we've learned about DrmInfo's presence or absence. This is meant
   3424             // mainly for prepareAsync() use case. For prepare(), this still can run to a race
   3425             // condition b/c MediaPlayerNative releases the prepare() lock before calling notify
   3426             // so we also set mDrmInfoResolved in prepare().
   3427             synchronized (mp.mDrmLock) {
   3428                 mp.mDrmInfoResolved = true;
   3429             }
   3430             break;
   3431 
   3432         }
   3433 
   3434         if (mp.mEventHandler != null) {
   3435             Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
   3436             mp.mEventHandler.sendMessage(m);
   3437         }
   3438     }
   3439 
   3440     /**
   3441      * Interface definition for a callback to be invoked when the media
   3442      * source is ready for playback.
   3443      */
   3444     public interface OnPreparedListener
   3445     {
   3446         /**
   3447          * Called when the media file is ready for playback.
   3448          *
   3449          * @param mp the MediaPlayer that is ready for playback
   3450          */
   3451         void onPrepared(MediaPlayer mp);
   3452     }
   3453 
   3454     /**
   3455      * Register a callback to be invoked when the media source is ready
   3456      * for playback.
   3457      *
   3458      * @param listener the callback that will be run
   3459      */
   3460     public void setOnPreparedListener(OnPreparedListener listener)
   3461     {
   3462         mOnPreparedListener = listener;
   3463     }
   3464 
   3465     private OnPreparedListener mOnPreparedListener;
   3466 
   3467     /**
   3468      * Interface definition for a callback to be invoked when playback of
   3469      * a media source has completed.
   3470      */
   3471     public interface OnCompletionListener
   3472     {
   3473         /**
   3474          * Called when the end of a media source is reached during playback.
   3475          *
   3476          * @param mp the MediaPlayer that reached the end of the file
   3477          */
   3478         void onCompletion(MediaPlayer mp);
   3479     }
   3480 
   3481     /**
   3482      * Register a callback to be invoked when the end of a media source
   3483      * has been reached during playback.
   3484      *
   3485      * @param listener the callback that will be run
   3486      */
   3487     public void setOnCompletionListener(OnCompletionListener listener)
   3488     {
   3489         mOnCompletionListener = listener;
   3490     }
   3491 
   3492     private OnCompletionListener mOnCompletionListener;
   3493 
   3494     /**
   3495      * @hide
   3496      * Internal completion listener to update PlayerBase of the play state. Always "registered".
   3497      */
   3498     private final OnCompletionListener mOnCompletionInternalListener = new OnCompletionListener() {
   3499         @Override
   3500         public void onCompletion(MediaPlayer mp) {
   3501             baseStop();
   3502         }
   3503     };
   3504 
   3505     /**
   3506      * Interface definition of a callback to be invoked indicating buffering
   3507      * status of a media resource being streamed over the network.
   3508      */
   3509     public interface OnBufferingUpdateListener
   3510     {
   3511         /**
   3512          * Called to update status in buffering a media stream received through
   3513          * progressive HTTP download. The received buffering percentage
   3514          * indicates how much of the content has been buffered or played.
   3515          * For example a buffering update of 80 percent when half the content
   3516          * has already been played indicates that the next 30 percent of the
   3517          * content to play has been buffered.
   3518          *
   3519          * @param mp      the MediaPlayer the update pertains to
   3520          * @param percent the percentage (0-100) of the content
   3521          *                that has been buffered or played thus far
   3522          */
   3523         void onBufferingUpdate(MediaPlayer mp, int percent);
   3524     }
   3525 
   3526     /**
   3527      * Register a callback to be invoked when the status of a network
   3528      * stream's buffer has changed.
   3529      *
   3530      * @param listener the callback that will be run.
   3531      */
   3532     public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener)
   3533     {
   3534         mOnBufferingUpdateListener = listener;
   3535     }
   3536 
   3537     private OnBufferingUpdateListener mOnBufferingUpdateListener;
   3538 
   3539     /**
   3540      * Interface definition of a callback to be invoked indicating
   3541      * the completion of a seek operation.
   3542      */
   3543     public interface OnSeekCompleteListener
   3544     {
   3545         /**
   3546          * Called to indicate the completion of a seek operation.
   3547          *
   3548          * @param mp the MediaPlayer that issued the seek operation
   3549          */
   3550         public void onSeekComplete(MediaPlayer mp);
   3551     }
   3552 
   3553     /**
   3554      * Register a callback to be invoked when a seek operation has been
   3555      * completed.
   3556      *
   3557      * @param listener the callback that will be run
   3558      */
   3559     public void setOnSeekCompleteListener(OnSeekCompleteListener listener)
   3560     {
   3561         mOnSeekCompleteListener = listener;
   3562     }
   3563 
   3564     private OnSeekCompleteListener mOnSeekCompleteListener;
   3565 
   3566     /**
   3567      * Interface definition of a callback to be invoked when the
   3568      * video size is first known or updated
   3569      */
   3570     public interface OnVideoSizeChangedListener
   3571     {
   3572         /**
   3573          * Called to indicate the video size
   3574          *
   3575          * The video size (width and height) could be 0 if there was no video,
   3576          * no display surface was set, or the value was not determined yet.
   3577          *
   3578          * @param mp        the MediaPlayer associated with this callback
   3579          * @param width     the width of the video
   3580          * @param height    the height of the video
   3581          */
   3582         public void onVideoSizeChanged(MediaPlayer mp, int width, int height);
   3583     }
   3584 
   3585     /**
   3586      * Register a callback to be invoked when the video size is
   3587      * known or updated.
   3588      *
   3589      * @param listener the callback that will be run
   3590      */
   3591     public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener)
   3592     {
   3593         mOnVideoSizeChangedListener = listener;
   3594     }
   3595 
   3596     private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
   3597 
   3598     /**
   3599      * Interface definition of a callback to be invoked when a
   3600      * timed text is available for display.
   3601      */
   3602     public interface OnTimedTextListener
   3603     {
   3604         /**
   3605          * Called to indicate an avaliable timed text
   3606          *
   3607          * @param mp             the MediaPlayer associated with this callback
   3608          * @param text           the timed text sample which contains the text
   3609          *                       needed to be displayed and the display format.
   3610          */
   3611         public void onTimedText(MediaPlayer mp, TimedText text);
   3612     }
   3613 
   3614     /**
   3615      * Register a callback to be invoked when a timed text is available
   3616      * for display.
   3617      *
   3618      * @param listener the callback that will be run
   3619      */
   3620     public void setOnTimedTextListener(OnTimedTextListener listener)
   3621     {
   3622         mOnTimedTextListener = listener;
   3623     }
   3624 
   3625     private OnTimedTextListener mOnTimedTextListener;
   3626 
   3627     /**
   3628      * Interface definition of a callback to be invoked when a
   3629      * track has data available.
   3630      *
   3631      * @hide
   3632      */
   3633     public interface OnSubtitleDataListener
   3634     {
   3635         public void onSubtitleData(MediaPlayer mp, SubtitleData data);
   3636     }
   3637 
   3638     /**
   3639      * Register a callback to be invoked when a track has data available.
   3640      *
   3641      * @param listener the callback that will be run
   3642      *
   3643      * @hide
   3644      */
   3645     public void setOnSubtitleDataListener(OnSubtitleDataListener listener)
   3646     {
   3647         mOnSubtitleDataListener = listener;
   3648     }
   3649 
   3650     private OnSubtitleDataListener mOnSubtitleDataListener;
   3651 
   3652     /**
   3653      * Interface definition of a callback to be invoked when a
   3654      * track has timed metadata available.
   3655      *
   3656      * @see MediaPlayer#setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener)
   3657      */
   3658     public interface OnTimedMetaDataAvailableListener
   3659     {
   3660         /**
   3661          * Called to indicate avaliable timed metadata
   3662          * <p>
   3663          * This method will be called as timed metadata is extracted from the media,
   3664          * in the same order as it occurs in the media. The timing of this event is
   3665          * not controlled by the associated timestamp.
   3666          *
   3667          * @param mp             the MediaPlayer associated with this callback
   3668          * @param data           the timed metadata sample associated with this event
   3669          */
   3670         public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data);
   3671     }
   3672 
   3673     /**
   3674      * Register a callback to be invoked when a selected track has timed metadata available.
   3675      * <p>
   3676      * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
   3677      * {@link TimedMetaData}.
   3678      *
   3679      * @see MediaPlayer#selectTrack(int)
   3680      * @see MediaPlayer.OnTimedMetaDataAvailableListener
   3681      * @see TimedMetaData
   3682      *
   3683      * @param listener the callback that will be run
   3684      */
   3685     public void setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener listener)
   3686     {
   3687         mOnTimedMetaDataAvailableListener = listener;
   3688     }
   3689 
   3690     private OnTimedMetaDataAvailableListener mOnTimedMetaDataAvailableListener;
   3691 
   3692     /* Do not change these values without updating their counterparts
   3693      * in include/media/mediaplayer.h!
   3694      */
   3695     /** Unspecified media player error.
   3696      * @see android.media.MediaPlayer.OnErrorListener
   3697      */
   3698     public static final int MEDIA_ERROR_UNKNOWN = 1;
   3699 
   3700     /** Media server died. In this case, the application must release the
   3701      * MediaPlayer object and instantiate a new one.
   3702      * @see android.media.MediaPlayer.OnErrorListener
   3703      */
   3704     public static final int MEDIA_ERROR_SERVER_DIED = 100;
   3705 
   3706     /** The video is streamed and its container is not valid for progressive
   3707      * playback i.e the video's index (e.g moov atom) is not at the start of the
   3708      * file.
   3709      * @see android.media.MediaPlayer.OnErrorListener
   3710      */
   3711     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
   3712 
   3713     /** File or network related operation errors. */
   3714     public static final int MEDIA_ERROR_IO = -1004;
   3715     /** Bitstream is not conforming to the related coding standard or file spec. */
   3716     public static final int MEDIA_ERROR_MALFORMED = -1007;
   3717     /** Bitstream is conforming to the related coding standard or file spec, but
   3718      * the media framework does not support the feature. */
   3719     public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
   3720     /** Some operation takes too long to complete, usually more than 3-5 seconds. */
   3721     public static final int MEDIA_ERROR_TIMED_OUT = -110;
   3722 
   3723     /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
   3724      * system/core/include/utils/Errors.h
   3725      * @see android.media.MediaPlayer.OnErrorListener
   3726      * @hide
   3727      */
   3728     public static final int MEDIA_ERROR_SYSTEM = -2147483648;
   3729 
   3730     /**
   3731      * Interface definition of a callback to be invoked when there
   3732      * has been an error during an asynchronous operation (other errors
   3733      * will throw exceptions at method call time).
   3734      */
   3735     public interface OnErrorListener
   3736     {
   3737         /**
   3738          * Called to indicate an error.
   3739          *
   3740          * @param mp      the MediaPlayer the error pertains to
   3741          * @param what    the type of error that has occurred:
   3742          * <ul>
   3743          * <li>{@link #MEDIA_ERROR_UNKNOWN}
   3744          * <li>{@link #MEDIA_ERROR_SERVER_DIED}
   3745          * </ul>
   3746          * @param extra an extra code, specific to the error. Typically
   3747          * implementation dependent.
   3748          * <ul>
   3749          * <li>{@link #MEDIA_ERROR_IO}
   3750          * <li>{@link #MEDIA_ERROR_MALFORMED}
   3751          * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
   3752          * <li>{@link #MEDIA_ERROR_TIMED_OUT}
   3753          * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
   3754          * </ul>
   3755          * @return True if the method handled the error, false if it didn't.
   3756          * Returning false, or not having an OnErrorListener at all, will
   3757          * cause the OnCompletionListener to be called.
   3758          */
   3759         boolean onError(MediaPlayer mp, int what, int extra);
   3760     }
   3761 
   3762     /**
   3763      * Register a callback to be invoked when an error has happened
   3764      * during an asynchronous operation.
   3765      *
   3766      * @param listener the callback that will be run
   3767      */
   3768     public void setOnErrorListener(OnErrorListener listener)
   3769     {
   3770         mOnErrorListener = listener;
   3771     }
   3772 
   3773     private OnErrorListener mOnErrorListener;
   3774 
   3775 
   3776     /* Do not change these values without updating their counterparts
   3777      * in include/media/mediaplayer.h!
   3778      */
   3779     /** Unspecified media player info.
   3780      * @see android.media.MediaPlayer.OnInfoListener
   3781      */
   3782     public static final int MEDIA_INFO_UNKNOWN = 1;
   3783 
   3784     /** The player was started because it was used as the next player for another
   3785      * player, which just completed playback.
   3786      * @see android.media.MediaPlayer.OnInfoListener
   3787      * @hide
   3788      */
   3789     public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
   3790 
   3791     /** The player just pushed the very first video frame for rendering.
   3792      * @see android.media.MediaPlayer.OnInfoListener
   3793      */
   3794     public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
   3795 
   3796     /** The video is too complex for the decoder: it can't decode frames fast
   3797      *  enough. Possibly only the audio plays fine at this stage.
   3798      * @see android.media.MediaPlayer.OnInfoListener
   3799      */
   3800     public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
   3801 
   3802     /** MediaPlayer is temporarily pausing playback internally in order to
   3803      * buffer more data.
   3804      * @see android.media.MediaPlayer.OnInfoListener
   3805      */
   3806     public static final int MEDIA_INFO_BUFFERING_START = 701;
   3807 
   3808     /** MediaPlayer is resuming playback after filling buffers.
   3809      * @see android.media.MediaPlayer.OnInfoListener
   3810      */
   3811     public static final int MEDIA_INFO_BUFFERING_END = 702;
   3812 
   3813     /** Estimated network bandwidth information (kbps) is available; currently this event fires
   3814      * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
   3815      * when playing network files.
   3816      * @see android.media.MediaPlayer.OnInfoListener
   3817      * @hide
   3818      */
   3819     public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
   3820 
   3821     /** Bad interleaving means that a media has been improperly interleaved or
   3822      * not interleaved at all, e.g has all the video samples first then all the
   3823      * audio ones. Video is playing but a lot of disk seeks may be happening.
   3824      * @see android.media.MediaPlayer.OnInfoListener
   3825      */
   3826     public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
   3827 
   3828     /** The media cannot be seeked (e.g live stream)
   3829      * @see android.media.MediaPlayer.OnInfoListener
   3830      */
   3831     public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
   3832 
   3833     /** A new set of metadata is available.
   3834      * @see android.media.MediaPlayer.OnInfoListener
   3835      */
   3836     public static final int MEDIA_INFO_METADATA_UPDATE = 802;
   3837 
   3838     /** A new set of external-only metadata is available.  Used by
   3839      *  JAVA framework to avoid triggering track scanning.
   3840      * @hide
   3841      */
   3842     public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803;
   3843 
   3844     /** Informs that audio is not playing. Note that playback of the video
   3845      * is not interrupted.
   3846      * @see android.media.MediaPlayer.OnInfoListener
   3847      */
   3848     public static final int MEDIA_INFO_AUDIO_NOT_PLAYING = 804;
   3849 
   3850     /** Informs that video is not playing. Note that playback of the audio
   3851      * is not interrupted.
   3852      * @see android.media.MediaPlayer.OnInfoListener
   3853      */
   3854     public static final int MEDIA_INFO_VIDEO_NOT_PLAYING = 805;
   3855 
   3856     /** Failed to handle timed text track properly.
   3857      * @see android.media.MediaPlayer.OnInfoListener
   3858      *
   3859      * {@hide}
   3860      */
   3861     public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
   3862 
   3863     /** Subtitle track was not supported by the media framework.
   3864      * @see android.media.MediaPlayer.OnInfoListener
   3865      */
   3866     public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
   3867 
   3868     /** Reading the subtitle track takes too long.
   3869      * @see android.media.MediaPlayer.OnInfoListener
   3870      */
   3871     public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
   3872 
   3873     /**
   3874      * Interface definition of a callback to be invoked to communicate some
   3875      * info and/or warning about the media or its playback.
   3876      */
   3877     public interface OnInfoListener
   3878     {
   3879         /**
   3880          * Called to indicate an info or a warning.
   3881          *
   3882          * @param mp      the MediaPlayer the info pertains to.
   3883          * @param what    the type of info or warning.
   3884          * <ul>
   3885          * <li>{@link #MEDIA_INFO_UNKNOWN}
   3886          * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
   3887          * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
   3888          * <li>{@link #MEDIA_INFO_BUFFERING_START}
   3889          * <li>{@link #MEDIA_INFO_BUFFERING_END}
   3890          * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> -
   3891          *     bandwidth information is available (as <code>extra</code> kbps)
   3892          * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
   3893          * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
   3894          * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
   3895          * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE}
   3896          * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT}
   3897          * </ul>
   3898          * @param extra an extra code, specific to the info. Typically
   3899          * implementation dependent.
   3900          * @return True if the method handled the info, false if it didn't.
   3901          * Returning false, or not having an OnInfoListener at all, will
   3902          * cause the info to be discarded.
   3903          */
   3904         boolean onInfo(MediaPlayer mp, int what, int extra);
   3905     }
   3906 
   3907     /**
   3908      * Register a callback to be invoked when an info/warning is available.
   3909      *
   3910      * @param listener the callback that will be run
   3911      */
   3912     public void setOnInfoListener(OnInfoListener listener)
   3913     {
   3914         mOnInfoListener = listener;
   3915     }
   3916 
   3917     private OnInfoListener mOnInfoListener;
   3918 
   3919     // Modular DRM begin
   3920 
   3921     /**
   3922      * Interface definition of a callback to be invoked when the app
   3923      * can do DRM configuration (get/set properties) before the session
   3924      * is opened. This facilitates configuration of the properties, like
   3925      * 'securityLevel', which has to be set after DRM scheme creation but
   3926      * before the DRM session is opened.
   3927      *
   3928      * The only allowed DRM calls in this listener are {@code getDrmPropertyString}
   3929      * and {@code setDrmPropertyString}.
   3930      *
   3931      */
   3932     public interface OnDrmConfigHelper
   3933     {
   3934         /**
   3935          * Called to give the app the opportunity to configure DRM before the session is created
   3936          *
   3937          * @param mp the {@code MediaPlayer} associated with this callback
   3938          */
   3939         public void onDrmConfig(MediaPlayer mp);
   3940     }
   3941 
   3942     /**
   3943      * Register a callback to be invoked for configuration of the DRM object before
   3944      * the session is created.
   3945      * The callback will be invoked synchronously during the execution
   3946      * of {@link #prepareDrm(UUID uuid)}.
   3947      *
   3948      * @param listener the callback that will be run
   3949      */
   3950     public void setOnDrmConfigHelper(OnDrmConfigHelper listener)
   3951     {
   3952         synchronized (mDrmLock) {
   3953             mOnDrmConfigHelper = listener;
   3954         } // synchronized
   3955     }
   3956 
   3957     private OnDrmConfigHelper mOnDrmConfigHelper;
   3958 
   3959     /**
   3960      * Interface definition of a callback to be invoked when the
   3961      * DRM info becomes available
   3962      */
   3963     public interface OnDrmInfoListener
   3964     {
   3965         /**
   3966          * Called to indicate DRM info is available
   3967          *
   3968          * @param mp the {@code MediaPlayer} associated with this callback
   3969          * @param drmInfo DRM info of the source including PSSH, and subset
   3970          *                of crypto schemes supported by this device
   3971          */
   3972         public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo);
   3973     }
   3974 
   3975     /**
   3976      * Register a callback to be invoked when the DRM info is
   3977      * known.
   3978      *
   3979      * @param listener the callback that will be run
   3980      */
   3981     public void setOnDrmInfoListener(OnDrmInfoListener listener)
   3982     {
   3983         setOnDrmInfoListener(listener, null);
   3984     }
   3985 
   3986     /**
   3987      * Register a callback to be invoked when the DRM info is
   3988      * known.
   3989      *
   3990      * @param listener the callback that will be run
   3991      */
   3992     public void setOnDrmInfoListener(OnDrmInfoListener listener, Handler handler)
   3993     {
   3994         synchronized (mDrmLock) {
   3995             if (listener != null) {
   3996                 mOnDrmInfoHandlerDelegate = new OnDrmInfoHandlerDelegate(this, listener, handler);
   3997             } else {
   3998                 mOnDrmInfoHandlerDelegate = null;
   3999             }
   4000         } // synchronized
   4001     }
   4002 
   4003     private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate;
   4004 
   4005 
   4006     /**
   4007      * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener.
   4008      * <p>
   4009      *
   4010      * DRM preparation has succeeded.
   4011      */
   4012     public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
   4013 
   4014     /**
   4015      * The device required DRM provisioning but couldn't reach the provisioning server.
   4016      */
   4017     public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
   4018 
   4019     /**
   4020      * The device required DRM provisioning but the provisioning server denied the request.
   4021      */
   4022     public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
   4023 
   4024     /**
   4025      * The DRM preparation has failed .
   4026      */
   4027     public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
   4028 
   4029 
   4030     /** @hide */
   4031     @IntDef({
   4032         PREPARE_DRM_STATUS_SUCCESS,
   4033         PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
   4034         PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
   4035         PREPARE_DRM_STATUS_PREPARATION_ERROR,
   4036     })
   4037     @Retention(RetentionPolicy.SOURCE)
   4038     public @interface PrepareDrmStatusCode {}
   4039 
   4040     /**
   4041      * Interface definition of a callback to notify the app when the
   4042      * DRM is ready for key request/response
   4043      */
   4044     public interface OnDrmPreparedListener
   4045     {
   4046         /**
   4047          * Called to notify the app that prepareDrm is finished and ready for key request/response
   4048          *
   4049          * @param mp the {@code MediaPlayer} associated with this callback
   4050          * @param status the result of DRM preparation which can be
   4051          * {@link #PREPARE_DRM_STATUS_SUCCESS},
   4052          * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR},
   4053          * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or
   4054          * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}.
   4055          */
   4056         public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status);
   4057     }
   4058 
   4059     /**
   4060      * Register a callback to be invoked when the DRM object is prepared.
   4061      *
   4062      * @param listener the callback that will be run
   4063      */
   4064     public void setOnDrmPreparedListener(OnDrmPreparedListener listener)
   4065     {
   4066         setOnDrmPreparedListener(listener, null);
   4067     }
   4068 
   4069     /**
   4070      * Register a callback to be invoked when the DRM object is prepared.
   4071      *
   4072      * @param listener the callback that will be run
   4073      * @param handler the Handler that will receive the callback
   4074      */
   4075     public void setOnDrmPreparedListener(OnDrmPreparedListener listener, Handler handler)
   4076     {
   4077         synchronized (mDrmLock) {
   4078             if (listener != null) {
   4079                 mOnDrmPreparedHandlerDelegate = new OnDrmPreparedHandlerDelegate(this,
   4080                                                             listener, handler);
   4081             } else {
   4082                 mOnDrmPreparedHandlerDelegate = null;
   4083             }
   4084         } // synchronized
   4085     }
   4086 
   4087     private OnDrmPreparedHandlerDelegate mOnDrmPreparedHandlerDelegate;
   4088 
   4089 
   4090     private class OnDrmInfoHandlerDelegate {
   4091         private MediaPlayer mMediaPlayer;
   4092         private OnDrmInfoListener mOnDrmInfoListener;
   4093         private Handler mHandler;
   4094 
   4095         OnDrmInfoHandlerDelegate(MediaPlayer mp, OnDrmInfoListener listener, Handler handler) {
   4096             mMediaPlayer = mp;
   4097             mOnDrmInfoListener = listener;
   4098 
   4099             // find the looper for our new event handler
   4100             if (handler != null) {
   4101                 mHandler = handler;
   4102             } else {
   4103                 // handler == null
   4104                 // Will let OnDrmInfoListener be called in mEventHandler similar to other
   4105                 // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be
   4106                 // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by
   4107                 // mediaserver). As a result, the callback has to be called directly by
   4108                 // EventHandler.handleMessage similar to onPrepared.
   4109             }
   4110         }
   4111 
   4112         void notifyClient(DrmInfo drmInfo) {
   4113             if (mHandler != null) {
   4114                 mHandler.post(new Runnable() {
   4115                     @Override
   4116                     public void run() {
   4117                        mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
   4118                     }
   4119                 });
   4120             }
   4121             else {  // no handler: direct call by mEventHandler
   4122                 mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
   4123             }
   4124         }
   4125     }
   4126 
   4127     private class OnDrmPreparedHandlerDelegate {
   4128         private MediaPlayer mMediaPlayer;
   4129         private OnDrmPreparedListener mOnDrmPreparedListener;
   4130         private Handler mHandler;
   4131 
   4132         OnDrmPreparedHandlerDelegate(MediaPlayer mp, OnDrmPreparedListener listener,
   4133                 Handler handler) {
   4134             mMediaPlayer = mp;
   4135             mOnDrmPreparedListener = listener;
   4136 
   4137             // find the looper for our new event handler
   4138             if (handler != null) {
   4139                 mHandler = handler;
   4140             } else if (mEventHandler != null) {
   4141                 // Otherwise, use mEventHandler
   4142                 mHandler = mEventHandler;
   4143             } else {
   4144                 Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler");
   4145             }
   4146         }
   4147 
   4148         void notifyClient(int status) {
   4149             if (mHandler != null) {
   4150                 mHandler.post(new Runnable() {
   4151                     @Override
   4152                     public void run() {
   4153                         mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status);
   4154                     }
   4155                 });
   4156             } else {
   4157                 Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler");
   4158             }
   4159         }
   4160     }
   4161 
   4162     /**
   4163      * Retrieves the DRM Info associated with the current source
   4164      *
   4165      * @throws IllegalStateException if called before prepare()
   4166      */
   4167     public DrmInfo getDrmInfo()
   4168     {
   4169         DrmInfo drmInfo = null;
   4170 
   4171         // there is not much point if the app calls getDrmInfo within an OnDrmInfoListenet;
   4172         // regardless below returns drmInfo anyway instead of raising an exception
   4173         synchronized (mDrmLock) {
   4174             if (!mDrmInfoResolved && mDrmInfo == null) {
   4175                 final String msg = "The Player has not been prepared yet";
   4176                 Log.v(TAG, msg);
   4177                 throw new IllegalStateException(msg);
   4178             }
   4179 
   4180             if (mDrmInfo != null) {
   4181                 drmInfo = mDrmInfo.makeCopy();
   4182             }
   4183         }   // synchronized
   4184 
   4185         return drmInfo;
   4186     }
   4187 
   4188 
   4189     /**
   4190      * Prepares the DRM for the current source
   4191      * <p>
   4192      * If {@code OnDrmConfigHelper} is registered, it will be called during
   4193      * preparation to allow configuration of the DRM properties before opening the
   4194      * DRM session. Note that the callback is called synchronously in the thread that called
   4195      * {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
   4196      * and {@code setDrmPropertyString} calls and refrain from any lengthy operation.
   4197      * <p>
   4198      * If the device has not been provisioned before, this call also provisions the device
   4199      * which involves accessing the provisioning server and can take a variable time to
   4200      * complete depending on the network connectivity.
   4201      * If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
   4202      * mode by launching the provisioning in the background and returning. The listener
   4203      * will be called when provisioning and preparation has finished. If a
   4204      * {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
   4205      * and preparation has finished, i.e., runs in blocking mode.
   4206      * <p>
   4207      * If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
   4208      * session being ready. The application should not make any assumption about its call
   4209      * sequence (e.g., before or after prepareDrm returns), or the thread context that will
   4210      * execute the listener (unless the listener is registered with a handler thread).
   4211      * <p>
   4212      *
   4213      * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
   4214      * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
   4215      *
   4216      * @throws IllegalStateException              if called before prepare(), or the DRM was
   4217      *                                            prepared already
   4218      * @throws UnsupportedSchemeException         if the crypto scheme is not supported
   4219      * @throws ResourceBusyException              if required DRM resources are in use
   4220      * @throws ProvisioningNetworkErrorException  if provisioning is required but failed due to a
   4221      *                                            network error
   4222      * @throws ProvisioningServerErrorException   if provisioning is required but failed due to
   4223      *                                            the request denied by the provisioning server
   4224      */
   4225     public void prepareDrm(@NonNull UUID uuid)
   4226             throws UnsupportedSchemeException, ResourceBusyException,
   4227                    ProvisioningNetworkErrorException, ProvisioningServerErrorException
   4228     {
   4229         Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
   4230 
   4231         boolean allDoneWithoutProvisioning = false;
   4232         // get a snapshot as we'll use them outside the lock
   4233         OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate = null;
   4234 
   4235         synchronized (mDrmLock) {
   4236 
   4237             // only allowing if tied to a protected source; might relax for releasing offline keys
   4238             if (mDrmInfo == null) {
   4239                 final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
   4240                         "DRM info be retrieved before this call.";
   4241                 Log.e(TAG, msg);
   4242                 throw new IllegalStateException(msg);
   4243             }
   4244 
   4245             if (mActiveDrmScheme) {
   4246                 final String msg = "prepareDrm(): Wrong usage: There is already " +
   4247                         "an active DRM scheme with " + mDrmUUID;
   4248                 Log.e(TAG, msg);
   4249                 throw new IllegalStateException(msg);
   4250             }
   4251 
   4252             if (mPrepareDrmInProgress) {
   4253                 final String msg = "prepareDrm(): Wrong usage: There is already " +
   4254                         "a pending prepareDrm call.";
   4255                 Log.e(TAG, msg);
   4256                 throw new IllegalStateException(msg);
   4257             }
   4258 
   4259             if (mDrmProvisioningInProgress) {
   4260                 final String msg = "prepareDrm(): Unexpectd: Provisioning is already in progress.";
   4261                 Log.e(TAG, msg);
   4262                 throw new IllegalStateException(msg);
   4263             }
   4264 
   4265             // shouldn't need this; just for safeguard
   4266             cleanDrmObj();
   4267 
   4268             mPrepareDrmInProgress = true;
   4269             // local copy while the lock is held
   4270             onDrmPreparedHandlerDelegate = mOnDrmPreparedHandlerDelegate;
   4271 
   4272             try {
   4273                 // only creating the DRM object to allow pre-openSession configuration
   4274                 prepareDrm_createDrmStep(uuid);
   4275             } catch (Exception e) {
   4276                 Log.w(TAG, "prepareDrm(): Exception ", e);
   4277                 mPrepareDrmInProgress = false;
   4278                 throw e;
   4279             }
   4280 
   4281             mDrmConfigAllowed = true;
   4282         }   // synchronized
   4283 
   4284 
   4285         // call the callback outside the lock
   4286         if (mOnDrmConfigHelper != null)  {
   4287             mOnDrmConfigHelper.onDrmConfig(this);
   4288         }
   4289 
   4290         synchronized (mDrmLock) {
   4291             mDrmConfigAllowed = false;
   4292             boolean earlyExit = false;
   4293 
   4294             try {
   4295                 prepareDrm_openSessionStep(uuid);
   4296 
   4297                 mDrmUUID = uuid;
   4298                 mActiveDrmScheme = true;
   4299 
   4300                 allDoneWithoutProvisioning = true;
   4301             } catch (IllegalStateException e) {
   4302                 final String msg = "prepareDrm(): Wrong usage: The player must be " +
   4303                         "in the prepared state to call prepareDrm().";
   4304                 Log.e(TAG, msg);
   4305                 earlyExit = true;
   4306                 throw new IllegalStateException(msg);
   4307             } catch (NotProvisionedException e) {
   4308                 Log.w(TAG, "prepareDrm: NotProvisionedException");
   4309 
   4310                 // handle provisioning internally; it'll reset mPrepareDrmInProgress
   4311                 int result = HandleProvisioninig(uuid);
   4312 
   4313                 // if blocking mode, we're already done;
   4314                 // if non-blocking mode, we attempted to launch background provisioning
   4315                 if (result != PREPARE_DRM_STATUS_SUCCESS) {
   4316                     earlyExit = true;
   4317                     String msg;
   4318 
   4319                     switch (result) {
   4320                     case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
   4321                         msg = "prepareDrm: Provisioning was required but failed " +
   4322                                 "due to a network error.";
   4323                         Log.e(TAG, msg);
   4324                         throw new ProvisioningNetworkErrorException(msg);
   4325 
   4326                     case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
   4327                         msg = "prepareDrm: Provisioning was required but the request " +
   4328                                 "was denied by the server.";
   4329                         Log.e(TAG, msg);
   4330                         throw new ProvisioningServerErrorException(msg);
   4331 
   4332                     case PREPARE_DRM_STATUS_PREPARATION_ERROR:
   4333                     default: // default for safeguard
   4334                         msg = "prepareDrm: Post-provisioning preparation failed.";
   4335                         Log.e(TAG, msg);
   4336                         throw new IllegalStateException(msg);
   4337                     }
   4338                 }
   4339                 // nothing else to do;
   4340                 // if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
   4341             } catch (Exception e) {
   4342                 Log.e(TAG, "prepareDrm: Exception " + e);
   4343                 earlyExit = true;
   4344                 throw e;
   4345             } finally {
   4346                 if (!mDrmProvisioningInProgress) {// if early exit other than provisioning exception
   4347                     mPrepareDrmInProgress = false;
   4348                 }
   4349                 if (earlyExit) {    // cleaning up object if didn't succeed
   4350                     cleanDrmObj();
   4351                 }
   4352             } // finally
   4353         }   // synchronized
   4354 
   4355 
   4356         // if finished successfully without provisioning, call the callback outside the lock
   4357         if (allDoneWithoutProvisioning) {
   4358             if (onDrmPreparedHandlerDelegate != null)
   4359                 onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS);
   4360         }
   4361 
   4362     }
   4363 
   4364 
   4365     private native void _releaseDrm();
   4366 
   4367     /**
   4368      * Releases the DRM session
   4369      * <p>
   4370      * The player has to have an active DRM session and be in stopped, or prepared
   4371      * state before this call is made.
   4372      * A {@code reset()} call will release the DRM session implicitly.
   4373      *
   4374      * @throws NoDrmSchemeException if there is no active DRM session to release
   4375      */
   4376     public void releaseDrm()
   4377             throws NoDrmSchemeException
   4378     {
   4379         Log.v(TAG, "releaseDrm:");
   4380 
   4381         synchronized (mDrmLock) {
   4382             if (!mActiveDrmScheme) {
   4383                 Log.e(TAG, "releaseDrm(): No active DRM scheme to release.");
   4384                 throw new NoDrmSchemeException("releaseDrm: No active DRM scheme to release.");
   4385             }
   4386 
   4387             try {
   4388                 // we don't have the player's state in this layer. The below call raises
   4389                 // exception if we're in a non-stopped/prepared state.
   4390 
   4391                 // for cleaning native/mediaserver crypto object
   4392                 _releaseDrm();
   4393 
   4394                 // for cleaning client-side MediaDrm object; only called if above has succeeded
   4395                 cleanDrmObj();
   4396 
   4397                 mActiveDrmScheme = false;
   4398             } catch (IllegalStateException e) {
   4399                 Log.w(TAG, "releaseDrm: Exception ", e);
   4400                 throw new IllegalStateException("releaseDrm: The player is not in a valid state.");
   4401             } catch (Exception e) {
   4402                 Log.e(TAG, "releaseDrm: Exception ", e);
   4403             }
   4404         }   // synchronized
   4405     }
   4406 
   4407 
   4408     /**
   4409      * A key request/response exchange occurs between the app and a license server
   4410      * to obtain or release keys used to decrypt encrypted content.
   4411      * <p>
   4412      * getKeyRequest() is used to obtain an opaque key request byte array that is
   4413      * delivered to the license server.  The opaque key request byte array is returned
   4414      * in KeyRequest.data.  The recommended URL to deliver the key request to is
   4415      * returned in KeyRequest.defaultUrl.
   4416      * <p>
   4417      * After the app has received the key request response from the server,
   4418      * it should deliver to the response to the DRM engine plugin using the method
   4419      * {@link #provideKeyResponse}.
   4420      *
   4421      * @param keySetId is the key-set identifier of the offline keys being released when keyType is
   4422      * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
   4423      * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}.
   4424      *
   4425      * @param initData is the container-specific initialization data when the keyType is
   4426      * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is
   4427      * interpreted based on the mime type provided in the mimeType parameter.  It could
   4428      * contain, for example, the content ID, key ID or other data obtained from the content
   4429      * metadata that is required in generating the key request.
   4430      * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null.
   4431      *
   4432      * @param mimeType identifies the mime type of the content
   4433      *
   4434      * @param keyType specifies the type of the request. The request may be to acquire
   4435      * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content
   4436      * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired
   4437      * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId.
   4438      *
   4439      * @param optionalParameters are included in the key request message to
   4440      * allow a client application to provide additional message parameters to the server.
   4441      * This may be {@code null} if no additional parameters are to be sent.
   4442      *
   4443      * @throws NoDrmSchemeException if there is no active DRM session
   4444      */
   4445     @NonNull
   4446     public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
   4447             @Nullable String mimeType, @MediaDrm.KeyType int keyType,
   4448             @Nullable Map<String, String> optionalParameters)
   4449             throws NoDrmSchemeException
   4450     {
   4451         Log.v(TAG, "getKeyRequest: " +
   4452                 " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
   4453                 " keyType: " + keyType + " optionalParameters: " + optionalParameters);
   4454 
   4455         synchronized (mDrmLock) {
   4456             if (!mActiveDrmScheme) {
   4457                 Log.e(TAG, "getKeyRequest NoDrmSchemeException");
   4458                 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first.");
   4459             }
   4460 
   4461             try {
   4462                 byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
   4463                         mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
   4464                         keySetId;       // keySetId for KEY_TYPE_RELEASE
   4465 
   4466                 HashMap<String, String> hmapOptionalParameters =
   4467                                                 (optionalParameters != null) ?
   4468                                                 new HashMap<String, String>(optionalParameters) :
   4469                                                 null;
   4470 
   4471                 MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
   4472                                                               keyType, hmapOptionalParameters);
   4473                 Log.v(TAG, "getKeyRequest:   --> request: " + request);
   4474 
   4475                 return request;
   4476 
   4477             } catch (NotProvisionedException e) {
   4478                 Log.w(TAG, "getKeyRequest NotProvisionedException: " +
   4479                         "Unexpected. Shouldn't have reached here.");
   4480                 throw new IllegalStateException("getKeyRequest: Unexpected provisioning error.");
   4481             } catch (Exception e) {
   4482                 Log.w(TAG, "getKeyRequest Exception " + e);
   4483                 throw e;
   4484             }
   4485 
   4486         }   // synchronized
   4487     }
   4488 
   4489 
   4490     /**
   4491      * A key response is received from the license server by the app, then it is
   4492      * provided to the DRM engine plugin using provideKeyResponse. When the
   4493      * response is for an offline key request, a key-set identifier is returned that
   4494      * can be used to later restore the keys to a new session with the method
   4495      * {@ link # restoreKeys}.
   4496      * When the response is for a streaming or release request, null is returned.
   4497      *
   4498      * @param keySetId When the response is for a release request, keySetId identifies
   4499      * the saved key associated with the release request (i.e., the same keySetId
   4500      * passed to the earlier {@ link # getKeyRequest} call. It MUST be null when the
   4501      * response is for either streaming or offline key requests.
   4502      *
   4503      * @param response the byte array response from the server
   4504      *
   4505      * @throws NoDrmSchemeException if there is no active DRM session
   4506      * @throws DeniedByServerException if the response indicates that the
   4507      * server rejected the request
   4508      */
   4509     public byte[] provideKeyResponse(@Nullable byte[] keySetId, @NonNull byte[] response)
   4510             throws NoDrmSchemeException, DeniedByServerException
   4511     {
   4512         Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response);
   4513 
   4514         synchronized (mDrmLock) {
   4515 
   4516             if (!mActiveDrmScheme) {
   4517                 Log.e(TAG, "getKeyRequest NoDrmSchemeException");
   4518                 throw new NoDrmSchemeException("getKeyRequest: Has to set a DRM scheme first.");
   4519             }
   4520 
   4521             try {
   4522                 byte[] scope = (keySetId == null) ?
   4523                                 mDrmSessionId :     // sessionId for KEY_TYPE_STREAMING/OFFLINE
   4524                                 keySetId;           // keySetId for KEY_TYPE_RELEASE
   4525 
   4526                 byte[] keySetResult = mDrmObj.provideKeyResponse(scope, response);
   4527 
   4528                 Log.v(TAG, "provideKeyResponse: keySetId: " + keySetId + " response: " + response +
   4529                         " --> " + keySetResult);
   4530 
   4531 
   4532                 return keySetResult;
   4533 
   4534             } catch (NotProvisionedException e) {
   4535                 Log.w(TAG, "provideKeyResponse NotProvisionedException: " +
   4536                         "Unexpected. Shouldn't have reached here.");
   4537                 throw new IllegalStateException("provideKeyResponse: " +
   4538                         "Unexpected provisioning error.");
   4539             } catch (Exception e) {
   4540                 Log.w(TAG, "provideKeyResponse Exception " + e);
   4541                 throw e;
   4542             }
   4543         }   // synchronized
   4544     }
   4545 
   4546 
   4547     /**
   4548      * Restore persisted offline keys into a new session.  keySetId identifies the
   4549      * keys to load, obtained from a prior call to {@link #provideKeyResponse}.
   4550      *
   4551      * @param keySetId identifies the saved key set to restore
   4552      */
   4553     public void restoreKeys(@NonNull byte[] keySetId)
   4554             throws NoDrmSchemeException
   4555     {
   4556         Log.v(TAG, "restoreKeys: keySetId: " + keySetId);
   4557 
   4558         synchronized (mDrmLock) {
   4559 
   4560             if (!mActiveDrmScheme) {
   4561                 Log.w(TAG, "restoreKeys NoDrmSchemeException");
   4562                 throw new NoDrmSchemeException("restoreKeys: Has to set a DRM scheme first.");
   4563             }
   4564 
   4565             try {
   4566                 mDrmObj.restoreKeys(mDrmSessionId, keySetId);
   4567             } catch (Exception e) {
   4568                 Log.w(TAG, "restoreKeys Exception " + e);
   4569                 throw e;
   4570             }
   4571 
   4572         }   // synchronized
   4573     }
   4574 
   4575 
   4576     /**
   4577      * Read a DRM engine plugin String property value, given the property name string.
   4578      * <p>
   4579      * @param propertyName the property name
   4580      *
   4581      * Standard fields names are:
   4582      * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
   4583      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
   4584      */
   4585     @NonNull
   4586     public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
   4587             throws NoDrmSchemeException
   4588     {
   4589         Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName);
   4590 
   4591         String value;
   4592         synchronized (mDrmLock) {
   4593 
   4594             if (!mActiveDrmScheme && !mDrmConfigAllowed) {
   4595                 Log.w(TAG, "getDrmPropertyString NoDrmSchemeException");
   4596                 throw new NoDrmSchemeException("getDrmPropertyString: Has to prepareDrm() first.");
   4597             }
   4598 
   4599             try {
   4600                 value = mDrmObj.getPropertyString(propertyName);
   4601             } catch (Exception e) {
   4602                 Log.w(TAG, "getDrmPropertyString Exception " + e);
   4603                 throw e;
   4604             }
   4605         }   // synchronized
   4606 
   4607         Log.v(TAG, "getDrmPropertyString: propertyName: " + propertyName + " --> value: " + value);
   4608 
   4609         return value;
   4610     }
   4611 
   4612 
   4613     /**
   4614      * Set a DRM engine plugin String property value.
   4615      * <p>
   4616      * @param propertyName the property name
   4617      * @param value the property value
   4618      *
   4619      * Standard fields names are:
   4620      * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
   4621      * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
   4622      */
   4623     public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName,
   4624                                      @NonNull String value)
   4625             throws NoDrmSchemeException
   4626     {
   4627         Log.v(TAG, "setDrmPropertyString: propertyName: " + propertyName + " value: " + value);
   4628 
   4629         synchronized (mDrmLock) {
   4630 
   4631             if ( !mActiveDrmScheme && !mDrmConfigAllowed ) {
   4632                 Log.w(TAG, "setDrmPropertyString NoDrmSchemeException");
   4633                 throw new NoDrmSchemeException("setDrmPropertyString: Has to prepareDrm() first.");
   4634             }
   4635 
   4636             try {
   4637                 mDrmObj.setPropertyString(propertyName, value);
   4638             } catch ( Exception e ) {
   4639                 Log.w(TAG, "setDrmPropertyString Exception " + e);
   4640                 throw e;
   4641             }
   4642         }   // synchronized
   4643     }
   4644 
   4645     /**
   4646      * Encapsulates the DRM properties of the source.
   4647      */
   4648     public static final class DrmInfo {
   4649         private Map<UUID, byte[]> mapPssh;
   4650         private UUID[] supportedSchemes;
   4651 
   4652         /**
   4653          * Returns the PSSH info of the data source for each supported DRM scheme.
   4654          */
   4655         public Map<UUID, byte[]> getPssh() {
   4656             return mapPssh;
   4657         }
   4658 
   4659         /**
   4660          * Returns the intersection of the data source and the device DRM schemes.
   4661          * It effectively identifies the subset of the source's DRM schemes which
   4662          * are supported by the device too.
   4663          */
   4664         public UUID[] getSupportedSchemes() {
   4665             return supportedSchemes;
   4666         }
   4667 
   4668         private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) {
   4669             mapPssh = Pssh;
   4670             supportedSchemes = SupportedSchemes;
   4671         }
   4672 
   4673         private DrmInfo(Parcel parcel) {
   4674             Log.v(TAG, "DrmInfo(" + parcel + ") size " + parcel.dataSize());
   4675 
   4676             int psshsize = parcel.readInt();
   4677             byte[] pssh = new byte[psshsize];
   4678             parcel.readByteArray(pssh);
   4679 
   4680             Log.v(TAG, "DrmInfo() PSSH: " + arrToHex(pssh));
   4681             mapPssh = parsePSSH(pssh, psshsize);
   4682             Log.v(TAG, "DrmInfo() PSSH: " + mapPssh);
   4683 
   4684             int supportedDRMsCount = parcel.readInt();
   4685             supportedSchemes = new UUID[supportedDRMsCount];
   4686             for (int i = 0; i < supportedDRMsCount; i++) {
   4687                 byte[] uuid = new byte[16];
   4688                 parcel.readByteArray(uuid);
   4689 
   4690                 supportedSchemes[i] = bytesToUUID(uuid);
   4691 
   4692                 Log.v(TAG, "DrmInfo() supportedScheme[" + i + "]: " +
   4693                       supportedSchemes[i]);
   4694             }
   4695 
   4696             Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize +
   4697                   " supportedDRMsCount: " + supportedDRMsCount);
   4698         }
   4699 
   4700         private DrmInfo makeCopy() {
   4701             return new DrmInfo(this.mapPssh, this.supportedSchemes);
   4702         }
   4703 
   4704         private String arrToHex(byte[] bytes) {
   4705             String out = "0x";
   4706             for (int i = 0; i < bytes.length; i++) {
   4707                 out += String.format("%02x", bytes[i]);
   4708             }
   4709 
   4710             return out;
   4711         }
   4712 
   4713         private UUID bytesToUUID(byte[] uuid) {
   4714             long msb = 0, lsb = 0;
   4715             for (int i = 0; i < 8; i++) {
   4716                 msb |= ( ((long)uuid[i]   & 0xff) << (8 * (7 - i)) );
   4717                 lsb |= ( ((long)uuid[i+8] & 0xff) << (8 * (7 - i)) );
   4718             }
   4719 
   4720             return new UUID(msb, lsb);
   4721         }
   4722 
   4723         private Map<UUID, byte[]> parsePSSH(byte[] pssh, int psshsize) {
   4724             Map<UUID, byte[]> result = new HashMap<UUID, byte[]>();
   4725 
   4726             final int UUID_SIZE = 16;
   4727             final int DATALEN_SIZE = 4;
   4728 
   4729             int len = psshsize;
   4730             int numentries = 0;
   4731             int i = 0;
   4732 
   4733             while (len > 0) {
   4734                 if (len < UUID_SIZE) {
   4735                     Log.w(TAG, String.format("parsePSSH: len is too short to parse " +
   4736                                              "UUID: (%d < 16) pssh: %d", len, psshsize));
   4737                     return null;
   4738                 }
   4739 
   4740                 byte[] subset = Arrays.copyOfRange(pssh, i, i + UUID_SIZE);
   4741                 UUID uuid = bytesToUUID(subset);
   4742                 i += UUID_SIZE;
   4743                 len -= UUID_SIZE;
   4744 
   4745                 // get data length
   4746                 if (len < 4) {
   4747                     Log.w(TAG, String.format("parsePSSH: len is too short to parse " +
   4748                                              "datalen: (%d < 4) pssh: %d", len, psshsize));
   4749                     return null;
   4750                 }
   4751 
   4752                 subset = Arrays.copyOfRange(pssh, i, i+DATALEN_SIZE);
   4753                 int datalen = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) ?
   4754                     ((subset[3] & 0xff) << 24) | ((subset[2] & 0xff) << 16) |
   4755                     ((subset[1] & 0xff) <<  8) |  (subset[0] & 0xff)          :
   4756                     ((subset[0] & 0xff) << 24) | ((subset[1] & 0xff) << 16) |
   4757                     ((subset[2] & 0xff) <<  8) |  (subset[3] & 0xff) ;
   4758                 i += DATALEN_SIZE;
   4759                 len -= DATALEN_SIZE;
   4760 
   4761                 if (len < datalen) {
   4762                     Log.w(TAG, String.format("parsePSSH: len is too short to parse " +
   4763                                              "data: (%d < %d) pssh: %d", len, datalen, psshsize));
   4764                     return null;
   4765                 }
   4766 
   4767                 byte[] data = Arrays.copyOfRange(pssh, i, i+datalen);
   4768 
   4769                 // skip the data
   4770                 i += datalen;
   4771                 len -= datalen;
   4772 
   4773                 Log.v(TAG, String.format("parsePSSH[%d]: <%s, %s> pssh: %d",
   4774                                          numentries, uuid, arrToHex(data), psshsize));
   4775                 numentries++;
   4776                 result.put(uuid, data);
   4777             }
   4778 
   4779             return result;
   4780         }
   4781 
   4782     };  // DrmInfo
   4783 
   4784     /**
   4785      * Thrown when a DRM method is called before preparing a DRM scheme through prepareDrm().
   4786      * Extends MediaDrm.MediaDrmException
   4787      */
   4788     public static final class NoDrmSchemeException extends MediaDrmException {
   4789         public NoDrmSchemeException(String detailMessage) {
   4790             super(detailMessage);
   4791         }
   4792     }
   4793 
   4794     /**
   4795      * Thrown when the device requires DRM provisioning but the provisioning attempt has
   4796      * failed due to a network error (Internet reachability, timeout, etc.).
   4797      * Extends MediaDrm.MediaDrmException
   4798      */
   4799     public static final class ProvisioningNetworkErrorException extends MediaDrmException {
   4800         public ProvisioningNetworkErrorException(String detailMessage) {
   4801             super(detailMessage);
   4802         }
   4803     }
   4804 
   4805     /**
   4806      * Thrown when the device requires DRM provisioning but the provisioning attempt has
   4807      * failed due to the provisioning server denying the request.
   4808      * Extends MediaDrm.MediaDrmException
   4809      */
   4810     public static final class ProvisioningServerErrorException extends MediaDrmException {
   4811         public ProvisioningServerErrorException(String detailMessage) {
   4812             super(detailMessage);
   4813         }
   4814     }
   4815 
   4816 
   4817     private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
   4818 
   4819         // Modular DRM helpers
   4820 
   4821     private void prepareDrm_createDrmStep(@NonNull UUID uuid)
   4822             throws UnsupportedSchemeException {
   4823         Log.v(TAG, "prepareDrm_createDrmStep: UUID: " + uuid);
   4824 
   4825         try {
   4826             mDrmObj = new MediaDrm(uuid);
   4827             Log.v(TAG, "prepareDrm_createDrmStep: Created mDrmObj=" + mDrmObj);
   4828         } catch (Exception e) { // UnsupportedSchemeException
   4829             Log.e(TAG, "prepareDrm_createDrmStep: MediaDrm failed with " + e);
   4830             throw e;
   4831         }
   4832     }
   4833 
   4834     private void prepareDrm_openSessionStep(@NonNull UUID uuid)
   4835             throws NotProvisionedException, ResourceBusyException {
   4836         Log.v(TAG, "prepareDrm_openSessionStep: uuid: " + uuid);
   4837 
   4838         // TODO: don't need an open session for a future specialKeyReleaseDrm mode but we should do
   4839         // it anyway so it raises provisioning error if needed. We'd rather handle provisioning
   4840         // at prepareDrm/openSession rather than getKeyRequest/provideKeyResponse
   4841         try {
   4842             mDrmSessionId = mDrmObj.openSession();
   4843             Log.v(TAG, "prepareDrm_openSessionStep: mDrmSessionId=" + mDrmSessionId);
   4844 
   4845             // Sending it down to native/mediaserver to create the crypto object
   4846             // This call could simply fail due to bad player state, e.g., after start().
   4847             _prepareDrm(getByteArrayFromUUID(uuid), mDrmSessionId);
   4848             Log.v(TAG, "prepareDrm_openSessionStep: _prepareDrm/Crypto succeeded");
   4849 
   4850         } catch (Exception e) { //ResourceBusyException, NotProvisionedException
   4851             Log.e(TAG, "prepareDrm_openSessionStep: open/crypto failed with " + e);
   4852             throw e;
   4853         }
   4854 
   4855     }
   4856 
   4857     private class ProvisioningThread extends Thread
   4858     {
   4859         public static final int TIMEOUT_MS = 60000;
   4860 
   4861         private UUID uuid;
   4862         private String urlStr;
   4863         private Object drmLock;
   4864         private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate;
   4865         private MediaPlayer mediaPlayer;
   4866         private int status;
   4867         private boolean finished;
   4868         public  int status() {
   4869             return status;
   4870         }
   4871 
   4872         public ProvisioningThread initialize(MediaDrm.ProvisionRequest request,
   4873                                           UUID uuid, MediaPlayer mediaPlayer) {
   4874             // lock is held by the caller
   4875             drmLock = mediaPlayer.mDrmLock;
   4876             onDrmPreparedHandlerDelegate = mediaPlayer.mOnDrmPreparedHandlerDelegate;
   4877             this.mediaPlayer = mediaPlayer;
   4878 
   4879             urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
   4880             this.uuid = uuid;
   4881 
   4882             status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
   4883 
   4884             Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr);
   4885             return this;
   4886         }
   4887 
   4888         public void run() {
   4889 
   4890             byte[] response = null;
   4891             boolean provisioningSucceeded = false;
   4892             try {
   4893                 URL url = new URL(urlStr);
   4894                 final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
   4895                 try {
   4896                     connection.setRequestMethod("POST");
   4897                     connection.setDoOutput(false);
   4898                     connection.setDoInput(true);
   4899                     connection.setConnectTimeout(TIMEOUT_MS);
   4900                     connection.setReadTimeout(TIMEOUT_MS);
   4901 
   4902                     connection.connect();
   4903                     response = Streams.readFully(connection.getInputStream());
   4904 
   4905                     Log.v(TAG, "HandleProvisioninig: Thread run: response " +
   4906                             response.length + " " + response);
   4907                 } catch (Exception e) {
   4908                     status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
   4909                     Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url);
   4910                 } finally {
   4911                     connection.disconnect();
   4912                 }
   4913             } catch (Exception e)   {
   4914                 status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
   4915                 Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e);
   4916             }
   4917 
   4918             if (response != null) {
   4919                 try {
   4920                     mDrmObj.provideProvisionResponse(response);
   4921                     Log.v(TAG, "HandleProvisioninig: Thread run: " +
   4922                             "provideProvisionResponse SUCCEEDED!");
   4923 
   4924                     provisioningSucceeded = true;
   4925                 } catch (Exception e) {
   4926                     status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
   4927                     Log.w(TAG, "HandleProvisioninig: Thread run: " +
   4928                             "provideProvisionResponse " + e);
   4929                 }
   4930             }
   4931 
   4932             boolean succeeded = false;
   4933 
   4934             // non-blocking mode needs the lock
   4935             if (onDrmPreparedHandlerDelegate != null) {
   4936 
   4937                 synchronized (drmLock) {
   4938                     // continuing with prepareDrm
   4939                     if (provisioningSucceeded) {
   4940                         succeeded = mediaPlayer.resumePrepareDrm(uuid);
   4941                         status = (succeeded) ?
   4942                                 PREPARE_DRM_STATUS_SUCCESS :
   4943                                 PREPARE_DRM_STATUS_PREPARATION_ERROR;
   4944                     }
   4945                     mediaPlayer.mDrmProvisioningInProgress = false;
   4946                     mediaPlayer.mPrepareDrmInProgress = false;
   4947                     if (!succeeded) {
   4948                         cleanDrmObj();  // cleaning up if it hasn't gone through while in the lock
   4949                     }
   4950                 } // synchronized
   4951 
   4952                 // calling the callback outside the lock
   4953                 onDrmPreparedHandlerDelegate.notifyClient(status);
   4954             } else {   // blocking mode already has the lock
   4955 
   4956                 // continuing with prepareDrm
   4957                 if (provisioningSucceeded) {
   4958                     succeeded = mediaPlayer.resumePrepareDrm(uuid);
   4959                     status = (succeeded) ?
   4960                             PREPARE_DRM_STATUS_SUCCESS :
   4961                             PREPARE_DRM_STATUS_PREPARATION_ERROR;
   4962                 }
   4963                 mediaPlayer.mDrmProvisioningInProgress = false;
   4964                 mediaPlayer.mPrepareDrmInProgress = false;
   4965                 if (!succeeded) {
   4966                     cleanDrmObj();  // cleaning up if it hasn't gone through
   4967                 }
   4968             }
   4969 
   4970             finished = true;
   4971         }   // run()
   4972 
   4973     }   // ProvisioningThread
   4974 
   4975     private int HandleProvisioninig(UUID uuid)
   4976     {
   4977         // the lock is already held by the caller
   4978 
   4979         if (mDrmProvisioningInProgress) {
   4980             Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
   4981             return PREPARE_DRM_STATUS_PREPARATION_ERROR;
   4982         }
   4983 
   4984         MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
   4985         if (provReq == null) {
   4986             Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
   4987             return PREPARE_DRM_STATUS_PREPARATION_ERROR;
   4988         }
   4989 
   4990         Log.v(TAG, "HandleProvisioninig provReq " +
   4991                 " data: " + provReq.getData() + " url: " + provReq.getDefaultUrl());
   4992 
   4993         // networking in a background thread
   4994         mDrmProvisioningInProgress = true;
   4995 
   4996         mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
   4997         mDrmProvisioningThread.start();
   4998 
   4999         int result;
   5000 
   5001         // non-blocking: this is not the final result
   5002         if (mOnDrmPreparedHandlerDelegate != null) {
   5003             result = PREPARE_DRM_STATUS_SUCCESS;
   5004         } else {
   5005             // if blocking mode, wait till provisioning is done
   5006             try {
   5007                 mDrmProvisioningThread.join();
   5008             } catch (Exception e) {
   5009                 Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
   5010             }
   5011             result = mDrmProvisioningThread.status();
   5012             // no longer need the thread
   5013             mDrmProvisioningThread = null;
   5014         }
   5015 
   5016         return result;
   5017     }
   5018 
   5019     private boolean resumePrepareDrm(UUID uuid)
   5020     {
   5021         Log.v(TAG, "resumePrepareDrm: uuid: " + uuid);
   5022 
   5023         // mDrmLock is guaranteed to be held
   5024         boolean success = false;
   5025         try {
   5026             // resuming
   5027             prepareDrm_openSessionStep(uuid);
   5028 
   5029             mDrmUUID = uuid;
   5030             mActiveDrmScheme = true;
   5031 
   5032             success = true;
   5033         } catch (Exception e) {
   5034             Log.w(TAG, "HandleProvisioninig: Thread run _prepareDrm resume failed with " + e);
   5035             // mDrmObj clean up is done by the caller
   5036         }
   5037 
   5038         return success;
   5039     }
   5040 
   5041     private void resetDrmState()
   5042     {
   5043         synchronized (mDrmLock) {
   5044             Log.v(TAG, "resetDrmState: " +
   5045                     " mDrmInfo=" + mDrmInfo +
   5046                     " mDrmProvisioningThread=" + mDrmProvisioningThread +
   5047                     " mPrepareDrmInProgress=" + mPrepareDrmInProgress +
   5048                     " mActiveDrmScheme=" + mActiveDrmScheme);
   5049 
   5050             mDrmInfoResolved = false;
   5051             mDrmInfo = null;
   5052 
   5053             if (mDrmProvisioningThread != null) {
   5054                 // timeout; relying on HttpUrlConnection
   5055                 try {
   5056                     mDrmProvisioningThread.join();
   5057                 }
   5058                 catch (InterruptedException e) {
   5059                     Log.w(TAG, "resetDrmState: ProvThread.join Exception " + e);
   5060                 }
   5061                 mDrmProvisioningThread = null;
   5062             }
   5063 
   5064             mPrepareDrmInProgress = false;
   5065             mActiveDrmScheme = false;
   5066 
   5067             cleanDrmObj();
   5068         }   // synchronized
   5069     }
   5070 
   5071     private void cleanDrmObj()
   5072     {
   5073         // the caller holds mDrmLock
   5074         Log.v(TAG, "cleanDrmObj: mDrmObj=" + mDrmObj + " mDrmSessionId=" + mDrmSessionId);
   5075 
   5076         if (mDrmSessionId != null)    {
   5077             mDrmObj.closeSession(mDrmSessionId);
   5078             mDrmSessionId = null;
   5079         }
   5080         if (mDrmObj != null) {
   5081             mDrmObj.release();
   5082             mDrmObj = null;
   5083         }
   5084     }
   5085 
   5086     private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
   5087         long msb = uuid.getMostSignificantBits();
   5088         long lsb = uuid.getLeastSignificantBits();
   5089 
   5090         byte[] uuidBytes = new byte[16];
   5091         for (int i = 0; i < 8; ++i) {
   5092             uuidBytes[i] = (byte)(msb >>> (8 * (7 - i)));
   5093             uuidBytes[8 + i] = (byte)(lsb >>> (8 * (7 - i)));
   5094         }
   5095 
   5096         return uuidBytes;
   5097     }
   5098 
   5099     // Modular DRM end
   5100 
   5101     /*
   5102      * Test whether a given video scaling mode is supported.
   5103      */
   5104     private boolean isVideoScalingModeSupported(int mode) {
   5105         return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
   5106                 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
   5107     }
   5108 
   5109     /** @hide */
   5110     static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
   5111             MediaTimeProvider {
   5112         private static final String TAG = "MTP";
   5113         private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L;
   5114         private static final long MAX_EARLY_CALLBACK_US = 1000;
   5115         private static final long TIME_ADJUSTMENT_RATE = 2;  /* meaning 1/2 */
   5116         private long mLastTimeUs = 0;
   5117         private MediaPlayer mPlayer;
   5118         private boolean mPaused = true;
   5119         private boolean mStopped = true;
   5120         private boolean mBuffering;
   5121         private long mLastReportedTime;
   5122         private long mTimeAdjustment;
   5123         // since we are expecting only a handful listeners per stream, there is
   5124         // no need for log(N) search performance
   5125         private MediaTimeProvider.OnMediaTimeListener mListeners[];
   5126         private long mTimes[];
   5127         private long mLastNanoTime;
   5128         private Handler mEventHandler;
   5129         private boolean mRefresh = false;
   5130         private boolean mPausing = false;
   5131         private boolean mSeeking = false;
   5132         private static final int NOTIFY = 1;
   5133         private static final int NOTIFY_TIME = 0;
   5134         private static final int REFRESH_AND_NOTIFY_TIME = 1;
   5135         private static final int NOTIFY_STOP = 2;
   5136         private static final int NOTIFY_SEEK = 3;
   5137         private static final int NOTIFY_TRACK_DATA = 4;
   5138         private HandlerThread mHandlerThread;
   5139 
   5140         /** @hide */
   5141         public boolean DEBUG = false;
   5142 
   5143         public TimeProvider(MediaPlayer mp) {
   5144             mPlayer = mp;
   5145             try {
   5146                 getCurrentTimeUs(true, false);
   5147             } catch (IllegalStateException e) {
   5148                 // we assume starting position
   5149                 mRefresh = true;
   5150             }
   5151 
   5152             Looper looper;
   5153             if ((looper = Looper.myLooper()) == null &&
   5154                 (looper = Looper.getMainLooper()) == null) {
   5155                 // Create our own looper here in case MP was created without one
   5156                 mHandlerThread = new HandlerThread("MediaPlayerMTPEventThread",
   5157                       Process.THREAD_PRIORITY_FOREGROUND);
   5158                 mHandlerThread.start();
   5159                 looper = mHandlerThread.getLooper();
   5160             }
   5161             mEventHandler = new EventHandler(looper);
   5162 
   5163             mListeners = new MediaTimeProvider.OnMediaTimeListener[0];
   5164             mTimes = new long[0];
   5165             mLastTimeUs = 0;
   5166             mTimeAdjustment = 0;
   5167         }
   5168 
   5169         private void scheduleNotification(int type, long delayUs) {
   5170             // ignore time notifications until seek is handled
   5171             if (mSeeking &&
   5172                     (type == NOTIFY_TIME || type == REFRESH_AND_NOTIFY_TIME)) {
   5173                 return;
   5174             }
   5175 
   5176             if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs);
   5177             mEventHandler.removeMessages(NOTIFY);
   5178             Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0);
   5179             mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000));
   5180         }
   5181 
   5182         /** @hide */
   5183         public void close() {
   5184             mEventHandler.removeMessages(NOTIFY);
   5185             if (mHandlerThread != null) {
   5186                 mHandlerThread.quitSafely();
   5187                 mHandlerThread = null;
   5188             }
   5189         }
   5190 
   5191         /** @hide */
   5192         protected void finalize() {
   5193             if (mHandlerThread != null) {
   5194                 mHandlerThread.quitSafely();
   5195             }
   5196         }
   5197 
   5198         /** @hide */
   5199         public void onPaused(boolean paused) {
   5200             synchronized(this) {
   5201                 if (DEBUG) Log.d(TAG, "onPaused: " + paused);
   5202                 if (mStopped) { // handle as seek if we were stopped
   5203                     mStopped = false;
   5204                     mSeeking = true;
   5205                     scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   5206                 } else {
   5207                     mPausing = paused;  // special handling if player disappeared
   5208                     mSeeking = false;
   5209                     scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
   5210                 }
   5211             }
   5212         }
   5213 
   5214         /** @hide */
   5215         public void onBuffering(boolean buffering) {
   5216             synchronized (this) {
   5217                 if (DEBUG) Log.d(TAG, "onBuffering: " + buffering);
   5218                 mBuffering = buffering;
   5219                 scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
   5220             }
   5221         }
   5222 
   5223         /** @hide */
   5224         public void onStopped() {
   5225             synchronized(this) {
   5226                 if (DEBUG) Log.d(TAG, "onStopped");
   5227                 mPaused = true;
   5228                 mStopped = true;
   5229                 mSeeking = false;
   5230                 mBuffering = false;
   5231                 scheduleNotification(NOTIFY_STOP, 0 /* delay */);
   5232             }
   5233         }
   5234 
   5235         /** @hide */
   5236         @Override
   5237         public void onSeekComplete(MediaPlayer mp) {
   5238             synchronized(this) {
   5239                 mStopped = false;
   5240                 mSeeking = true;
   5241                 scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   5242             }
   5243         }
   5244 
   5245         /** @hide */
   5246         public void onNewPlayer() {
   5247             if (mRefresh) {
   5248                 synchronized(this) {
   5249                     mStopped = false;
   5250                     mSeeking = true;
   5251                     mBuffering = false;
   5252                     scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   5253                 }
   5254             }
   5255         }
   5256 
   5257         private synchronized void notifySeek() {
   5258             mSeeking = false;
   5259             try {
   5260                 long timeUs = getCurrentTimeUs(true, false);
   5261                 if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs);
   5262 
   5263                 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) {
   5264                     if (listener == null) {
   5265                         break;
   5266                     }
   5267                     listener.onSeek(timeUs);
   5268                 }
   5269             } catch (IllegalStateException e) {
   5270                 // we should not be there, but at least signal pause
   5271                 if (DEBUG) Log.d(TAG, "onSeekComplete but no player");
   5272                 mPausing = true;  // special handling if player disappeared
   5273                 notifyTimedEvent(false /* refreshTime */);
   5274             }
   5275         }
   5276 
   5277         private synchronized void notifyTrackData(Pair<SubtitleTrack, byte[]> trackData) {
   5278             SubtitleTrack track = trackData.first;
   5279             byte[] data = trackData.second;
   5280             track.onData(data, true /* eos */, ~0 /* runID: keep forever */);
   5281         }
   5282 
   5283         private synchronized void notifyStop() {
   5284             for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) {
   5285                 if (listener == null) {
   5286                     break;
   5287                 }
   5288                 listener.onStop();
   5289             }
   5290         }
   5291 
   5292         private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) {
   5293             int i = 0;
   5294             for (; i < mListeners.length; i++) {
   5295                 if (mListeners[i] == listener || mListeners[i] == null) {
   5296                     break;
   5297                 }
   5298             }
   5299 
   5300             // new listener
   5301             if (i >= mListeners.length) {
   5302                 MediaTimeProvider.OnMediaTimeListener[] newListeners =
   5303                     new MediaTimeProvider.OnMediaTimeListener[i + 1];
   5304                 long[] newTimes = new long[i + 1];
   5305                 System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length);
   5306                 System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length);
   5307                 mListeners = newListeners;
   5308                 mTimes = newTimes;
   5309             }
   5310 
   5311             if (mListeners[i] == null) {
   5312                 mListeners[i] = listener;
   5313                 mTimes[i] = MediaTimeProvider.NO_TIME;
   5314             }
   5315             return i;
   5316         }
   5317 
   5318         public void notifyAt(
   5319                 long timeUs, MediaTimeProvider.OnMediaTimeListener listener) {
   5320             synchronized(this) {
   5321                 if (DEBUG) Log.d(TAG, "notifyAt " + timeUs);
   5322                 mTimes[registerListener(listener)] = timeUs;
   5323                 scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   5324             }
   5325         }
   5326 
   5327         public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) {
   5328             synchronized(this) {
   5329                 if (DEBUG) Log.d(TAG, "scheduleUpdate");
   5330                 int i = registerListener(listener);
   5331 
   5332                 if (!mStopped) {
   5333                     mTimes[i] = 0;
   5334                     scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   5335                 }
   5336             }
   5337         }
   5338 
   5339         public void cancelNotifications(
   5340                 MediaTimeProvider.OnMediaTimeListener listener) {
   5341             synchronized(this) {
   5342                 int i = 0;
   5343                 for (; i < mListeners.length; i++) {
   5344                     if (mListeners[i] == listener) {
   5345                         System.arraycopy(mListeners, i + 1,
   5346                                 mListeners, i, mListeners.length - i - 1);
   5347                         System.arraycopy(mTimes, i + 1,
   5348                                 mTimes, i, mTimes.length - i - 1);
   5349                         mListeners[mListeners.length - 1] = null;
   5350                         mTimes[mTimes.length - 1] = NO_TIME;
   5351                         break;
   5352                     } else if (mListeners[i] == null) {
   5353                         break;
   5354                     }
   5355                 }
   5356 
   5357                 scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   5358             }
   5359         }
   5360 
   5361         private synchronized void notifyTimedEvent(boolean refreshTime) {
   5362             // figure out next callback
   5363             long nowUs;
   5364             try {
   5365                 nowUs = getCurrentTimeUs(refreshTime, true);
   5366             } catch (IllegalStateException e) {
   5367                 // assume we paused until new player arrives
   5368                 mRefresh = true;
   5369                 mPausing = true; // this ensures that call succeeds
   5370                 nowUs = getCurrentTimeUs(refreshTime, true);
   5371             }
   5372             long nextTimeUs = nowUs;
   5373 
   5374             if (mSeeking) {
   5375                 // skip timed-event notifications until seek is complete
   5376                 return;
   5377             }
   5378 
   5379             if (DEBUG) {
   5380                 StringBuilder sb = new StringBuilder();
   5381                 sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ")
   5382                         .append(nowUs).append(") from {");
   5383                 boolean first = true;
   5384                 for (long time: mTimes) {
   5385                     if (time == NO_TIME) {
   5386                         continue;
   5387                     }
   5388                     if (!first) sb.append(", ");
   5389                     sb.append(time);
   5390                     first = false;
   5391                 }
   5392                 sb.append("}");
   5393                 Log.d(TAG, sb.toString());
   5394             }
   5395 
   5396             Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners =
   5397                 new Vector<MediaTimeProvider.OnMediaTimeListener>();
   5398             for (int ix = 0; ix < mTimes.length; ix++) {
   5399                 if (mListeners[ix] == null) {
   5400                     break;
   5401                 }
   5402                 if (mTimes[ix] <= NO_TIME) {
   5403                     // ignore, unless we were stopped
   5404                 } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) {
   5405                     activatedListeners.add(mListeners[ix]);
   5406                     if (DEBUG) Log.d(TAG, "removed");
   5407                     mTimes[ix] = NO_TIME;
   5408                 } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) {
   5409                     nextTimeUs = mTimes[ix];
   5410                 }
   5411             }
   5412 
   5413             if (nextTimeUs > nowUs && !mPaused) {
   5414                 // schedule callback at nextTimeUs
   5415                 if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs);
   5416                 scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs);
   5417             } else {
   5418                 mEventHandler.removeMessages(NOTIFY);
   5419                 // no more callbacks
   5420             }
   5421 
   5422             for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) {
   5423                 listener.onTimedEvent(nowUs);
   5424             }
   5425         }
   5426 
   5427         private long getEstimatedTime(long nanoTime, boolean monotonic) {
   5428             if (mPaused) {
   5429                 mLastReportedTime = mLastTimeUs + mTimeAdjustment;
   5430             } else {
   5431                 long timeSinceRead = (nanoTime - mLastNanoTime) / 1000;
   5432                 mLastReportedTime = mLastTimeUs + timeSinceRead;
   5433                 if (mTimeAdjustment > 0) {
   5434                     long adjustment =
   5435                         mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE;
   5436                     if (adjustment <= 0) {
   5437                         mTimeAdjustment = 0;
   5438                     } else {
   5439                         mLastReportedTime += adjustment;
   5440                     }
   5441                 }
   5442             }
   5443             return mLastReportedTime;
   5444         }
   5445 
   5446         public long getCurrentTimeUs(boolean refreshTime, boolean monotonic)
   5447                 throws IllegalStateException {
   5448             synchronized (this) {
   5449                 // we always refresh the time when the paused-state changes, because
   5450                 // we expect to have received the pause-change event delayed.
   5451                 if (mPaused && !refreshTime) {
   5452                     return mLastReportedTime;
   5453                 }
   5454 
   5455                 long nanoTime = System.nanoTime();
   5456                 if (refreshTime ||
   5457                         nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
   5458                     try {
   5459                         mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
   5460                         mPaused = !mPlayer.isPlaying() || mBuffering;
   5461                         if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
   5462                     } catch (IllegalStateException e) {
   5463                         if (mPausing) {
   5464                             // if we were pausing, get last estimated timestamp
   5465                             mPausing = false;
   5466                             getEstimatedTime(nanoTime, monotonic);
   5467                             mPaused = true;
   5468                             if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime);
   5469                             return mLastReportedTime;
   5470                         }
   5471                         // TODO get time when prepared
   5472                         throw e;
   5473                     }
   5474                     mLastNanoTime = nanoTime;
   5475                     if (monotonic && mLastTimeUs < mLastReportedTime) {
   5476                         /* have to adjust time */
   5477                         mTimeAdjustment = mLastReportedTime - mLastTimeUs;
   5478                         if (mTimeAdjustment > 1000000) {
   5479                             // schedule seeked event if time jumped significantly
   5480                             // TODO: do this properly by introducing an exception
   5481                             mStopped = false;
   5482                             mSeeking = true;
   5483                             scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   5484                         }
   5485                     } else {
   5486                         mTimeAdjustment = 0;
   5487                     }
   5488                 }
   5489 
   5490                 return getEstimatedTime(nanoTime, monotonic);
   5491             }
   5492         }
   5493 
   5494         private class EventHandler extends Handler {
   5495             public EventHandler(Looper looper) {
   5496                 super(looper);
   5497             }
   5498 
   5499             @Override
   5500             public void handleMessage(Message msg) {
   5501                 if (msg.what == NOTIFY) {
   5502                     switch (msg.arg1) {
   5503                     case NOTIFY_TIME:
   5504                         notifyTimedEvent(false /* refreshTime */);
   5505                         break;
   5506                     case REFRESH_AND_NOTIFY_TIME:
   5507                         notifyTimedEvent(true /* refreshTime */);
   5508                         break;
   5509                     case NOTIFY_STOP:
   5510                         notifyStop();
   5511                         break;
   5512                     case NOTIFY_SEEK:
   5513                         notifySeek();
   5514                         break;
   5515                     case NOTIFY_TRACK_DATA:
   5516                         notifyTrackData((Pair<SubtitleTrack, byte[]>)msg.obj);
   5517                         break;
   5518                     }
   5519                 }
   5520             }
   5521         }
   5522     }
   5523 
   5524     public final static class MetricsConstants
   5525     {
   5526         private MetricsConstants() {}
   5527 
   5528         /**
   5529          * Key to extract the MIME type of the video track
   5530          * from the {@link MediaPlayer#getMetrics} return value.
   5531          * The value is a String.
   5532          */
   5533         public static final String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
   5534 
   5535         /**
   5536          * Key to extract the codec being used to decode the video track
   5537          * from the {@link MediaPlayer#getMetrics} return value.
   5538          * The value is a String.
   5539          */
   5540         public static final String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
   5541 
   5542         /**
   5543          * Key to extract the width (in pixels) of the video track
   5544          * from the {@link MediaPlayer#getMetrics} return value.
   5545          * The value is an integer.
   5546          */
   5547         public static final String WIDTH = "android.media.mediaplayer.width";
   5548 
   5549         /**
   5550          * Key to extract the height (in pixels) of the video track
   5551          * from the {@link MediaPlayer#getMetrics} return value.
   5552          * The value is an integer.
   5553          */
   5554         public static final String HEIGHT = "android.media.mediaplayer.height";
   5555 
   5556         /**
   5557          * Key to extract the count of video frames played
   5558          * from the {@link MediaPlayer#getMetrics} return value.
   5559          * The value is an integer.
   5560          */
   5561         public static final String FRAMES = "android.media.mediaplayer.frames";
   5562 
   5563         /**
   5564          * Key to extract the count of video frames dropped
   5565          * from the {@link MediaPlayer#getMetrics} return value.
   5566          * The value is an integer.
   5567          */
   5568         public static final String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
   5569 
   5570         /**
   5571          * Key to extract the MIME type of the audio track
   5572          * from the {@link MediaPlayer#getMetrics} return value.
   5573          * The value is a String.
   5574          */
   5575         public static final String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
   5576 
   5577         /**
   5578          * Key to extract the codec being used to decode the audio track
   5579          * from the {@link MediaPlayer#getMetrics} return value.
   5580          * The value is a String.
   5581          */
   5582         public static final String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
   5583 
   5584         /**
   5585          * Key to extract the duration (in milliseconds) of the
   5586          * media being played
   5587          * from the {@link MediaPlayer#getMetrics} return value.
   5588          * The value is a long.
   5589          */
   5590         public static final String DURATION = "android.media.mediaplayer.durationMs";
   5591 
   5592         /**
   5593          * Key to extract the playing time (in milliseconds) of the
   5594          * media being played
   5595          * from the {@link MediaPlayer#getMetrics} return value.
   5596          * The value is a long.
   5597          */
   5598         public static final String PLAYING = "android.media.mediaplayer.playingMs";
   5599 
   5600         /**
   5601          * Key to extract the count of errors encountered while
   5602          * playing the media
   5603          * from the {@link MediaPlayer#getMetrics} return value.
   5604          * The value is an integer.
   5605          */
   5606         public static final String ERRORS = "android.media.mediaplayer.err";
   5607 
   5608         /**
   5609          * Key to extract an (optional) error code detected while
   5610          * playing the media
   5611          * from the {@link MediaPlayer#getMetrics} return value.
   5612          * The value is an integer.
   5613          */
   5614         public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
   5615 
   5616     }
   5617 }
   5618