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.app.AppOpsManager;
     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.Handler;
     29 import android.os.HandlerThread;
     30 import android.os.IBinder;
     31 import android.os.Looper;
     32 import android.os.Message;
     33 import android.os.Parcel;
     34 import android.os.Parcelable;
     35 import android.os.Process;
     36 import android.os.PowerManager;
     37 import android.os.RemoteException;
     38 import android.os.ServiceManager;
     39 import android.provider.Settings;
     40 import android.system.ErrnoException;
     41 import android.system.OsConstants;
     42 import android.util.Log;
     43 import android.util.Pair;
     44 import android.view.Surface;
     45 import android.view.SurfaceHolder;
     46 import android.widget.VideoView;
     47 import android.graphics.SurfaceTexture;
     48 import android.media.AudioManager;
     49 import android.media.MediaFormat;
     50 import android.media.MediaTimeProvider;
     51 import android.media.PlaybackParams;
     52 import android.media.SubtitleController;
     53 import android.media.SubtitleController.Anchor;
     54 import android.media.SubtitleData;
     55 import android.media.SubtitleTrack.RenderingWidget;
     56 import android.media.SyncParams;
     57 
     58 import com.android.internal.app.IAppOpsService;
     59 
     60 import libcore.io.IoBridge;
     61 import libcore.io.Libcore;
     62 
     63 import java.io.ByteArrayOutputStream;
     64 import java.io.File;
     65 import java.io.FileDescriptor;
     66 import java.io.FileInputStream;
     67 import java.io.FileNotFoundException;
     68 import java.io.IOException;
     69 import java.io.InputStream;
     70 import java.lang.Runnable;
     71 import java.lang.annotation.Retention;
     72 import java.lang.annotation.RetentionPolicy;
     73 import java.net.InetSocketAddress;
     74 import java.util.BitSet;
     75 import java.util.HashSet;
     76 import java.util.Map;
     77 import java.util.Scanner;
     78 import java.util.Set;
     79 import java.util.Vector;
     80 import java.lang.ref.WeakReference;
     81 
     82 /**
     83  * MediaPlayer class can be used to control playback
     84  * of audio/video files and streams. An example on how to use the methods in
     85  * this class can be found in {@link android.widget.VideoView}.
     86  *
     87  * <p>Topics covered here are:
     88  * <ol>
     89  * <li><a href="#StateDiagram">State Diagram</a>
     90  * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
     91  * <li><a href="#Permissions">Permissions</a>
     92  * <li><a href="#Callbacks">Register informational and error callbacks</a>
     93  * </ol>
     94  *
     95  * <div class="special reference">
     96  * <h3>Developer Guides</h3>
     97  * <p>For more information about how to use MediaPlayer, read the
     98  * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
     99  * </div>
    100  *
    101  * <a name="StateDiagram"></a>
    102  * <h3>State Diagram</h3>
    103  *
    104  * <p>Playback control of audio/video files and streams is managed as a state
    105  * machine. The following diagram shows the life cycle and the states of a
    106  * MediaPlayer object driven by the supported playback control operations.
    107  * The ovals represent the states a MediaPlayer object may reside
    108  * in. The arcs represent the playback control operations that drive the object
    109  * state transition. There are two types of arcs. The arcs with a single arrow
    110  * head represent synchronous method calls, while those with
    111  * a double arrow head represent asynchronous method calls.</p>
    112  *
    113  * <p><img src="../../../images/mediaplayer_state_diagram.gif"
    114  *         alt="MediaPlayer State diagram"
    115  *         border="0" /></p>
    116  *
    117  * <p>From this state diagram, one can see that a MediaPlayer object has the
    118  *    following states:</p>
    119  * <ul>
    120  *     <li>When a MediaPlayer object is just created using <code>new</code> or
    121  *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
    122  *         {@link #release()} is called, it is in the <em>End</em> state. Between these
    123  *         two states is the life cycle of the MediaPlayer object.
    124  *         <ul>
    125  *         <li>There is a subtle but important difference between a newly constructed
    126  *         MediaPlayer object and the MediaPlayer object after {@link #reset()}
    127  *         is called. It is a programming error to invoke methods such
    128  *         as {@link #getCurrentPosition()},
    129  *         {@link #getDuration()}, {@link #getVideoHeight()},
    130  *         {@link #getVideoWidth()}, {@link #setAudioStreamType(int)},
    131  *         {@link #setLooping(boolean)},
    132  *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()},
    133  *         {@link #stop()}, {@link #seekTo(int)}, {@link #prepare()} or
    134  *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
    135  *         methods is called right after a MediaPlayer object is constructed,
    136  *         the user supplied callback method OnErrorListener.onError() won't be
    137  *         called by the internal player engine and the object state remains
    138  *         unchanged; but if these methods are called right after {@link #reset()},
    139  *         the user supplied callback method OnErrorListener.onError() will be
    140  *         invoked by the internal player engine and the object will be
    141  *         transfered to the <em>Error</em> state. </li>
    142  *         <li>It is also recommended that once
    143  *         a MediaPlayer object is no longer being used, call {@link #release()} immediately
    144  *         so that resources used by the internal player engine associated with the
    145  *         MediaPlayer object can be released immediately. Resource may include
    146  *         singleton resources such as hardware acceleration components and
    147  *         failure to call {@link #release()} may cause subsequent instances of
    148  *         MediaPlayer objects to fallback to software implementations or fail
    149  *         altogether. Once the MediaPlayer
    150  *         object is in the <em>End</em> state, it can no longer be used and
    151  *         there is no way to bring it back to any other state. </li>
    152  *         <li>Furthermore,
    153  *         the MediaPlayer objects created using <code>new</code> is in the
    154  *         <em>Idle</em> state, while those created with one
    155  *         of the overloaded convenient <code>create</code> methods are <em>NOT</em>
    156  *         in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em>
    157  *         state if the creation using <code>create</code> method is successful.
    158  *         </li>
    159  *         </ul>
    160  *         </li>
    161  *     <li>In general, some playback control operation may fail due to various
    162  *         reasons, such as unsupported audio/video format, poorly interleaved
    163  *         audio/video, resolution too high, streaming timeout, and the like.
    164  *         Thus, error reporting and recovery is an important concern under
    165  *         these circumstances. Sometimes, due to programming errors, invoking a playback
    166  *         control operation in an invalid state may also occur. Under all these
    167  *         error conditions, the internal player engine invokes a user supplied
    168  *         OnErrorListener.onError() method if an OnErrorListener has been
    169  *         registered beforehand via
    170  *         {@link #setOnErrorListener(android.media.MediaPlayer.OnErrorListener)}.
    171  *         <ul>
    172  *         <li>It is important to note that once an error occurs, the
    173  *         MediaPlayer object enters the <em>Error</em> state (except as noted
    174  *         above), even if an error listener has not been registered by the application.</li>
    175  *         <li>In order to reuse a MediaPlayer object that is in the <em>
    176  *         Error</em> state and recover from the error,
    177  *         {@link #reset()} can be called to restore the object to its <em>Idle</em>
    178  *         state.</li>
    179  *         <li>It is good programming practice to have your application
    180  *         register a OnErrorListener to look out for error notifications from
    181  *         the internal player engine.</li>
    182  *         <li>IllegalStateException is
    183  *         thrown to prevent programming errors such as calling {@link #prepare()},
    184  *         {@link #prepareAsync()}, or one of the overloaded <code>setDataSource
    185  *         </code> methods in an invalid state. </li>
    186  *         </ul>
    187  *         </li>
    188  *     <li>Calling
    189  *         {@link #setDataSource(FileDescriptor)}, or
    190  *         {@link #setDataSource(String)}, or
    191  *         {@link #setDataSource(Context, Uri)}, or
    192  *         {@link #setDataSource(FileDescriptor, long, long)}, or
    193  *         {@link #setDataSource(MediaDataSource)} transfers a
    194  *         MediaPlayer object in the <em>Idle</em> state to the
    195  *         <em>Initialized</em> state.
    196  *         <ul>
    197  *         <li>An IllegalStateException is thrown if
    198  *         setDataSource() is called in any other state.</li>
    199  *         <li>It is good programming
    200  *         practice to always look out for <code>IllegalArgumentException</code>
    201  *         and <code>IOException</code> that may be thrown from the overloaded
    202  *         <code>setDataSource</code> methods.</li>
    203  *         </ul>
    204  *         </li>
    205  *     <li>A MediaPlayer object must first enter the <em>Prepared</em> state
    206  *         before playback can be started.
    207  *         <ul>
    208  *         <li>There are two ways (synchronous vs.
    209  *         asynchronous) that the <em>Prepared</em> state can be reached:
    210  *         either a call to {@link #prepare()} (synchronous) which
    211  *         transfers the object to the <em>Prepared</em> state once the method call
    212  *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
    213  *         first transfers the object to the <em>Preparing</em> state after the
    214  *         call returns (which occurs almost right way) while the internal
    215  *         player engine continues working on the rest of preparation work
    216  *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
    217  *         the internal player engine then calls a user supplied callback method,
    218  *         onPrepared() of the OnPreparedListener interface, if an
    219  *         OnPreparedListener is registered beforehand via {@link
    220  *         #setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)}.</li>
    221  *         <li>It is important to note that
    222  *         the <em>Preparing</em> state is a transient state, and the behavior
    223  *         of calling any method with side effect while a MediaPlayer object is
    224  *         in the <em>Preparing</em> state is undefined.</li>
    225  *         <li>An IllegalStateException is
    226  *         thrown if {@link #prepare()} or {@link #prepareAsync()} is called in
    227  *         any other state.</li>
    228  *         <li>While in the <em>Prepared</em> state, properties
    229  *         such as audio/sound volume, screenOnWhilePlaying, looping can be
    230  *         adjusted by invoking the corresponding set methods.</li>
    231  *         </ul>
    232  *         </li>
    233  *     <li>To start the playback, {@link #start()} must be called. After
    234  *         {@link #start()} returns successfully, the MediaPlayer object is in the
    235  *         <em>Started</em> state. {@link #isPlaying()} can be called to test
    236  *         whether the MediaPlayer object is in the <em>Started</em> state.
    237  *         <ul>
    238  *         <li>While in the <em>Started</em> state, the internal player engine calls
    239  *         a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback
    240  *         method if a OnBufferingUpdateListener has been registered beforehand
    241  *         via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}.
    242  *         This callback allows applications to keep track of the buffering status
    243  *         while streaming audio/video.</li>
    244  *         <li>Calling {@link #start()} has not effect
    245  *         on a MediaPlayer object that is already in the <em>Started</em> state.</li>
    246  *         </ul>
    247  *         </li>
    248  *     <li>Playback can be paused and stopped, and the current playback position
    249  *         can be adjusted. Playback can be paused via {@link #pause()}. When the call to
    250  *         {@link #pause()} returns, the MediaPlayer object enters the
    251  *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
    252  *         state to the <em>Paused</em> state and vice versa happens
    253  *         asynchronously in the player engine. It may take some time before
    254  *         the state is updated in calls to {@link #isPlaying()}, and it can be
    255  *         a number of seconds in the case of streamed content.
    256  *         <ul>
    257  *         <li>Calling {@link #start()} to resume playback for a paused
    258  *         MediaPlayer object, and the resumed playback
    259  *         position is the same as where it was paused. When the call to
    260  *         {@link #start()} returns, the paused MediaPlayer object goes back to
    261  *         the <em>Started</em> state.</li>
    262  *         <li>Calling {@link #pause()} has no effect on
    263  *         a MediaPlayer object that is already in the <em>Paused</em> state.</li>
    264  *         </ul>
    265  *         </li>
    266  *     <li>Calling  {@link #stop()} stops playback and causes a
    267  *         MediaPlayer in the <em>Started</em>, <em>Paused</em>, <em>Prepared
    268  *         </em> or <em>PlaybackCompleted</em> state to enter the
    269  *         <em>Stopped</em> state.
    270  *         <ul>
    271  *         <li>Once in the <em>Stopped</em> state, playback cannot be started
    272  *         until {@link #prepare()} or {@link #prepareAsync()} are called to set
    273  *         the MediaPlayer object to the <em>Prepared</em> state again.</li>
    274  *         <li>Calling {@link #stop()} has no effect on a MediaPlayer
    275  *         object that is already in the <em>Stopped</em> state.</li>
    276  *         </ul>
    277  *         </li>
    278  *     <li>The playback position can be adjusted with a call to
    279  *         {@link #seekTo(int)}.
    280  *         <ul>
    281  *         <li>Although the asynchronuous {@link #seekTo(int)}
    282  *         call returns right way, the actual seek operation may take a while to
    283  *         finish, especially for audio/video being streamed. When the actual
    284  *         seek operation completes, the internal player engine calls a user
    285  *         supplied OnSeekComplete.onSeekComplete() if an OnSeekCompleteListener
    286  *         has been registered beforehand via
    287  *         {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}.</li>
    288  *         <li>Please
    289  *         note that {@link #seekTo(int)} can also be called in the other states,
    290  *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
    291  *         </em> state.</li>
    292  *         <li>Furthermore, the actual current playback position
    293  *         can be retrieved with a call to {@link #getCurrentPosition()}, which
    294  *         is helpful for applications such as a Music player that need to keep
    295  *         track of the playback progress.</li>
    296  *         </ul>
    297  *         </li>
    298  *     <li>When the playback reaches the end of stream, the playback completes.
    299  *         <ul>
    300  *         <li>If the looping mode was being set to <var>true</var>with
    301  *         {@link #setLooping(boolean)}, the MediaPlayer object shall remain in
    302  *         the <em>Started</em> state.</li>
    303  *         <li>If the looping mode was set to <var>false
    304  *         </var>, the player engine calls a user supplied callback method,
    305  *         OnCompletion.onCompletion(), if a OnCompletionListener is registered
    306  *         beforehand via {@link #setOnCompletionListener(OnCompletionListener)}.
    307  *         The invoke of the callback signals that the object is now in the <em>
    308  *         PlaybackCompleted</em> state.</li>
    309  *         <li>While in the <em>PlaybackCompleted</em>
    310  *         state, calling {@link #start()} can restart the playback from the
    311  *         beginning of the audio/video source.</li>
    312  * </ul>
    313  *
    314  *
    315  * <a name="Valid_and_Invalid_States"></a>
    316  * <h3>Valid and invalid states</h3>
    317  *
    318  * <table border="0" cellspacing="0" cellpadding="0">
    319  * <tr><td>Method Name </p></td>
    320  *     <td>Valid Sates </p></td>
    321  *     <td>Invalid States </p></td>
    322  *     <td>Comments </p></td></tr>
    323  * <tr><td>attachAuxEffect </p></td>
    324  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    325  *     <td>{Idle, Error} </p></td>
    326  *     <td>This method must be called after setDataSource.
    327  *     Calling it does not change the object state. </p></td></tr>
    328  * <tr><td>getAudioSessionId </p></td>
    329  *     <td>any </p></td>
    330  *     <td>{} </p></td>
    331  *     <td>This method can be called in any state and calling it does not change
    332  *         the object state. </p></td></tr>
    333  * <tr><td>getCurrentPosition </p></td>
    334  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    335  *         PlaybackCompleted} </p></td>
    336  *     <td>{Error}</p></td>
    337  *     <td>Successful invoke of this method in a valid state does not change the
    338  *         state. Calling this method in an invalid state transfers the object
    339  *         to the <em>Error</em> state. </p></td></tr>
    340  * <tr><td>getDuration </p></td>
    341  *     <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    342  *     <td>{Idle, Initialized, Error} </p></td>
    343  *     <td>Successful invoke of this method in a valid state does not change the
    344  *         state. Calling this method in an invalid state transfers the object
    345  *         to the <em>Error</em> state. </p></td></tr>
    346  * <tr><td>getVideoHeight </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>getVideoWidth </p></td>
    354  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    355  *         PlaybackCompleted}</p></td>
    356  *     <td>{Error}</p></td>
    357  *     <td>Successful invoke of this method in a valid state does not change
    358  *         the state. Calling this method in an invalid state transfers the
    359  *         object to the <em>Error</em> state. </p></td></tr>
    360  * <tr><td>isPlaying </p></td>
    361  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    362  *          PlaybackCompleted}</p></td>
    363  *     <td>{Error}</p></td>
    364  *     <td>Successful invoke of this method in a valid state does not change
    365  *         the state. Calling this method in an invalid state transfers the
    366  *         object to the <em>Error</em> state. </p></td></tr>
    367  * <tr><td>pause </p></td>
    368  *     <td>{Started, Paused, PlaybackCompleted}</p></td>
    369  *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
    370  *     <td>Successful invoke of this method in a valid state transfers the
    371  *         object to the <em>Paused</em> state. Calling this method in an
    372  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    373  * <tr><td>prepare </p></td>
    374  *     <td>{Initialized, Stopped} </p></td>
    375  *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
    376  *     <td>Successful invoke of this method in a valid state transfers the
    377  *         object to the <em>Prepared</em> state. Calling this method in an
    378  *         invalid state throws an IllegalStateException.</p></td></tr>
    379  * <tr><td>prepareAsync </p></td>
    380  *     <td>{Initialized, Stopped} </p></td>
    381  *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
    382  *     <td>Successful invoke of this method in a valid state transfers the
    383  *         object to the <em>Preparing</em> state. Calling this method in an
    384  *         invalid state throws an IllegalStateException.</p></td></tr>
    385  * <tr><td>release </p></td>
    386  *     <td>any </p></td>
    387  *     <td>{} </p></td>
    388  *     <td>After {@link #release()}, the object is no longer available. </p></td></tr>
    389  * <tr><td>reset </p></td>
    390  *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
    391  *         PlaybackCompleted, Error}</p></td>
    392  *     <td>{}</p></td>
    393  *     <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
    394  * <tr><td>seekTo </p></td>
    395  *     <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
    396  *     <td>{Idle, Initialized, Stopped, Error}</p></td>
    397  *     <td>Successful invoke of this method in a valid state does not change
    398  *         the state. Calling this method in an invalid state transfers the
    399  *         object to the <em>Error</em> state. </p></td></tr>
    400  * <tr><td>setAudioAttributes </p></td>
    401  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    402  *          PlaybackCompleted}</p></td>
    403  *     <td>{Error}</p></td>
    404  *     <td>Successful invoke of this method does not change the state. In order for the
    405  *         target audio attributes type to become effective, this method must be called before
    406  *         prepare() or prepareAsync().</p></td></tr>
    407  * <tr><td>setAudioSessionId </p></td>
    408  *     <td>{Idle} </p></td>
    409  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
    410  *          Error} </p></td>
    411  *     <td>This method must be called in idle state as the audio session ID must be known before
    412  *         calling setDataSource. Calling it does not change the object state. </p></td></tr>
    413  * <tr><td>setAudioStreamType </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 stream type to become effective, this method must be called before
    419  *         prepare() or prepareAsync().</p></td></tr>
    420  * <tr><td>setAuxEffectSendLevel </p></td>
    421  *     <td>any</p></td>
    422  *     <td>{} </p></td>
    423  *     <td>Calling this method does not change the object state. </p></td></tr>
    424  * <tr><td>setDataSource </p></td>
    425  *     <td>{Idle} </p></td>
    426  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
    427  *          Error} </p></td>
    428  *     <td>Successful invoke of this method in a valid state transfers the
    429  *         object to the <em>Initialized</em> state. Calling this method in an
    430  *         invalid state throws an IllegalStateException.</p></td></tr>
    431  * <tr><td>setDisplay </p></td>
    432  *     <td>any </p></td>
    433  *     <td>{} </p></td>
    434  *     <td>This method can be called in any state and calling it does not change
    435  *         the object state. </p></td></tr>
    436  * <tr><td>setSurface </p></td>
    437  *     <td>any </p></td>
    438  *     <td>{} </p></td>
    439  *     <td>This method can be called in any state and calling it does not change
    440  *         the object state. </p></td></tr>
    441  * <tr><td>setVideoScalingMode </p></td>
    442  *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
    443  *     <td>{Idle, Error}</p></td>
    444  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    445  * <tr><td>setLooping </p></td>
    446  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    447  *         PlaybackCompleted}</p></td>
    448  *     <td>{Error}</p></td>
    449  *     <td>Successful invoke of this method in a valid state does not change
    450  *         the state. Calling this method in an
    451  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    452  * <tr><td>isLooping </p></td>
    453  *     <td>any </p></td>
    454  *     <td>{} </p></td>
    455  *     <td>This method can be called in any state and calling it does not change
    456  *         the object state. </p></td></tr>
    457  * <tr><td>setOnBufferingUpdateListener </p></td>
    458  *     <td>any </p></td>
    459  *     <td>{} </p></td>
    460  *     <td>This method can be called in any state and calling it does not change
    461  *         the object state. </p></td></tr>
    462  * <tr><td>setOnCompletionListener </p></td>
    463  *     <td>any </p></td>
    464  *     <td>{} </p></td>
    465  *     <td>This method can be called in any state and calling it does not change
    466  *         the object state. </p></td></tr>
    467  * <tr><td>setOnErrorListener </p></td>
    468  *     <td>any </p></td>
    469  *     <td>{} </p></td>
    470  *     <td>This method can be called in any state and calling it does not change
    471  *         the object state. </p></td></tr>
    472  * <tr><td>setOnPreparedListener </p></td>
    473  *     <td>any </p></td>
    474  *     <td>{} </p></td>
    475  *     <td>This method can be called in any state and calling it does not change
    476  *         the object state. </p></td></tr>
    477  * <tr><td>setOnSeekCompleteListener </p></td>
    478  *     <td>any </p></td>
    479  *     <td>{} </p></td>
    480  *     <td>This method can be called in any state and calling it does not change
    481  *         the object state. </p></td></tr>
    482  * <tr><td>setPlaybackRate</p></td>
    483  *     <td>any </p></td>
    484  *     <td>{} </p></td>
    485  *     <td>This method can be called in any state and calling it does not change
    486  *         the object state. </p></td></tr>
    487  * <tr><td>setPlaybackParams</p></td>
    488  *     <td>any </p></td>
    489  *     <td>{} </p></td>
    490  *     <td>This method can be called in any state and calling it does not change
    491  *         the object state. </p></td></tr>
    492  * <tr><td>setScreenOnWhilePlaying</></td>
    493  *     <td>any </p></td>
    494  *     <td>{} </p></td>
    495  *     <td>This method can be called in any state and calling it does not change
    496  *         the object state.  </p></td></tr>
    497  * <tr><td>setVolume </p></td>
    498  *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
    499  *          PlaybackCompleted}</p></td>
    500  *     <td>{Error}</p></td>
    501  *     <td>Successful invoke of this method does not change the state.
    502  * <tr><td>setWakeMode </p></td>
    503  *     <td>any </p></td>
    504  *     <td>{} </p></td>
    505  *     <td>This method can be called in any state and calling it does not change
    506  *         the object state.</p></td></tr>
    507  * <tr><td>start </p></td>
    508  *     <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
    509  *     <td>{Idle, Initialized, Stopped, Error}</p></td>
    510  *     <td>Successful invoke of this method in a valid state transfers the
    511  *         object to the <em>Started</em> state. Calling this method in an
    512  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    513  * <tr><td>stop </p></td>
    514  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    515  *     <td>{Idle, Initialized, Error}</p></td>
    516  *     <td>Successful invoke of this method in a valid state transfers the
    517  *         object to the <em>Stopped</em> state. Calling this method in an
    518  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
    519  * <tr><td>getTrackInfo </p></td>
    520  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    521  *     <td>{Idle, Initialized, Error}</p></td>
    522  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    523  * <tr><td>addTimedTextSource </p></td>
    524  *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
    525  *     <td>{Idle, Initialized, Error}</p></td>
    526  *     <td>Successful invoke of this method does not change the state.</p></td></tr>
    527  * <tr><td>selectTrack </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>deselectTrack </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  *
    536  * </table>
    537  *
    538  * <a name="Permissions"></a>
    539  * <h3>Permissions</h3>
    540  * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
    541  * android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
    542  * element.
    543  *
    544  * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
    545  * when used with network-based content.
    546  *
    547  * <a name="Callbacks"></a>
    548  * <h3>Callbacks</h3>
    549  * <p>Applications may want to register for informational and error
    550  * events in order to be informed of some internal state update and
    551  * possible runtime errors during playback or streaming. Registration for
    552  * these events is done by properly setting the appropriate listeners (via calls
    553  * to
    554  * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener,
    555  * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener,
    556  * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener,
    557  * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener,
    558  * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener,
    559  * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener,
    560  * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc).
    561  * In order to receive the respective callback
    562  * associated with these listeners, applications are required to create
    563  * MediaPlayer objects on a thread with its own Looper running (main UI
    564  * thread by default has a Looper running).
    565  *
    566  */
    567 public class MediaPlayer implements SubtitleController.Listener
    568 {
    569     /**
    570        Constant to retrieve only the new metadata since the last
    571        call.
    572        // FIXME: unhide.
    573        // FIXME: add link to getMetadata(boolean, boolean)
    574        {@hide}
    575      */
    576     public static final boolean METADATA_UPDATE_ONLY = true;
    577 
    578     /**
    579        Constant to retrieve all the metadata.
    580        // FIXME: unhide.
    581        // FIXME: add link to getMetadata(boolean, boolean)
    582        {@hide}
    583      */
    584     public static final boolean METADATA_ALL = false;
    585 
    586     /**
    587        Constant to enable the metadata filter during retrieval.
    588        // FIXME: unhide.
    589        // FIXME: add link to getMetadata(boolean, boolean)
    590        {@hide}
    591      */
    592     public static final boolean APPLY_METADATA_FILTER = true;
    593 
    594     /**
    595        Constant to disable the metadata filter during retrieval.
    596        // FIXME: unhide.
    597        // FIXME: add link to getMetadata(boolean, boolean)
    598        {@hide}
    599      */
    600     public static final boolean BYPASS_METADATA_FILTER = false;
    601 
    602     static {
    603         System.loadLibrary("media_jni");
    604         native_init();
    605     }
    606 
    607     private final static String TAG = "MediaPlayer";
    608     // Name of the remote interface for the media player. Must be kept
    609     // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE
    610     // macro invocation in IMediaPlayer.cpp
    611     private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";
    612 
    613     private long mNativeContext; // accessed by native methods
    614     private long mNativeSurfaceTexture;  // accessed by native methods
    615     private int mListenerContext; // accessed by native methods
    616     private SurfaceHolder mSurfaceHolder;
    617     private EventHandler mEventHandler;
    618     private PowerManager.WakeLock mWakeLock = null;
    619     private boolean mScreenOnWhilePlaying;
    620     private boolean mStayAwake;
    621     private final IAppOpsService mAppOps;
    622     private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
    623     private int mUsage = -1;
    624     private boolean mBypassInterruptionPolicy;
    625 
    626     /**
    627      * Default constructor. Consider using one of the create() methods for
    628      * synchronously instantiating a MediaPlayer from a Uri or resource.
    629      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    630      * to free the resources. If not released, too many MediaPlayer instances may
    631      * result in an exception.</p>
    632      */
    633     public MediaPlayer() {
    634 
    635         Looper looper;
    636         if ((looper = Looper.myLooper()) != null) {
    637             mEventHandler = new EventHandler(this, looper);
    638         } else if ((looper = Looper.getMainLooper()) != null) {
    639             mEventHandler = new EventHandler(this, looper);
    640         } else {
    641             mEventHandler = null;
    642         }
    643 
    644         mTimeProvider = new TimeProvider(this);
    645         mOpenSubtitleSources = new Vector<InputStream>();
    646         IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
    647         mAppOps = IAppOpsService.Stub.asInterface(b);
    648 
    649         /* Native setup requires a weak reference to our object.
    650          * It's easier to create it here than in C++.
    651          */
    652         native_setup(new WeakReference<MediaPlayer>(this));
    653     }
    654 
    655     /*
    656      * Update the MediaPlayer SurfaceTexture.
    657      * Call after setting a new display surface.
    658      */
    659     private native void _setVideoSurface(Surface surface);
    660 
    661     /* Do not change these values (starting with INVOKE_ID) without updating
    662      * their counterparts in include/media/mediaplayer.h!
    663      */
    664     private static final int INVOKE_ID_GET_TRACK_INFO = 1;
    665     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
    666     private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
    667     private static final int INVOKE_ID_SELECT_TRACK = 4;
    668     private static final int INVOKE_ID_DESELECT_TRACK = 5;
    669     private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
    670     private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
    671 
    672     /**
    673      * Create a request parcel which can be routed to the native media
    674      * player using {@link #invoke(Parcel, Parcel)}. The Parcel
    675      * returned has the proper InterfaceToken set. The caller should
    676      * not overwrite that token, i.e it can only append data to the
    677      * Parcel.
    678      *
    679      * @return A parcel suitable to hold a request for the native
    680      * player.
    681      * {@hide}
    682      */
    683     public Parcel newRequest() {
    684         Parcel parcel = Parcel.obtain();
    685         parcel.writeInterfaceToken(IMEDIA_PLAYER);
    686         return parcel;
    687     }
    688 
    689     /**
    690      * Invoke a generic method on the native player using opaque
    691      * parcels for the request and reply. Both payloads' format is a
    692      * convention between the java caller and the native player.
    693      * Must be called after setDataSource to make sure a native player
    694      * exists. On failure, a RuntimeException is thrown.
    695      *
    696      * @param request Parcel with the data for the extension. The
    697      * caller must use {@link #newRequest()} to get one.
    698      *
    699      * @param reply Output parcel with the data returned by the
    700      * native player.
    701      * {@hide}
    702      */
    703     public void invoke(Parcel request, Parcel reply) {
    704         int retcode = native_invoke(request, reply);
    705         reply.setDataPosition(0);
    706         if (retcode != 0) {
    707             throw new RuntimeException("failure code: " + retcode);
    708         }
    709     }
    710 
    711     /**
    712      * Sets the {@link SurfaceHolder} to use for displaying the video
    713      * portion of the media.
    714      *
    715      * Either a surface holder or surface must be set if a display or video sink
    716      * is needed.  Not calling this method or {@link #setSurface(Surface)}
    717      * when playing back a video will result in only the audio track being played.
    718      * A null surface holder or surface will result in only the audio track being
    719      * played.
    720      *
    721      * @param sh the SurfaceHolder to use for video display
    722      */
    723     public void setDisplay(SurfaceHolder sh) {
    724         mSurfaceHolder = sh;
    725         Surface surface;
    726         if (sh != null) {
    727             surface = sh.getSurface();
    728         } else {
    729             surface = null;
    730         }
    731         _setVideoSurface(surface);
    732         updateSurfaceScreenOn();
    733     }
    734 
    735     /**
    736      * Sets the {@link Surface} to be used as the sink for the video portion of
    737      * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
    738      * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
    739      * Surface will un-set any Surface or SurfaceHolder that was previously set.
    740      * A null surface will result in only the audio track being played.
    741      *
    742      * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
    743      * returned from {@link SurfaceTexture#getTimestamp()} will have an
    744      * unspecified zero point.  These timestamps cannot be directly compared
    745      * between different media sources, different instances of the same media
    746      * source, or multiple runs of the same program.  The timestamp is normally
    747      * monotonically increasing and is unaffected by time-of-day adjustments,
    748      * but it is reset when the position is set.
    749      *
    750      * @param surface The {@link Surface} to be used for the video portion of
    751      * the media.
    752      */
    753     public void setSurface(Surface surface) {
    754         if (mScreenOnWhilePlaying && surface != null) {
    755             Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
    756         }
    757         mSurfaceHolder = null;
    758         _setVideoSurface(surface);
    759         updateSurfaceScreenOn();
    760     }
    761 
    762     /* Do not change these video scaling mode values below without updating
    763      * their counterparts in system/window.h! Please do not forget to update
    764      * {@link #isVideoScalingModeSupported} when new video scaling modes
    765      * are added.
    766      */
    767     /**
    768      * Specifies a video scaling mode. The content is stretched to the
    769      * surface rendering area. When the surface has the same aspect ratio
    770      * as the content, the aspect ratio of the content is maintained;
    771      * otherwise, the aspect ratio of the content is not maintained when video
    772      * is being rendered. Unlike {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING},
    773      * there is no content cropping with this video scaling mode.
    774      */
    775     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
    776 
    777     /**
    778      * Specifies a video scaling mode. The content is scaled, maintaining
    779      * its aspect ratio. The whole surface area is always used. When the
    780      * aspect ratio of the content is the same as the surface, no content
    781      * is cropped; otherwise, content is cropped to fit the surface.
    782      */
    783     public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
    784     /**
    785      * Sets video scaling mode. To make the target video scaling mode
    786      * effective during playback, this method must be called after
    787      * data source is set. If not called, the default video
    788      * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
    789      *
    790      * <p> The supported video scaling modes are:
    791      * <ul>
    792      * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
    793      * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
    794      * </ul>
    795      *
    796      * @param mode target video scaling mode. Most be one of the supported
    797      * video scaling modes; otherwise, IllegalArgumentException will be thrown.
    798      *
    799      * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT
    800      * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
    801      */
    802     public void setVideoScalingMode(int mode) {
    803         if (!isVideoScalingModeSupported(mode)) {
    804             final String msg = "Scaling mode " + mode + " is not supported";
    805             throw new IllegalArgumentException(msg);
    806         }
    807         Parcel request = Parcel.obtain();
    808         Parcel reply = Parcel.obtain();
    809         try {
    810             request.writeInterfaceToken(IMEDIA_PLAYER);
    811             request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
    812             request.writeInt(mode);
    813             invoke(request, reply);
    814         } finally {
    815             request.recycle();
    816             reply.recycle();
    817         }
    818     }
    819 
    820     /**
    821      * Convenience method to create a MediaPlayer for a given Uri.
    822      * On success, {@link #prepare()} will already have been called and must not be called again.
    823      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    824      * to free the resources. If not released, too many MediaPlayer instances will
    825      * result in an exception.</p>
    826      * <p>Note that since {@link #prepare()} is called automatically in this method,
    827      * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
    828      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    829      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    830      *
    831      * @param context the Context to use
    832      * @param uri the Uri from which to get the datasource
    833      * @return a MediaPlayer object, or null if creation failed
    834      */
    835     public static MediaPlayer create(Context context, Uri uri) {
    836         return create (context, uri, null);
    837     }
    838 
    839     /**
    840      * Convenience method to create a MediaPlayer for a given Uri.
    841      * On success, {@link #prepare()} will already have been called and must not be called again.
    842      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    843      * to free the resources. If not released, too many MediaPlayer instances will
    844      * result in an exception.</p>
    845      * <p>Note that since {@link #prepare()} is called automatically in this method,
    846      * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
    847      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    848      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    849      *
    850      * @param context the Context to use
    851      * @param uri the Uri from which to get the datasource
    852      * @param holder the SurfaceHolder to use for displaying the video
    853      * @return a MediaPlayer object, or null if creation failed
    854      */
    855     public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) {
    856         int s = AudioSystem.newAudioSessionId();
    857         return create(context, uri, holder, null, s > 0 ? s : 0);
    858     }
    859 
    860     /**
    861      * Same factory method as {@link #create(Context, Uri, SurfaceHolder)} but that lets you specify
    862      * the audio attributes and session ID to be used by the new MediaPlayer instance.
    863      * @param context the Context to use
    864      * @param uri the Uri from which to get the datasource
    865      * @param holder the SurfaceHolder to use for displaying the video, may be null.
    866      * @param audioAttributes the {@link AudioAttributes} to be used by the media player.
    867      * @param audioSessionId the audio session ID to be used by the media player,
    868      *     see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
    869      * @return a MediaPlayer object, or null if creation failed
    870      */
    871     public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder,
    872             AudioAttributes audioAttributes, int audioSessionId) {
    873 
    874         try {
    875             MediaPlayer mp = new MediaPlayer();
    876             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    877                 new AudioAttributes.Builder().build();
    878             mp.setAudioAttributes(aa);
    879             mp.setAudioSessionId(audioSessionId);
    880             mp.setDataSource(context, uri);
    881             if (holder != null) {
    882                 mp.setDisplay(holder);
    883             }
    884             mp.prepare();
    885             return mp;
    886         } catch (IOException ex) {
    887             Log.d(TAG, "create failed:", ex);
    888             // fall through
    889         } catch (IllegalArgumentException ex) {
    890             Log.d(TAG, "create failed:", ex);
    891             // fall through
    892         } catch (SecurityException ex) {
    893             Log.d(TAG, "create failed:", ex);
    894             // fall through
    895         }
    896 
    897         return null;
    898     }
    899 
    900     // Note no convenience method to create a MediaPlayer with SurfaceTexture sink.
    901 
    902     /**
    903      * Convenience method to create a MediaPlayer for a given resource id.
    904      * On success, {@link #prepare()} will already have been called and must not be called again.
    905      * <p>When done with the MediaPlayer, you should call  {@link #release()},
    906      * to free the resources. If not released, too many MediaPlayer instances will
    907      * result in an exception.</p>
    908      * <p>Note that since {@link #prepare()} is called automatically in this method,
    909      * you cannot change the audio stream type (see {@link #setAudioStreamType(int)}), audio
    910      * session ID (see {@link #setAudioSessionId(int)}) or audio attributes
    911      * (see {@link #setAudioAttributes(AudioAttributes)} of the new MediaPlayer.</p>
    912      *
    913      * @param context the Context to use
    914      * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
    915      *              the resource to use as the datasource
    916      * @return a MediaPlayer object, or null if creation failed
    917      */
    918     public static MediaPlayer create(Context context, int resid) {
    919         int s = AudioSystem.newAudioSessionId();
    920         return create(context, resid, null, s > 0 ? s : 0);
    921     }
    922 
    923     /**
    924      * Same factory method as {@link #create(Context, int)} but that lets you specify the audio
    925      * attributes and session ID to be used by the new MediaPlayer instance.
    926      * @param context the Context to use
    927      * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
    928      *              the resource to use as the datasource
    929      * @param audioAttributes the {@link AudioAttributes} to be used by the media player.
    930      * @param audioSessionId the audio session ID to be used by the media player,
    931      *     see {@link AudioManager#generateAudioSessionId()} to obtain a new session.
    932      * @return a MediaPlayer object, or null if creation failed
    933      */
    934     public static MediaPlayer create(Context context, int resid,
    935             AudioAttributes audioAttributes, int audioSessionId) {
    936         try {
    937             AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
    938             if (afd == null) return null;
    939 
    940             MediaPlayer mp = new MediaPlayer();
    941 
    942             final AudioAttributes aa = audioAttributes != null ? audioAttributes :
    943                 new AudioAttributes.Builder().build();
    944             mp.setAudioAttributes(aa);
    945             mp.setAudioSessionId(audioSessionId);
    946 
    947             mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
    948             afd.close();
    949             mp.prepare();
    950             return mp;
    951         } catch (IOException ex) {
    952             Log.d(TAG, "create failed:", ex);
    953             // fall through
    954         } catch (IllegalArgumentException ex) {
    955             Log.d(TAG, "create failed:", ex);
    956            // fall through
    957         } catch (SecurityException ex) {
    958             Log.d(TAG, "create failed:", ex);
    959             // fall through
    960         }
    961         return null;
    962     }
    963 
    964     /**
    965      * Sets the data source as a content Uri.
    966      *
    967      * @param context the Context to use when resolving the Uri
    968      * @param uri the Content URI of the data you want to play
    969      * @throws IllegalStateException if it is called in an invalid state
    970      */
    971     public void setDataSource(Context context, Uri uri)
    972         throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
    973         setDataSource(context, uri, null);
    974     }
    975 
    976     /**
    977      * Sets the data source as a content Uri.
    978      *
    979      * @param context the Context to use when resolving the Uri
    980      * @param uri the Content URI of the data you want to play
    981      * @param headers the headers to be sent together with the request for the data
    982      *                Note that the cross domain redirection is allowed by default, but that can be
    983      *                changed with key/value pairs through the headers parameter with
    984      *                "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
    985      *                to disallow or allow cross domain redirection.
    986      * @throws IllegalStateException if it is called in an invalid state
    987      */
    988     public void setDataSource(Context context, Uri uri, Map<String, String> headers)
    989             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
    990         final String scheme = uri.getScheme();
    991         if (ContentResolver.SCHEME_FILE.equals(scheme)) {
    992             setDataSource(uri.getPath());
    993             return;
    994         } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
    995                 && Settings.AUTHORITY.equals(uri.getAuthority())) {
    996             // Redirect ringtones to go directly to underlying provider
    997             uri = RingtoneManager.getActualDefaultRingtoneUri(context,
    998                     RingtoneManager.getDefaultType(uri));
    999             if (uri == null) {
   1000                 throw new FileNotFoundException("Failed to resolve default ringtone");
   1001             }
   1002         }
   1003 
   1004         AssetFileDescriptor fd = null;
   1005         try {
   1006             ContentResolver resolver = context.getContentResolver();
   1007             fd = resolver.openAssetFileDescriptor(uri, "r");
   1008             if (fd == null) {
   1009                 return;
   1010             }
   1011             // Note: using getDeclaredLength so that our behavior is the same
   1012             // as previous versions when the content provider is returning
   1013             // a full file.
   1014             if (fd.getDeclaredLength() < 0) {
   1015                 setDataSource(fd.getFileDescriptor());
   1016             } else {
   1017                 setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
   1018             }
   1019             return;
   1020         } catch (SecurityException | IOException ex) {
   1021             Log.w(TAG, "Couldn't open file on client side; trying server side: " + ex);
   1022         } finally {
   1023             if (fd != null) {
   1024                 fd.close();
   1025             }
   1026         }
   1027 
   1028         setDataSource(uri.toString(), headers);
   1029     }
   1030 
   1031     /**
   1032      * Sets the data source (file-path or http/rtsp URL) to use.
   1033      *
   1034      * @param path the path of the file, or the http/rtsp URL of the stream you want to play
   1035      * @throws IllegalStateException if it is called in an invalid state
   1036      *
   1037      * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
   1038      * process other than the calling application.  This implies that the pathname
   1039      * should be an absolute path (as any other process runs with unspecified current working
   1040      * directory), and that the pathname should reference a world-readable file.
   1041      * As an alternative, the application could first open the file for reading,
   1042      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
   1043      */
   1044     public void setDataSource(String path)
   1045             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1046         setDataSource(path, null, null);
   1047     }
   1048 
   1049     /**
   1050      * Sets the data source (file-path or http/rtsp URL) to use.
   1051      *
   1052      * @param path the path of the file, or the http/rtsp URL of the stream you want to play
   1053      * @param headers the headers associated with the http request for the stream you want to play
   1054      * @throws IllegalStateException if it is called in an invalid state
   1055      * @hide pending API council
   1056      */
   1057     public void setDataSource(String path, Map<String, String> headers)
   1058             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
   1059     {
   1060         String[] keys = null;
   1061         String[] values = null;
   1062 
   1063         if (headers != null) {
   1064             keys = new String[headers.size()];
   1065             values = new String[headers.size()];
   1066 
   1067             int i = 0;
   1068             for (Map.Entry<String, String> entry: headers.entrySet()) {
   1069                 keys[i] = entry.getKey();
   1070                 values[i] = entry.getValue();
   1071                 ++i;
   1072             }
   1073         }
   1074         setDataSource(path, keys, values);
   1075     }
   1076 
   1077     private void setDataSource(String path, String[] keys, String[] values)
   1078             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
   1079         final Uri uri = Uri.parse(path);
   1080         final String scheme = uri.getScheme();
   1081         if ("file".equals(scheme)) {
   1082             path = uri.getPath();
   1083         } else if (scheme != null) {
   1084             // handle non-file sources
   1085             nativeSetDataSource(
   1086                 MediaHTTPService.createHttpServiceBinderIfNecessary(path),
   1087                 path,
   1088                 keys,
   1089                 values);
   1090             return;
   1091         }
   1092 
   1093         final File file = new File(path);
   1094         if (file.exists()) {
   1095             FileInputStream is = new FileInputStream(file);
   1096             FileDescriptor fd = is.getFD();
   1097             setDataSource(fd);
   1098             is.close();
   1099         } else {
   1100             throw new IOException("setDataSource failed.");
   1101         }
   1102     }
   1103 
   1104     private native void nativeSetDataSource(
   1105         IBinder httpServiceBinder, String path, String[] keys, String[] values)
   1106         throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
   1107 
   1108     /**
   1109      * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
   1110      * to close the file descriptor. It is safe to do so as soon as this call returns.
   1111      *
   1112      * @param fd the FileDescriptor for the file you want to play
   1113      * @throws IllegalStateException if it is called in an invalid state
   1114      */
   1115     public void setDataSource(FileDescriptor fd)
   1116             throws IOException, IllegalArgumentException, IllegalStateException {
   1117         // intentionally less than LONG_MAX
   1118         setDataSource(fd, 0, 0x7ffffffffffffffL);
   1119     }
   1120 
   1121     /**
   1122      * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be
   1123      * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
   1124      * to close the file descriptor. It is safe to do so as soon as this call returns.
   1125      *
   1126      * @param fd the FileDescriptor for the file you want to play
   1127      * @param offset the offset into the file where the data to be played starts, in bytes
   1128      * @param length the length in bytes of the data to be played
   1129      * @throws IllegalStateException if it is called in an invalid state
   1130      */
   1131     public void setDataSource(FileDescriptor fd, long offset, long length)
   1132             throws IOException, IllegalArgumentException, IllegalStateException {
   1133         _setDataSource(fd, offset, length);
   1134     }
   1135 
   1136     private native void _setDataSource(FileDescriptor fd, long offset, long length)
   1137             throws IOException, IllegalArgumentException, IllegalStateException;
   1138 
   1139     /**
   1140      * Sets the data source (MediaDataSource) to use.
   1141      *
   1142      * @param dataSource the MediaDataSource for the media you want to play
   1143      * @throws IllegalStateException if it is called in an invalid state
   1144      */
   1145     public void setDataSource(MediaDataSource dataSource)
   1146             throws IllegalArgumentException, IllegalStateException {
   1147         _setDataSource(dataSource);
   1148     }
   1149 
   1150     private native void _setDataSource(MediaDataSource dataSource)
   1151           throws IllegalArgumentException, IllegalStateException;
   1152 
   1153     /**
   1154      * Prepares the player for playback, synchronously.
   1155      *
   1156      * After setting the datasource and the display surface, you need to either
   1157      * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
   1158      * which blocks until MediaPlayer is ready for playback.
   1159      *
   1160      * @throws IllegalStateException if it is called in an invalid state
   1161      */
   1162     public void prepare() throws IOException, IllegalStateException {
   1163         _prepare();
   1164         scanInternalSubtitleTracks();
   1165     }
   1166 
   1167     private native void _prepare() throws IOException, IllegalStateException;
   1168 
   1169     /**
   1170      * Prepares the player for playback, asynchronously.
   1171      *
   1172      * After setting the datasource and the display surface, you need to either
   1173      * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
   1174      * which returns immediately, rather than blocking until enough data has been
   1175      * buffered.
   1176      *
   1177      * @throws IllegalStateException if it is called in an invalid state
   1178      */
   1179     public native void prepareAsync() throws IllegalStateException;
   1180 
   1181     /**
   1182      * Starts or resumes playback. If playback had previously been paused,
   1183      * playback will continue from where it was paused. If playback had
   1184      * been stopped, or never started before, playback will start at the
   1185      * beginning.
   1186      *
   1187      * @throws IllegalStateException if it is called in an invalid state
   1188      */
   1189     public void start() throws IllegalStateException {
   1190         if (isRestricted()) {
   1191             _setVolume(0, 0);
   1192         }
   1193         stayAwake(true);
   1194         _start();
   1195     }
   1196 
   1197     private native void _start() throws IllegalStateException;
   1198 
   1199     private boolean isRestricted() {
   1200         if (mBypassInterruptionPolicy) {
   1201             return false;
   1202         }
   1203         try {
   1204             final int usage = mUsage != -1 ? mUsage
   1205                     : AudioAttributes.usageForLegacyStreamType(getAudioStreamType());
   1206             final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, usage,
   1207                     Process.myUid(), ActivityThread.currentPackageName());
   1208             return mode != AppOpsManager.MODE_ALLOWED;
   1209         } catch (RemoteException e) {
   1210             return false;
   1211         }
   1212     }
   1213 
   1214     private int getAudioStreamType() {
   1215         if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   1216             mStreamType = _getAudioStreamType();
   1217         }
   1218         return mStreamType;
   1219     }
   1220 
   1221     private native int _getAudioStreamType() throws IllegalStateException;
   1222 
   1223     /**
   1224      * Stops playback after playback has been stopped or paused.
   1225      *
   1226      * @throws IllegalStateException if the internal player engine has not been
   1227      * initialized.
   1228      */
   1229     public void stop() throws IllegalStateException {
   1230         stayAwake(false);
   1231         _stop();
   1232     }
   1233 
   1234     private native void _stop() throws IllegalStateException;
   1235 
   1236     /**
   1237      * Pauses playback. Call start() to resume.
   1238      *
   1239      * @throws IllegalStateException if the internal player engine has not been
   1240      * initialized.
   1241      */
   1242     public void pause() throws IllegalStateException {
   1243         stayAwake(false);
   1244         _pause();
   1245     }
   1246 
   1247     private native void _pause() throws IllegalStateException;
   1248 
   1249     /**
   1250      * Set the low-level power management behavior for this MediaPlayer.  This
   1251      * can be used when the MediaPlayer is not playing through a SurfaceHolder
   1252      * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
   1253      * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
   1254      *
   1255      * <p>This function has the MediaPlayer access the low-level power manager
   1256      * service to control the device's power usage while playing is occurring.
   1257      * The parameter is a combination of {@link android.os.PowerManager} wake flags.
   1258      * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
   1259      * permission.
   1260      * By default, no attempt is made to keep the device awake during playback.
   1261      *
   1262      * @param context the Context to use
   1263      * @param mode    the power/wake mode to set
   1264      * @see android.os.PowerManager
   1265      */
   1266     public void setWakeMode(Context context, int mode) {
   1267         boolean washeld = false;
   1268         if (mWakeLock != null) {
   1269             if (mWakeLock.isHeld()) {
   1270                 washeld = true;
   1271                 mWakeLock.release();
   1272             }
   1273             mWakeLock = null;
   1274         }
   1275 
   1276         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
   1277         mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
   1278         mWakeLock.setReferenceCounted(false);
   1279         if (washeld) {
   1280             mWakeLock.acquire();
   1281         }
   1282     }
   1283 
   1284     /**
   1285      * Control whether we should use the attached SurfaceHolder to keep the
   1286      * screen on while video playback is occurring.  This is the preferred
   1287      * method over {@link #setWakeMode} where possible, since it doesn't
   1288      * require that the application have permission for low-level wake lock
   1289      * access.
   1290      *
   1291      * @param screenOn Supply true to keep the screen on, false to allow it
   1292      * to turn off.
   1293      */
   1294     public void setScreenOnWhilePlaying(boolean screenOn) {
   1295         if (mScreenOnWhilePlaying != screenOn) {
   1296             if (screenOn && mSurfaceHolder == null) {
   1297                 Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
   1298             }
   1299             mScreenOnWhilePlaying = screenOn;
   1300             updateSurfaceScreenOn();
   1301         }
   1302     }
   1303 
   1304     private void stayAwake(boolean awake) {
   1305         if (mWakeLock != null) {
   1306             if (awake && !mWakeLock.isHeld()) {
   1307                 mWakeLock.acquire();
   1308             } else if (!awake && mWakeLock.isHeld()) {
   1309                 mWakeLock.release();
   1310             }
   1311         }
   1312         mStayAwake = awake;
   1313         updateSurfaceScreenOn();
   1314     }
   1315 
   1316     private void updateSurfaceScreenOn() {
   1317         if (mSurfaceHolder != null) {
   1318             mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
   1319         }
   1320     }
   1321 
   1322     /**
   1323      * Returns the width of the video.
   1324      *
   1325      * @return the width of the video, or 0 if there is no video,
   1326      * no display surface was set, or the width has not been determined
   1327      * yet. The OnVideoSizeChangedListener can be registered via
   1328      * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
   1329      * to provide a notification when the width is available.
   1330      */
   1331     public native int getVideoWidth();
   1332 
   1333     /**
   1334      * Returns the height of the video.
   1335      *
   1336      * @return the height of the video, or 0 if there is no video,
   1337      * no display surface was set, or the height has not been determined
   1338      * yet. The OnVideoSizeChangedListener can be registered via
   1339      * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
   1340      * to provide a notification when the height is available.
   1341      */
   1342     public native int getVideoHeight();
   1343 
   1344     /**
   1345      * Checks whether the MediaPlayer is playing.
   1346      *
   1347      * @return true if currently playing, false otherwise
   1348      * @throws IllegalStateException if the internal player engine has not been
   1349      * initialized or has been released.
   1350      */
   1351     public native boolean isPlaying();
   1352 
   1353     /**
   1354      * Change playback speed of audio by resampling the audio.
   1355      * <p>
   1356      * Specifies resampling as audio mode for variable rate playback, i.e.,
   1357      * resample the waveform based on the requested playback rate to get
   1358      * a new waveform, and play back the new waveform at the original sampling
   1359      * frequency.
   1360      * When rate is larger than 1.0, pitch becomes higher.
   1361      * When rate is smaller than 1.0, pitch becomes lower.
   1362      *
   1363      * @hide
   1364      */
   1365     public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
   1366 
   1367     /**
   1368      * Change playback speed of audio without changing its pitch.
   1369      * <p>
   1370      * Specifies time stretching as audio mode for variable rate playback.
   1371      * Time stretching changes the duration of the audio samples without
   1372      * affecting its pitch.
   1373      * <p>
   1374      * This mode is only supported for a limited range of playback speed factors,
   1375      * e.g. between 1/2x and 2x.
   1376      *
   1377      * @hide
   1378      */
   1379     public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
   1380 
   1381     /**
   1382      * Change playback speed of audio without changing its pitch, and
   1383      * possibly mute audio if time stretching is not supported for the playback
   1384      * speed.
   1385      * <p>
   1386      * Try to keep audio pitch when changing the playback rate, but allow the
   1387      * system to determine how to change audio playback if the rate is out
   1388      * of range.
   1389      *
   1390      * @hide
   1391      */
   1392     public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
   1393 
   1394     /** @hide */
   1395     @IntDef(
   1396         value = {
   1397             PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
   1398             PLAYBACK_RATE_AUDIO_MODE_STRETCH,
   1399             PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
   1400     })
   1401     @Retention(RetentionPolicy.SOURCE)
   1402     public @interface PlaybackRateAudioMode {}
   1403 
   1404     /**
   1405      * Sets playback rate and audio mode.
   1406      *
   1407      * @param rate the ratio between desired playback rate and normal one.
   1408      * @param audioMode audio playback mode. Must be one of the supported
   1409      * audio modes.
   1410      *
   1411      * @throws IllegalStateException if the internal player engine has not been
   1412      * initialized.
   1413      * @throws IllegalArgumentException if audioMode is not supported.
   1414      *
   1415      * @hide
   1416      */
   1417     @NonNull
   1418     public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
   1419         PlaybackParams params = new PlaybackParams();
   1420         params.allowDefaults();
   1421         switch (audioMode) {
   1422         case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
   1423             params.setSpeed(rate).setPitch(1.0f);
   1424             break;
   1425         case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
   1426             params.setSpeed(rate).setPitch(1.0f)
   1427                     .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
   1428             break;
   1429         case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
   1430             params.setSpeed(rate).setPitch(rate);
   1431             break;
   1432         default:
   1433             final String msg = "Audio playback mode " + audioMode + " is not supported";
   1434             throw new IllegalArgumentException(msg);
   1435         }
   1436         return params;
   1437     }
   1438 
   1439     /**
   1440      * Sets playback rate using {@link PlaybackParams}.
   1441      *
   1442      * @param params the playback params.
   1443      *
   1444      * @throws IllegalStateException if the internal player engine has not been
   1445      * initialized.
   1446      * @throws IllegalArgumentException if params is not supported.
   1447      */
   1448     public native void setPlaybackParams(@NonNull PlaybackParams params);
   1449 
   1450     /**
   1451      * Gets the playback params, containing the current playback rate.
   1452      *
   1453      * @return the playback params.
   1454      * @throws IllegalStateException if the internal player engine has not been
   1455      * initialized.
   1456      */
   1457     @NonNull
   1458     public native PlaybackParams getPlaybackParams();
   1459 
   1460     /**
   1461      * Sets A/V sync mode.
   1462      *
   1463      * @param params the A/V sync params to apply
   1464      *
   1465      * @throws IllegalStateException if the internal player engine has not been
   1466      * initialized.
   1467      * @throws IllegalArgumentException if params are not supported.
   1468      */
   1469     public native void setSyncParams(@NonNull SyncParams params);
   1470 
   1471     /**
   1472      * Gets the A/V sync mode.
   1473      *
   1474      * @return the A/V sync params
   1475      *
   1476      * @throws IllegalStateException if the internal player engine has not been
   1477      * initialized.
   1478      */
   1479     @NonNull
   1480     public native SyncParams getSyncParams();
   1481 
   1482     /**
   1483      * Seeks to specified time position.
   1484      *
   1485      * @param msec the offset in milliseconds from the start to seek to
   1486      * @throws IllegalStateException if the internal player engine has not been
   1487      * initialized
   1488      */
   1489     public native void seekTo(int msec) throws IllegalStateException;
   1490 
   1491     /**
   1492      * Get current playback position as a {@link MediaTimestamp}.
   1493      * <p>
   1494      * The MediaTimestamp represents how the media time correlates to the system time in
   1495      * a linear fashion using an anchor and a clock rate. During regular playback, the media
   1496      * time moves fairly constantly (though the anchor frame may be rebased to a current
   1497      * system time, the linear correlation stays steady). Therefore, this method does not
   1498      * need to be called often.
   1499      * <p>
   1500      * To help users get current playback position, this method always anchors the timestamp
   1501      * to the current {@link System#nanoTime system time}, so
   1502      * {@link MediaTimestamp#getAnchorMediaTimeUs} can be used as current playback position.
   1503      *
   1504      * @return a MediaTimestamp object if a timestamp is available, or {@code null} if no timestamp
   1505      *         is available, e.g. because the media player has not been initialized.
   1506      *
   1507      * @see MediaTimestamp
   1508      */
   1509     @Nullable
   1510     public MediaTimestamp getTimestamp()
   1511     {
   1512         try {
   1513             // TODO: get the timestamp from native side
   1514             return new MediaTimestamp(
   1515                     getCurrentPosition() * 1000L,
   1516                     System.nanoTime(),
   1517                     isPlaying() ? getPlaybackParams().getSpeed() : 0.f);
   1518         } catch (IllegalStateException e) {
   1519             return null;
   1520         }
   1521     }
   1522 
   1523     /**
   1524      * Gets the current playback position.
   1525      *
   1526      * @return the current position in milliseconds
   1527      */
   1528     public native int getCurrentPosition();
   1529 
   1530     /**
   1531      * Gets the duration of the file.
   1532      *
   1533      * @return the duration in milliseconds, if no duration is available
   1534      *         (for example, if streaming live content), -1 is returned.
   1535      */
   1536     public native int getDuration();
   1537 
   1538     /**
   1539      * Gets the media metadata.
   1540      *
   1541      * @param update_only controls whether the full set of available
   1542      * metadata is returned or just the set that changed since the
   1543      * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
   1544      * #METADATA_ALL}.
   1545      *
   1546      * @param apply_filter if true only metadata that matches the
   1547      * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
   1548      * #BYPASS_METADATA_FILTER}.
   1549      *
   1550      * @return The metadata, possibly empty. null if an error occured.
   1551      // FIXME: unhide.
   1552      * {@hide}
   1553      */
   1554     public Metadata getMetadata(final boolean update_only,
   1555                                 final boolean apply_filter) {
   1556         Parcel reply = Parcel.obtain();
   1557         Metadata data = new Metadata();
   1558 
   1559         if (!native_getMetadata(update_only, apply_filter, reply)) {
   1560             reply.recycle();
   1561             return null;
   1562         }
   1563 
   1564         // Metadata takes over the parcel, don't recycle it unless
   1565         // there is an error.
   1566         if (!data.parse(reply)) {
   1567             reply.recycle();
   1568             return null;
   1569         }
   1570         return data;
   1571     }
   1572 
   1573     /**
   1574      * Set a filter for the metadata update notification and update
   1575      * retrieval. The caller provides 2 set of metadata keys, allowed
   1576      * and blocked. The blocked set always takes precedence over the
   1577      * allowed one.
   1578      * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
   1579      * shorthands to allow/block all or no metadata.
   1580      *
   1581      * By default, there is no filter set.
   1582      *
   1583      * @param allow Is the set of metadata the client is interested
   1584      *              in receiving new notifications for.
   1585      * @param block Is the set of metadata the client is not interested
   1586      *              in receiving new notifications for.
   1587      * @return The call status code.
   1588      *
   1589      // FIXME: unhide.
   1590      * {@hide}
   1591      */
   1592     public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
   1593         // Do our serialization manually instead of calling
   1594         // Parcel.writeArray since the sets are made of the same type
   1595         // we avoid paying the price of calling writeValue (used by
   1596         // writeArray) which burns an extra int per element to encode
   1597         // the type.
   1598         Parcel request =  newRequest();
   1599 
   1600         // The parcel starts already with an interface token. There
   1601         // are 2 filters. Each one starts with a 4bytes number to
   1602         // store the len followed by a number of int (4 bytes as well)
   1603         // representing the metadata type.
   1604         int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
   1605 
   1606         if (request.dataCapacity() < capacity) {
   1607             request.setDataCapacity(capacity);
   1608         }
   1609 
   1610         request.writeInt(allow.size());
   1611         for(Integer t: allow) {
   1612             request.writeInt(t);
   1613         }
   1614         request.writeInt(block.size());
   1615         for(Integer t: block) {
   1616             request.writeInt(t);
   1617         }
   1618         return native_setMetadataFilter(request);
   1619     }
   1620 
   1621     /**
   1622      * Set the MediaPlayer to start when this MediaPlayer finishes playback
   1623      * (i.e. reaches the end of the stream).
   1624      * The media framework will attempt to transition from this player to
   1625      * the next as seamlessly as possible. The next player can be set at
   1626      * any time before completion. The next player must be prepared by the
   1627      * app, and the application should not call start() on it.
   1628      * The next MediaPlayer must be different from 'this'. An exception
   1629      * will be thrown if next == this.
   1630      * The application may call setNextMediaPlayer(null) to indicate no
   1631      * next player should be started at the end of playback.
   1632      * If the current player is looping, it will keep looping and the next
   1633      * player will not be started.
   1634      *
   1635      * @param next the player to start after this one completes playback.
   1636      *
   1637      */
   1638     public native void setNextMediaPlayer(MediaPlayer next);
   1639 
   1640     /**
   1641      * Releases resources associated with this MediaPlayer object.
   1642      * It is considered good practice to call this method when you're
   1643      * done using the MediaPlayer. In particular, whenever an Activity
   1644      * of an application is paused (its onPause() method is called),
   1645      * or stopped (its onStop() method is called), this method should be
   1646      * invoked to release the MediaPlayer object, unless the application
   1647      * has a special need to keep the object around. In addition to
   1648      * unnecessary resources (such as memory and instances of codecs)
   1649      * being held, failure to call this method immediately if a
   1650      * MediaPlayer object is no longer needed may also lead to
   1651      * continuous battery consumption for mobile devices, and playback
   1652      * failure for other applications if no multiple instances of the
   1653      * same codec are supported on a device. Even if multiple instances
   1654      * of the same codec are supported, some performance degradation
   1655      * may be expected when unnecessary multiple instances are used
   1656      * at the same time.
   1657      */
   1658     public void release() {
   1659         stayAwake(false);
   1660         updateSurfaceScreenOn();
   1661         mOnPreparedListener = null;
   1662         mOnBufferingUpdateListener = null;
   1663         mOnCompletionListener = null;
   1664         mOnSeekCompleteListener = null;
   1665         mOnErrorListener = null;
   1666         mOnInfoListener = null;
   1667         mOnVideoSizeChangedListener = null;
   1668         mOnTimedTextListener = null;
   1669         if (mTimeProvider != null) {
   1670             mTimeProvider.close();
   1671             mTimeProvider = null;
   1672         }
   1673         mOnSubtitleDataListener = null;
   1674         _release();
   1675     }
   1676 
   1677     private native void _release();
   1678 
   1679     /**
   1680      * Resets the MediaPlayer to its uninitialized state. After calling
   1681      * this method, you will have to initialize it again by setting the
   1682      * data source and calling prepare().
   1683      */
   1684     public void reset() {
   1685         mSelectedSubtitleTrackIndex = -1;
   1686         synchronized(mOpenSubtitleSources) {
   1687             for (final InputStream is: mOpenSubtitleSources) {
   1688                 try {
   1689                     is.close();
   1690                 } catch (IOException e) {
   1691                 }
   1692             }
   1693             mOpenSubtitleSources.clear();
   1694         }
   1695         if (mSubtitleController != null) {
   1696             mSubtitleController.reset();
   1697         }
   1698         if (mTimeProvider != null) {
   1699             mTimeProvider.close();
   1700             mTimeProvider = null;
   1701         }
   1702 
   1703         stayAwake(false);
   1704         _reset();
   1705         // make sure none of the listeners get called anymore
   1706         if (mEventHandler != null) {
   1707             mEventHandler.removeCallbacksAndMessages(null);
   1708         }
   1709 
   1710         synchronized (mIndexTrackPairs) {
   1711             mIndexTrackPairs.clear();
   1712             mInbandTrackIndices.clear();
   1713         };
   1714     }
   1715 
   1716     private native void _reset();
   1717 
   1718     /**
   1719      * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
   1720      * for a list of stream types. Must call this method before prepare() or
   1721      * prepareAsync() in order for the target stream type to become effective
   1722      * thereafter.
   1723      *
   1724      * @param streamtype the audio stream type
   1725      * @see android.media.AudioManager
   1726      */
   1727     public void setAudioStreamType(int streamtype) {
   1728         _setAudioStreamType(streamtype);
   1729         mStreamType = streamtype;
   1730     }
   1731 
   1732     private native void _setAudioStreamType(int streamtype);
   1733 
   1734     // Keep KEY_PARAMETER_* in sync with include/media/mediaplayer.h
   1735     private final static int KEY_PARAMETER_AUDIO_ATTRIBUTES = 1400;
   1736     /**
   1737      * Sets the parameter indicated by key.
   1738      * @param key key indicates the parameter to be set.
   1739      * @param value value of the parameter to be set.
   1740      * @return true if the parameter is set successfully, false otherwise
   1741      * {@hide}
   1742      */
   1743     private native boolean setParameter(int key, Parcel value);
   1744 
   1745     /**
   1746      * Sets the audio attributes for this MediaPlayer.
   1747      * See {@link AudioAttributes} for how to build and configure an instance of this class.
   1748      * You must call this method before {@link #prepare()} or {@link #prepareAsync()} in order
   1749      * for the audio attributes to become effective thereafter.
   1750      * @param attributes a non-null set of audio attributes
   1751      */
   1752     public void setAudioAttributes(AudioAttributes attributes) throws IllegalArgumentException {
   1753         if (attributes == null) {
   1754             final String msg = "Cannot set AudioAttributes to null";
   1755             throw new IllegalArgumentException(msg);
   1756         }
   1757         mUsage = attributes.getUsage();
   1758         mBypassInterruptionPolicy = (attributes.getAllFlags()
   1759                 & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0;
   1760         Parcel pattributes = Parcel.obtain();
   1761         attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS);
   1762         setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes);
   1763         pattributes.recycle();
   1764     }
   1765 
   1766     /**
   1767      * Sets the player to be looping or non-looping.
   1768      *
   1769      * @param looping whether to loop or not
   1770      */
   1771     public native void setLooping(boolean looping);
   1772 
   1773     /**
   1774      * Checks whether the MediaPlayer is looping or non-looping.
   1775      *
   1776      * @return true if the MediaPlayer is currently looping, false otherwise
   1777      */
   1778     public native boolean isLooping();
   1779 
   1780     /**
   1781      * Sets the volume on this player.
   1782      * This API is recommended for balancing the output of audio streams
   1783      * within an application. Unless you are writing an application to
   1784      * control user settings, this API should be used in preference to
   1785      * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
   1786      * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
   1787      * UI controls should be scaled logarithmically.
   1788      *
   1789      * @param leftVolume left volume scalar
   1790      * @param rightVolume right volume scalar
   1791      */
   1792     /*
   1793      * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
   1794      * The single parameter form below is preferred if the channel volumes don't need
   1795      * to be set independently.
   1796      */
   1797     public void setVolume(float leftVolume, float rightVolume) {
   1798         if (isRestricted()) {
   1799             return;
   1800         }
   1801         _setVolume(leftVolume, rightVolume);
   1802     }
   1803 
   1804     private native void _setVolume(float leftVolume, float rightVolume);
   1805 
   1806     /**
   1807      * Similar, excepts sets volume of all channels to same value.
   1808      * @hide
   1809      */
   1810     public void setVolume(float volume) {
   1811         setVolume(volume, volume);
   1812     }
   1813 
   1814     /**
   1815      * Sets the audio session ID.
   1816      *
   1817      * @param sessionId the audio session ID.
   1818      * The audio session ID is a system wide unique identifier for the audio stream played by
   1819      * this MediaPlayer instance.
   1820      * The primary use of the audio session ID  is to associate audio effects to a particular
   1821      * instance of MediaPlayer: if an audio session ID is provided when creating an audio effect,
   1822      * this effect will be applied only to the audio content of media players within the same
   1823      * audio session and not to the output mix.
   1824      * When created, a MediaPlayer instance automatically generates its own audio session ID.
   1825      * However, it is possible to force this player to be part of an already existing audio session
   1826      * by calling this method.
   1827      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
   1828      * @throws IllegalStateException if it is called in an invalid state
   1829      */
   1830     public native void setAudioSessionId(int sessionId)  throws IllegalArgumentException, IllegalStateException;
   1831 
   1832     /**
   1833      * Returns the audio session ID.
   1834      *
   1835      * @return the audio session ID. {@see #setAudioSessionId(int)}
   1836      * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer was contructed.
   1837      */
   1838     public native int getAudioSessionId();
   1839 
   1840     /**
   1841      * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
   1842      * effect which can be applied on any sound source that directs a certain amount of its
   1843      * energy to this effect. This amount is defined by setAuxEffectSendLevel().
   1844      * See {@link #setAuxEffectSendLevel(float)}.
   1845      * <p>After creating an auxiliary effect (e.g.
   1846      * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
   1847      * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
   1848      * to attach the player to the effect.
   1849      * <p>To detach the effect from the player, call this method with a null effect id.
   1850      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
   1851      * methods.
   1852      * @param effectId system wide unique id of the effect to attach
   1853      */
   1854     public native void attachAuxEffect(int effectId);
   1855 
   1856 
   1857     /**
   1858      * Sets the send level of the player to the attached auxiliary effect.
   1859      * See {@link #attachAuxEffect(int)}. The level value range is 0 to 1.0.
   1860      * <p>By default the send level is 0, so even if an effect is attached to the player
   1861      * this method must be called for the effect to be applied.
   1862      * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
   1863      * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
   1864      * so an appropriate conversion from linear UI input x to level is:
   1865      * x == 0 -> level = 0
   1866      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
   1867      * @param level send level scalar
   1868      */
   1869     public void setAuxEffectSendLevel(float level) {
   1870         if (isRestricted()) {
   1871             return;
   1872         }
   1873         _setAuxEffectSendLevel(level);
   1874     }
   1875 
   1876     private native void _setAuxEffectSendLevel(float level);
   1877 
   1878     /*
   1879      * @param request Parcel destinated to the media player. The
   1880      *                Interface token must be set to the IMediaPlayer
   1881      *                one to be routed correctly through the system.
   1882      * @param reply[out] Parcel that will contain the reply.
   1883      * @return The status code.
   1884      */
   1885     private native final int native_invoke(Parcel request, Parcel reply);
   1886 
   1887 
   1888     /*
   1889      * @param update_only If true fetch only the set of metadata that have
   1890      *                    changed since the last invocation of getMetadata.
   1891      *                    The set is built using the unfiltered
   1892      *                    notifications the native player sent to the
   1893      *                    MediaPlayerService during that period of
   1894      *                    time. If false, all the metadatas are considered.
   1895      * @param apply_filter  If true, once the metadata set has been built based on
   1896      *                     the value update_only, the current filter is applied.
   1897      * @param reply[out] On return contains the serialized
   1898      *                   metadata. Valid only if the call was successful.
   1899      * @return The status code.
   1900      */
   1901     private native final boolean native_getMetadata(boolean update_only,
   1902                                                     boolean apply_filter,
   1903                                                     Parcel reply);
   1904 
   1905     /*
   1906      * @param request Parcel with the 2 serialized lists of allowed
   1907      *                metadata types followed by the one to be
   1908      *                dropped. Each list starts with an integer
   1909      *                indicating the number of metadata type elements.
   1910      * @return The status code.
   1911      */
   1912     private native final int native_setMetadataFilter(Parcel request);
   1913 
   1914     private static native final void native_init();
   1915     private native final void native_setup(Object mediaplayer_this);
   1916     private native final void native_finalize();
   1917 
   1918     /**
   1919      * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
   1920      *
   1921      * @see android.media.MediaPlayer#getTrackInfo
   1922      */
   1923     static public class TrackInfo implements Parcelable {
   1924         /**
   1925          * Gets the track type.
   1926          * @return TrackType which indicates if the track is video, audio, timed text.
   1927          */
   1928         public int getTrackType() {
   1929             return mTrackType;
   1930         }
   1931 
   1932         /**
   1933          * Gets the language code of the track.
   1934          * @return a language code in either way of ISO-639-1 or ISO-639-2.
   1935          * When the language is unknown or could not be determined,
   1936          * ISO-639-2 language code, "und", is returned.
   1937          */
   1938         public String getLanguage() {
   1939             String language = mFormat.getString(MediaFormat.KEY_LANGUAGE);
   1940             return language == null ? "und" : language;
   1941         }
   1942 
   1943         /**
   1944          * Gets the {@link MediaFormat} of the track.  If the format is
   1945          * unknown or could not be determined, null is returned.
   1946          */
   1947         public MediaFormat getFormat() {
   1948             if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT
   1949                     || mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   1950                 return mFormat;
   1951             }
   1952             return null;
   1953         }
   1954 
   1955         public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
   1956         public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
   1957         public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
   1958         public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
   1959         public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
   1960         public static final int MEDIA_TRACK_TYPE_METADATA = 5;
   1961 
   1962         final int mTrackType;
   1963         final MediaFormat mFormat;
   1964 
   1965         TrackInfo(Parcel in) {
   1966             mTrackType = in.readInt();
   1967             // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat
   1968             // even for audio/video tracks, meaning we only set the mime and language.
   1969             String mime = in.readString();
   1970             String language = in.readString();
   1971             mFormat = MediaFormat.createSubtitleFormat(mime, language);
   1972 
   1973             if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   1974                 mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt());
   1975                 mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt());
   1976                 mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt());
   1977             }
   1978         }
   1979 
   1980         /** @hide */
   1981         TrackInfo(int type, MediaFormat format) {
   1982             mTrackType = type;
   1983             mFormat = format;
   1984         }
   1985 
   1986         /**
   1987          * {@inheritDoc}
   1988          */
   1989         @Override
   1990         public int describeContents() {
   1991             return 0;
   1992         }
   1993 
   1994         /**
   1995          * {@inheritDoc}
   1996          */
   1997         @Override
   1998         public void writeToParcel(Parcel dest, int flags) {
   1999             dest.writeInt(mTrackType);
   2000             dest.writeString(getLanguage());
   2001 
   2002             if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) {
   2003                 dest.writeString(mFormat.getString(MediaFormat.KEY_MIME));
   2004                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_AUTOSELECT));
   2005                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_DEFAULT));
   2006                 dest.writeInt(mFormat.getInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE));
   2007             }
   2008         }
   2009 
   2010         @Override
   2011         public String toString() {
   2012             StringBuilder out = new StringBuilder(128);
   2013             out.append(getClass().getName());
   2014             out.append('{');
   2015             switch (mTrackType) {
   2016             case MEDIA_TRACK_TYPE_VIDEO:
   2017                 out.append("VIDEO");
   2018                 break;
   2019             case MEDIA_TRACK_TYPE_AUDIO:
   2020                 out.append("AUDIO");
   2021                 break;
   2022             case MEDIA_TRACK_TYPE_TIMEDTEXT:
   2023                 out.append("TIMEDTEXT");
   2024                 break;
   2025             case MEDIA_TRACK_TYPE_SUBTITLE:
   2026                 out.append("SUBTITLE");
   2027                 break;
   2028             default:
   2029                 out.append("UNKNOWN");
   2030                 break;
   2031             }
   2032             out.append(", " + mFormat.toString());
   2033             out.append("}");
   2034             return out.toString();
   2035         }
   2036 
   2037         /**
   2038          * Used to read a TrackInfo from a Parcel.
   2039          */
   2040         static final Parcelable.Creator<TrackInfo> CREATOR
   2041                 = new Parcelable.Creator<TrackInfo>() {
   2042                     @Override
   2043                     public TrackInfo createFromParcel(Parcel in) {
   2044                         return new TrackInfo(in);
   2045                     }
   2046 
   2047                     @Override
   2048                     public TrackInfo[] newArray(int size) {
   2049                         return new TrackInfo[size];
   2050                     }
   2051                 };
   2052 
   2053     };
   2054 
   2055     // We would like domain specific classes with more informative names than the `first` and `second`
   2056     // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise
   2057     // we document the meanings of `first` and `second` here:
   2058     //
   2059     // Pair.first - inband track index; non-null iff representing an inband track.
   2060     // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing
   2061     //               an inband subtitle track or any out-of-band track (subtitle or timedtext).
   2062     private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>();
   2063     private BitSet mInbandTrackIndices = new BitSet();
   2064 
   2065     /**
   2066      * Returns an array of track information.
   2067      *
   2068      * @return Array of track info. The total number of tracks is the array length.
   2069      * Must be called again if an external timed text source has been added after any of the
   2070      * addTimedTextSource methods are called.
   2071      * @throws IllegalStateException if it is called in an invalid state.
   2072      */
   2073     public TrackInfo[] getTrackInfo() throws IllegalStateException {
   2074         TrackInfo trackInfo[] = getInbandTrackInfo();
   2075         // add out-of-band tracks
   2076         synchronized (mIndexTrackPairs) {
   2077             TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()];
   2078             for (int i = 0; i < allTrackInfo.length; i++) {
   2079                 Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2080                 if (p.first != null) {
   2081                     // inband track
   2082                     allTrackInfo[i] = trackInfo[p.first];
   2083                 } else {
   2084                     SubtitleTrack track = p.second;
   2085                     allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat());
   2086                 }
   2087             }
   2088             return allTrackInfo;
   2089         }
   2090     }
   2091 
   2092     private TrackInfo[] getInbandTrackInfo() throws IllegalStateException {
   2093         Parcel request = Parcel.obtain();
   2094         Parcel reply = Parcel.obtain();
   2095         try {
   2096             request.writeInterfaceToken(IMEDIA_PLAYER);
   2097             request.writeInt(INVOKE_ID_GET_TRACK_INFO);
   2098             invoke(request, reply);
   2099             TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
   2100             return trackInfo;
   2101         } finally {
   2102             request.recycle();
   2103             reply.recycle();
   2104         }
   2105     }
   2106 
   2107     /* Do not change these values without updating their counterparts
   2108      * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
   2109      */
   2110     /**
   2111      * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
   2112      */
   2113     public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
   2114 
   2115     /**
   2116      * MIME type for WebVTT subtitle data.
   2117      * @hide
   2118      */
   2119     public static final String MEDIA_MIMETYPE_TEXT_VTT = "text/vtt";
   2120 
   2121     /**
   2122      * MIME type for CEA-608 closed caption data.
   2123      * @hide
   2124      */
   2125     public static final String MEDIA_MIMETYPE_TEXT_CEA_608 = "text/cea-608";
   2126 
   2127     /*
   2128      * A helper function to check if the mime type is supported by media framework.
   2129      */
   2130     private static boolean availableMimeTypeForExternalSource(String mimeType) {
   2131         if (MEDIA_MIMETYPE_TEXT_SUBRIP.equals(mimeType)) {
   2132             return true;
   2133         }
   2134         return false;
   2135     }
   2136 
   2137     private SubtitleController mSubtitleController;
   2138 
   2139     /** @hide */
   2140     public void setSubtitleAnchor(
   2141             SubtitleController controller,
   2142             SubtitleController.Anchor anchor) {
   2143         // TODO: create SubtitleController in MediaPlayer
   2144         mSubtitleController = controller;
   2145         mSubtitleController.setAnchor(anchor);
   2146     }
   2147 
   2148     /**
   2149      * The private version of setSubtitleAnchor is used internally to set mSubtitleController if
   2150      * necessary when clients don't provide their own SubtitleControllers using the public version
   2151      * {@link #setSubtitleAnchor(SubtitleController, Anchor)} (e.g. {@link VideoView} provides one).
   2152      */
   2153     private synchronized void setSubtitleAnchor() {
   2154         if (mSubtitleController == null) {
   2155             final HandlerThread thread = new HandlerThread("SetSubtitleAnchorThread");
   2156             thread.start();
   2157             Handler handler = new Handler(thread.getLooper());
   2158             handler.post(new Runnable() {
   2159                 @Override
   2160                 public void run() {
   2161                     Context context = ActivityThread.currentApplication();
   2162                     mSubtitleController = new SubtitleController(context, mTimeProvider, MediaPlayer.this);
   2163                     mSubtitleController.setAnchor(new Anchor() {
   2164                         @Override
   2165                         public void setSubtitleWidget(RenderingWidget subtitleWidget) {
   2166                         }
   2167 
   2168                         @Override
   2169                         public Looper getSubtitleLooper() {
   2170                             return Looper.getMainLooper();
   2171                         }
   2172                     });
   2173                     thread.getLooper().quitSafely();
   2174                 }
   2175             });
   2176             try {
   2177                 thread.join();
   2178             } catch (InterruptedException e) {
   2179                 Thread.currentThread().interrupt();
   2180                 Log.w(TAG, "failed to join SetSubtitleAnchorThread");
   2181             }
   2182         }
   2183     }
   2184 
   2185     private int mSelectedSubtitleTrackIndex = -1;
   2186     private Vector<InputStream> mOpenSubtitleSources;
   2187 
   2188     private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() {
   2189         @Override
   2190         public void onSubtitleData(MediaPlayer mp, SubtitleData data) {
   2191             int index = data.getTrackIndex();
   2192             synchronized (mIndexTrackPairs) {
   2193                 for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
   2194                     if (p.first != null && p.first == index && p.second != null) {
   2195                         // inband subtitle track that owns data
   2196                         SubtitleTrack track = p.second;
   2197                         track.onData(data);
   2198                     }
   2199                 }
   2200             }
   2201         }
   2202     };
   2203 
   2204     /** @hide */
   2205     @Override
   2206     public void onSubtitleTrackSelected(SubtitleTrack track) {
   2207         if (mSelectedSubtitleTrackIndex >= 0) {
   2208             try {
   2209                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, false);
   2210             } catch (IllegalStateException e) {
   2211             }
   2212             mSelectedSubtitleTrackIndex = -1;
   2213         }
   2214         setOnSubtitleDataListener(null);
   2215         if (track == null) {
   2216             return;
   2217         }
   2218 
   2219         synchronized (mIndexTrackPairs) {
   2220             for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) {
   2221                 if (p.first != null && p.second == track) {
   2222                     // inband subtitle track that is selected
   2223                     mSelectedSubtitleTrackIndex = p.first;
   2224                     break;
   2225                 }
   2226             }
   2227         }
   2228 
   2229         if (mSelectedSubtitleTrackIndex >= 0) {
   2230             try {
   2231                 selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true);
   2232             } catch (IllegalStateException e) {
   2233             }
   2234             setOnSubtitleDataListener(mSubtitleDataListener);
   2235         }
   2236         // no need to select out-of-band tracks
   2237     }
   2238 
   2239     /** @hide */
   2240     public void addSubtitleSource(InputStream is, MediaFormat format)
   2241             throws IllegalStateException
   2242     {
   2243         final InputStream fIs = is;
   2244         final MediaFormat fFormat = format;
   2245 
   2246         // Ensure all input streams are closed.  It is also a handy
   2247         // way to implement timeouts in the future.
   2248         synchronized(mOpenSubtitleSources) {
   2249             mOpenSubtitleSources.add(is);
   2250         }
   2251 
   2252         // process each subtitle in its own thread
   2253         final HandlerThread thread = new HandlerThread("SubtitleReadThread",
   2254               Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
   2255         thread.start();
   2256         Handler handler = new Handler(thread.getLooper());
   2257         handler.post(new Runnable() {
   2258             private int addTrack() {
   2259                 if (fIs == null || mSubtitleController == null) {
   2260                     return MEDIA_INFO_UNSUPPORTED_SUBTITLE;
   2261                 }
   2262 
   2263                 SubtitleTrack track = mSubtitleController.addTrack(fFormat);
   2264                 if (track == null) {
   2265                     return MEDIA_INFO_UNSUPPORTED_SUBTITLE;
   2266                 }
   2267 
   2268                 // TODO: do the conversion in the subtitle track
   2269                 Scanner scanner = new Scanner(fIs, "UTF-8");
   2270                 String contents = scanner.useDelimiter("\\A").next();
   2271                 synchronized(mOpenSubtitleSources) {
   2272                     mOpenSubtitleSources.remove(fIs);
   2273                 }
   2274                 scanner.close();
   2275                 synchronized (mIndexTrackPairs) {
   2276                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
   2277                 }
   2278                 track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */);
   2279                 return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
   2280             }
   2281 
   2282             public void run() {
   2283                 int res = addTrack();
   2284                 if (mEventHandler != null) {
   2285                     Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
   2286                     mEventHandler.sendMessage(m);
   2287                 }
   2288                 thread.getLooper().quitSafely();
   2289             }
   2290         });
   2291     }
   2292 
   2293     private void scanInternalSubtitleTracks() {
   2294         if (mSubtitleController == null) {
   2295             Log.d(TAG, "setSubtitleAnchor in MediaPlayer");
   2296             setSubtitleAnchor();
   2297         }
   2298 
   2299         populateInbandTracks();
   2300 
   2301         if (mSubtitleController != null) {
   2302             mSubtitleController.selectDefaultTrack();
   2303         }
   2304     }
   2305 
   2306     private void populateInbandTracks() {
   2307         TrackInfo[] tracks = getInbandTrackInfo();
   2308         synchronized (mIndexTrackPairs) {
   2309             for (int i = 0; i < tracks.length; i++) {
   2310                 if (mInbandTrackIndices.get(i)) {
   2311                     continue;
   2312                 } else {
   2313                     mInbandTrackIndices.set(i);
   2314                 }
   2315 
   2316                 // newly appeared inband track
   2317                 if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) {
   2318                     SubtitleTrack track = mSubtitleController.addTrack(
   2319                             tracks[i].getFormat());
   2320                     mIndexTrackPairs.add(Pair.create(i, track));
   2321                 } else {
   2322                     mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null));
   2323                 }
   2324             }
   2325         }
   2326     }
   2327 
   2328     /* TODO: Limit the total number of external timed text source to a reasonable number.
   2329      */
   2330     /**
   2331      * Adds an external timed text source file.
   2332      *
   2333      * Currently supported format is SubRip with the file extension .srt, case insensitive.
   2334      * Note that a single external timed text source may contain multiple tracks in it.
   2335      * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
   2336      * additional tracks become available after this method call.
   2337      *
   2338      * @param path The file path of external timed text source file.
   2339      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2340      * @throws IOException if the file cannot be accessed or is corrupted.
   2341      * @throws IllegalArgumentException if the mimeType is not supported.
   2342      * @throws IllegalStateException if called in an invalid state.
   2343      */
   2344     public void addTimedTextSource(String path, String mimeType)
   2345             throws IOException, IllegalArgumentException, IllegalStateException {
   2346         if (!availableMimeTypeForExternalSource(mimeType)) {
   2347             final String msg = "Illegal mimeType for timed text source: " + mimeType;
   2348             throw new IllegalArgumentException(msg);
   2349         }
   2350 
   2351         File file = new File(path);
   2352         if (file.exists()) {
   2353             FileInputStream is = new FileInputStream(file);
   2354             FileDescriptor fd = is.getFD();
   2355             addTimedTextSource(fd, mimeType);
   2356             is.close();
   2357         } else {
   2358             // We do not support the case where the path is not a file.
   2359             throw new IOException(path);
   2360         }
   2361     }
   2362 
   2363     /**
   2364      * Adds an external timed text source file (Uri).
   2365      *
   2366      * Currently supported format is SubRip with the file extension .srt, case insensitive.
   2367      * Note that a single external timed text source may contain multiple tracks in it.
   2368      * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
   2369      * additional tracks become available after this method call.
   2370      *
   2371      * @param context the Context to use when resolving the Uri
   2372      * @param uri the Content URI of the data you want to play
   2373      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2374      * @throws IOException if the file cannot be accessed or is corrupted.
   2375      * @throws IllegalArgumentException if the mimeType is not supported.
   2376      * @throws IllegalStateException if called in an invalid state.
   2377      */
   2378     public void addTimedTextSource(Context context, Uri uri, String mimeType)
   2379             throws IOException, IllegalArgumentException, IllegalStateException {
   2380         String scheme = uri.getScheme();
   2381         if(scheme == null || scheme.equals("file")) {
   2382             addTimedTextSource(uri.getPath(), mimeType);
   2383             return;
   2384         }
   2385 
   2386         AssetFileDescriptor fd = null;
   2387         try {
   2388             ContentResolver resolver = context.getContentResolver();
   2389             fd = resolver.openAssetFileDescriptor(uri, "r");
   2390             if (fd == null) {
   2391                 return;
   2392             }
   2393             addTimedTextSource(fd.getFileDescriptor(), mimeType);
   2394             return;
   2395         } catch (SecurityException ex) {
   2396         } catch (IOException ex) {
   2397         } finally {
   2398             if (fd != null) {
   2399                 fd.close();
   2400             }
   2401         }
   2402     }
   2403 
   2404     /**
   2405      * Adds an external timed text source file (FileDescriptor).
   2406      *
   2407      * It is the caller's responsibility to close the file descriptor.
   2408      * It is safe to do so as soon as this call returns.
   2409      *
   2410      * Currently supported format is SubRip. Note that a single external timed text source may
   2411      * contain multiple tracks in it. One can find the total number of available tracks
   2412      * using {@link #getTrackInfo()} to see what additional tracks become available
   2413      * after this method call.
   2414      *
   2415      * @param fd the FileDescriptor for the file you want to play
   2416      * @param mimeType The mime type of the file. Must be one of the mime types listed above.
   2417      * @throws IllegalArgumentException if the mimeType is not supported.
   2418      * @throws IllegalStateException if called in an invalid state.
   2419      */
   2420     public void addTimedTextSource(FileDescriptor fd, String mimeType)
   2421             throws IllegalArgumentException, IllegalStateException {
   2422         // intentionally less than LONG_MAX
   2423         addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType);
   2424     }
   2425 
   2426     /**
   2427      * Adds an external timed text file (FileDescriptor).
   2428      *
   2429      * It is the caller's responsibility to close the file descriptor.
   2430      * It is safe to do so as soon as this call returns.
   2431      *
   2432      * Currently supported format is SubRip. Note that a single external timed text source may
   2433      * contain multiple tracks in it. One can find the total number of available tracks
   2434      * using {@link #getTrackInfo()} to see what additional tracks become available
   2435      * after this method call.
   2436      *
   2437      * @param fd the FileDescriptor for the file you want to play
   2438      * @param offset the offset into the file where the data to be played starts, in bytes
   2439      * @param length the length in bytes of the data to be played
   2440      * @param mime The mime type of the file. Must be one of the mime types listed above.
   2441      * @throws IllegalArgumentException if the mimeType is not supported.
   2442      * @throws IllegalStateException if called in an invalid state.
   2443      */
   2444     public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mime)
   2445             throws IllegalArgumentException, IllegalStateException {
   2446         if (!availableMimeTypeForExternalSource(mime)) {
   2447             throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mime);
   2448         }
   2449 
   2450         FileDescriptor fd2;
   2451         try {
   2452             fd2 = Libcore.os.dup(fd);
   2453         } catch (ErrnoException ex) {
   2454             Log.e(TAG, ex.getMessage(), ex);
   2455             throw new RuntimeException(ex);
   2456         }
   2457 
   2458         final MediaFormat fFormat = new MediaFormat();
   2459         fFormat.setString(MediaFormat.KEY_MIME, mime);
   2460         fFormat.setInteger(MediaFormat.KEY_IS_TIMED_TEXT, 1);
   2461 
   2462         // A MediaPlayer created by a VideoView should already have its mSubtitleController set.
   2463         if (mSubtitleController == null) {
   2464             setSubtitleAnchor();
   2465         }
   2466 
   2467         if (!mSubtitleController.hasRendererFor(fFormat)) {
   2468             // test and add not atomic
   2469             Context context = ActivityThread.currentApplication();
   2470             mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler));
   2471         }
   2472         final SubtitleTrack track = mSubtitleController.addTrack(fFormat);
   2473         synchronized (mIndexTrackPairs) {
   2474             mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track));
   2475         }
   2476 
   2477         final FileDescriptor fd3 = fd2;
   2478         final long offset2 = offset;
   2479         final long length2 = length;
   2480         final HandlerThread thread = new HandlerThread(
   2481                 "TimedTextReadThread",
   2482                 Process.THREAD_PRIORITY_BACKGROUND + Process.THREAD_PRIORITY_MORE_FAVORABLE);
   2483         thread.start();
   2484         Handler handler = new Handler(thread.getLooper());
   2485         handler.post(new Runnable() {
   2486             private int addTrack() {
   2487                 InputStream is = null;
   2488                 final ByteArrayOutputStream bos = new ByteArrayOutputStream();
   2489                 try {
   2490                     Libcore.os.lseek(fd3, offset2, OsConstants.SEEK_SET);
   2491                     byte[] buffer = new byte[4096];
   2492                     for (long total = 0; total < length2;) {
   2493                         int bytesToRead = (int) Math.min(buffer.length, length2 - total);
   2494                         int bytes = IoBridge.read(fd3, buffer, 0, bytesToRead);
   2495                         if (bytes < 0) {
   2496                             break;
   2497                         } else {
   2498                             bos.write(buffer, 0, bytes);
   2499                             total += bytes;
   2500                         }
   2501                     }
   2502                     track.onData(bos.toByteArray(), true /* eos */, ~0 /* runID: keep forever */);
   2503                     return MEDIA_INFO_EXTERNAL_METADATA_UPDATE;
   2504                 } catch (Exception e) {
   2505                     Log.e(TAG, e.getMessage(), e);
   2506                     return MEDIA_INFO_TIMED_TEXT_ERROR;
   2507                 } finally {
   2508                     if (is != null) {
   2509                         try {
   2510                             is.close();
   2511                         } catch (IOException e) {
   2512                             Log.e(TAG, e.getMessage(), e);
   2513                         }
   2514                     }
   2515                 }
   2516             }
   2517 
   2518             public void run() {
   2519                 int res = addTrack();
   2520                 if (mEventHandler != null) {
   2521                     Message m = mEventHandler.obtainMessage(MEDIA_INFO, res, 0, null);
   2522                     mEventHandler.sendMessage(m);
   2523                 }
   2524                 thread.getLooper().quitSafely();
   2525             }
   2526         });
   2527     }
   2528 
   2529     /**
   2530      * Returns the index of the audio, video, or subtitle track currently selected for playback,
   2531      * The return value is an index into the array returned by {@link #getTrackInfo()}, and can
   2532      * be used in calls to {@link #selectTrack(int)} or {@link #deselectTrack(int)}.
   2533      *
   2534      * @param trackType should be one of {@link TrackInfo#MEDIA_TRACK_TYPE_VIDEO},
   2535      * {@link TrackInfo#MEDIA_TRACK_TYPE_AUDIO}, or
   2536      * {@link TrackInfo#MEDIA_TRACK_TYPE_SUBTITLE}
   2537      * @return index of the audio, video, or subtitle track currently selected for playback;
   2538      * a negative integer is returned when there is no selected track for {@code trackType} or
   2539      * when {@code trackType} is not one of audio, video, or subtitle.
   2540      * @throws IllegalStateException if called after {@link #release()}
   2541      *
   2542      * @see #getTrackInfo()
   2543      * @see #selectTrack(int)
   2544      * @see #deselectTrack(int)
   2545      */
   2546     public int getSelectedTrack(int trackType) throws IllegalStateException {
   2547         if (mSubtitleController != null
   2548                 && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE
   2549                 || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) {
   2550             SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack();
   2551             if (subtitleTrack != null) {
   2552                 synchronized (mIndexTrackPairs) {
   2553                     for (int i = 0; i < mIndexTrackPairs.size(); i++) {
   2554                         Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2555                         if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) {
   2556                             return i;
   2557                         }
   2558                     }
   2559                 }
   2560             }
   2561         }
   2562 
   2563         Parcel request = Parcel.obtain();
   2564         Parcel reply = Parcel.obtain();
   2565         try {
   2566             request.writeInterfaceToken(IMEDIA_PLAYER);
   2567             request.writeInt(INVOKE_ID_GET_SELECTED_TRACK);
   2568             request.writeInt(trackType);
   2569             invoke(request, reply);
   2570             int inbandTrackIndex = reply.readInt();
   2571             synchronized (mIndexTrackPairs) {
   2572                 for (int i = 0; i < mIndexTrackPairs.size(); i++) {
   2573                     Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i);
   2574                     if (p.first != null && p.first == inbandTrackIndex) {
   2575                         return i;
   2576                     }
   2577                 }
   2578             }
   2579             return -1;
   2580         } finally {
   2581             request.recycle();
   2582             reply.recycle();
   2583         }
   2584     }
   2585 
   2586     /**
   2587      * Selects a track.
   2588      * <p>
   2589      * If a MediaPlayer is in invalid state, it throws an IllegalStateException exception.
   2590      * If a MediaPlayer is in <em>Started</em> state, the selected track is presented immediately.
   2591      * If a MediaPlayer is not in Started state, it just marks the track to be played.
   2592      * </p>
   2593      * <p>
   2594      * In any valid state, if it is called multiple times on the same type of track (ie. Video,
   2595      * Audio, Timed Text), the most recent one will be chosen.
   2596      * </p>
   2597      * <p>
   2598      * The first audio and video tracks are selected by default if available, even though
   2599      * this method is not called. However, no timed text track will be selected until
   2600      * this function is called.
   2601      * </p>
   2602      * <p>
   2603      * Currently, only timed text tracks or audio tracks can be selected via this method.
   2604      * In addition, the support for selecting an audio track at runtime is pretty limited
   2605      * in that an audio track can only be selected in the <em>Prepared</em> state.
   2606      * </p>
   2607      * @param index the index of the track to be selected. The valid range of the index
   2608      * is 0..total number of track - 1. The total number of tracks as well as the type of
   2609      * each individual track can be found by calling {@link #getTrackInfo()} method.
   2610      * @throws IllegalStateException if called in an invalid state.
   2611      *
   2612      * @see android.media.MediaPlayer#getTrackInfo
   2613      */
   2614     public void selectTrack(int index) throws IllegalStateException {
   2615         selectOrDeselectTrack(index, true /* select */);
   2616     }
   2617 
   2618     /**
   2619      * Deselect a track.
   2620      * <p>
   2621      * Currently, the track must be a timed text track and no audio or video tracks can be
   2622      * deselected. If the timed text track identified by index has not been
   2623      * selected before, it throws an exception.
   2624      * </p>
   2625      * @param index the index of the track to be deselected. The valid range of the index
   2626      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
   2627      * each individual track can be found by calling {@link #getTrackInfo()} method.
   2628      * @throws IllegalStateException if called in an invalid state.
   2629      *
   2630      * @see android.media.MediaPlayer#getTrackInfo
   2631      */
   2632     public void deselectTrack(int index) throws IllegalStateException {
   2633         selectOrDeselectTrack(index, false /* select */);
   2634     }
   2635 
   2636     private void selectOrDeselectTrack(int index, boolean select)
   2637             throws IllegalStateException {
   2638         // handle subtitle track through subtitle controller
   2639         populateInbandTracks();
   2640 
   2641         Pair<Integer,SubtitleTrack> p = null;
   2642         try {
   2643             p = mIndexTrackPairs.get(index);
   2644         } catch (ArrayIndexOutOfBoundsException e) {
   2645             // ignore bad index
   2646             return;
   2647         }
   2648 
   2649         SubtitleTrack track = p.second;
   2650         if (track == null) {
   2651             // inband (de)select
   2652             selectOrDeselectInbandTrack(p.first, select);
   2653             return;
   2654         }
   2655 
   2656         if (mSubtitleController == null) {
   2657             return;
   2658         }
   2659 
   2660         if (!select) {
   2661             // out-of-band deselect
   2662             if (mSubtitleController.getSelectedTrack() == track) {
   2663                 mSubtitleController.selectTrack(null);
   2664             } else {
   2665                 Log.w(TAG, "trying to deselect track that was not selected");
   2666             }
   2667             return;
   2668         }
   2669 
   2670         // out-of-band select
   2671         if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
   2672             int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT);
   2673             synchronized (mIndexTrackPairs) {
   2674                 if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) {
   2675                     Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex);
   2676                     if (p2.first != null && p2.second == null) {
   2677                         // deselect inband counterpart
   2678                         selectOrDeselectInbandTrack(p2.first, false);
   2679                     }
   2680                 }
   2681             }
   2682         }
   2683         mSubtitleController.selectTrack(track);
   2684     }
   2685 
   2686     private void selectOrDeselectInbandTrack(int index, boolean select)
   2687             throws IllegalStateException {
   2688         Parcel request = Parcel.obtain();
   2689         Parcel reply = Parcel.obtain();
   2690         try {
   2691             request.writeInterfaceToken(IMEDIA_PLAYER);
   2692             request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK);
   2693             request.writeInt(index);
   2694             invoke(request, reply);
   2695         } finally {
   2696             request.recycle();
   2697             reply.recycle();
   2698         }
   2699     }
   2700 
   2701 
   2702     /**
   2703      * @param reply Parcel with audio/video duration info for battery
   2704                     tracking usage
   2705      * @return The status code.
   2706      * {@hide}
   2707      */
   2708     public native static int native_pullBatteryData(Parcel reply);
   2709 
   2710     /**
   2711      * Sets the target UDP re-transmit endpoint for the low level player.
   2712      * Generally, the address portion of the endpoint is an IP multicast
   2713      * address, although a unicast address would be equally valid.  When a valid
   2714      * retransmit endpoint has been set, the media player will not decode and
   2715      * render the media presentation locally.  Instead, the player will attempt
   2716      * to re-multiplex its media data using the Android@Home RTP profile and
   2717      * re-transmit to the target endpoint.  Receiver devices (which may be
   2718      * either the same as the transmitting device or different devices) may
   2719      * instantiate, prepare, and start a receiver player using a setDataSource
   2720      * URL of the form...
   2721      *
   2722      * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
   2723      *
   2724      * to receive, decode and render the re-transmitted content.
   2725      *
   2726      * setRetransmitEndpoint may only be called before setDataSource has been
   2727      * called; while the player is in the Idle state.
   2728      *
   2729      * @param endpoint the address and UDP port of the re-transmission target or
   2730      * null if no re-transmission is to be performed.
   2731      * @throws IllegalStateException if it is called in an invalid state
   2732      * @throws IllegalArgumentException if the retransmit endpoint is supplied,
   2733      * but invalid.
   2734      *
   2735      * {@hide} pending API council
   2736      */
   2737     public void setRetransmitEndpoint(InetSocketAddress endpoint)
   2738             throws IllegalStateException, IllegalArgumentException
   2739     {
   2740         String addrString = null;
   2741         int port = 0;
   2742 
   2743         if (null != endpoint) {
   2744             addrString = endpoint.getAddress().getHostAddress();
   2745             port = endpoint.getPort();
   2746         }
   2747 
   2748         int ret = native_setRetransmitEndpoint(addrString, port);
   2749         if (ret != 0) {
   2750             throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
   2751         }
   2752     }
   2753 
   2754     private native final int native_setRetransmitEndpoint(String addrString, int port);
   2755 
   2756     @Override
   2757     protected void finalize() { native_finalize(); }
   2758 
   2759     /* Do not change these values without updating their counterparts
   2760      * in include/media/mediaplayer.h!
   2761      */
   2762     private static final int MEDIA_NOP = 0; // interface test message
   2763     private static final int MEDIA_PREPARED = 1;
   2764     private static final int MEDIA_PLAYBACK_COMPLETE = 2;
   2765     private static final int MEDIA_BUFFERING_UPDATE = 3;
   2766     private static final int MEDIA_SEEK_COMPLETE = 4;
   2767     private static final int MEDIA_SET_VIDEO_SIZE = 5;
   2768     private static final int MEDIA_STARTED = 6;
   2769     private static final int MEDIA_PAUSED = 7;
   2770     private static final int MEDIA_STOPPED = 8;
   2771     private static final int MEDIA_SKIPPED = 9;
   2772     private static final int MEDIA_TIMED_TEXT = 99;
   2773     private static final int MEDIA_ERROR = 100;
   2774     private static final int MEDIA_INFO = 200;
   2775     private static final int MEDIA_SUBTITLE_DATA = 201;
   2776     private static final int MEDIA_META_DATA = 202;
   2777 
   2778     private TimeProvider mTimeProvider;
   2779 
   2780     /** @hide */
   2781     public MediaTimeProvider getMediaTimeProvider() {
   2782         if (mTimeProvider == null) {
   2783             mTimeProvider = new TimeProvider(this);
   2784         }
   2785         return mTimeProvider;
   2786     }
   2787 
   2788     private class EventHandler extends Handler
   2789     {
   2790         private MediaPlayer mMediaPlayer;
   2791 
   2792         public EventHandler(MediaPlayer mp, Looper looper) {
   2793             super(looper);
   2794             mMediaPlayer = mp;
   2795         }
   2796 
   2797         @Override
   2798         public void handleMessage(Message msg) {
   2799             if (mMediaPlayer.mNativeContext == 0) {
   2800                 Log.w(TAG, "mediaplayer went away with unhandled events");
   2801                 return;
   2802             }
   2803             switch(msg.what) {
   2804             case MEDIA_PREPARED:
   2805                 try {
   2806                     scanInternalSubtitleTracks();
   2807                 } catch (RuntimeException e) {
   2808                     // send error message instead of crashing;
   2809                     // send error message instead of inlining a call to onError
   2810                     // to avoid code duplication.
   2811                     Message msg2 = obtainMessage(
   2812                             MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
   2813                     sendMessage(msg2);
   2814                 }
   2815                 if (mOnPreparedListener != null)
   2816                     mOnPreparedListener.onPrepared(mMediaPlayer);
   2817                 return;
   2818 
   2819             case MEDIA_PLAYBACK_COMPLETE:
   2820                 if (mOnCompletionListener != null)
   2821                     mOnCompletionListener.onCompletion(mMediaPlayer);
   2822                 stayAwake(false);
   2823                 return;
   2824 
   2825             case MEDIA_STOPPED:
   2826                 {
   2827                     TimeProvider timeProvider = mTimeProvider;
   2828                     if (timeProvider != null) {
   2829                         timeProvider.onStopped();
   2830                     }
   2831                 }
   2832                 break;
   2833 
   2834             case MEDIA_STARTED:
   2835             case MEDIA_PAUSED:
   2836                 {
   2837                     TimeProvider timeProvider = mTimeProvider;
   2838                     if (timeProvider != null) {
   2839                         timeProvider.onPaused(msg.what == MEDIA_PAUSED);
   2840                     }
   2841                 }
   2842                 break;
   2843 
   2844             case MEDIA_BUFFERING_UPDATE:
   2845                 if (mOnBufferingUpdateListener != null)
   2846                     mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
   2847                 return;
   2848 
   2849             case MEDIA_SEEK_COMPLETE:
   2850                 if (mOnSeekCompleteListener != null) {
   2851                     mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
   2852                 }
   2853                 // fall through
   2854 
   2855             case MEDIA_SKIPPED:
   2856                 {
   2857                     TimeProvider timeProvider = mTimeProvider;
   2858                     if (timeProvider != null) {
   2859                         timeProvider.onSeekComplete(mMediaPlayer);
   2860                     }
   2861                 }
   2862                 return;
   2863 
   2864             case MEDIA_SET_VIDEO_SIZE:
   2865                 if (mOnVideoSizeChangedListener != null) {
   2866                     mOnVideoSizeChangedListener.onVideoSizeChanged(
   2867                         mMediaPlayer, msg.arg1, msg.arg2);
   2868                 }
   2869                 return;
   2870 
   2871             case MEDIA_ERROR:
   2872                 Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
   2873                 boolean error_was_handled = false;
   2874                 if (mOnErrorListener != null) {
   2875                     error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
   2876                 }
   2877                 if (mOnCompletionListener != null && ! error_was_handled) {
   2878                     mOnCompletionListener.onCompletion(mMediaPlayer);
   2879                 }
   2880                 stayAwake(false);
   2881                 return;
   2882 
   2883             case MEDIA_INFO:
   2884                 switch (msg.arg1) {
   2885                 case MEDIA_INFO_VIDEO_TRACK_LAGGING:
   2886                     Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
   2887                     break;
   2888                 case MEDIA_INFO_METADATA_UPDATE:
   2889                     try {
   2890                         scanInternalSubtitleTracks();
   2891                     } catch (RuntimeException e) {
   2892                         Message msg2 = obtainMessage(
   2893                                 MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_UNSUPPORTED, null);
   2894                         sendMessage(msg2);
   2895                     }
   2896                     // fall through
   2897 
   2898                 case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
   2899                     msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
   2900                     // update default track selection
   2901                     if (mSubtitleController != null) {
   2902                         mSubtitleController.selectDefaultTrack();
   2903                     }
   2904                     break;
   2905                 case MEDIA_INFO_BUFFERING_START:
   2906                 case MEDIA_INFO_BUFFERING_END:
   2907                     TimeProvider timeProvider = mTimeProvider;
   2908                     if (timeProvider != null) {
   2909                         timeProvider.onBuffering(msg.arg1 == MEDIA_INFO_BUFFERING_START);
   2910                     }
   2911                     break;
   2912                 }
   2913 
   2914                 if (mOnInfoListener != null) {
   2915                     mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
   2916                 }
   2917                 // No real default action so far.
   2918                 return;
   2919             case MEDIA_TIMED_TEXT:
   2920                 if (mOnTimedTextListener == null)
   2921                     return;
   2922                 if (msg.obj == null) {
   2923                     mOnTimedTextListener.onTimedText(mMediaPlayer, null);
   2924                 } else {
   2925                     if (msg.obj instanceof Parcel) {
   2926                         Parcel parcel = (Parcel)msg.obj;
   2927                         TimedText text = new TimedText(parcel);
   2928                         parcel.recycle();
   2929                         mOnTimedTextListener.onTimedText(mMediaPlayer, text);
   2930                     }
   2931                 }
   2932                 return;
   2933 
   2934             case MEDIA_SUBTITLE_DATA:
   2935                 if (mOnSubtitleDataListener == null) {
   2936                     return;
   2937                 }
   2938                 if (msg.obj instanceof Parcel) {
   2939                     Parcel parcel = (Parcel) msg.obj;
   2940                     SubtitleData data = new SubtitleData(parcel);
   2941                     parcel.recycle();
   2942                     mOnSubtitleDataListener.onSubtitleData(mMediaPlayer, data);
   2943                 }
   2944                 return;
   2945 
   2946             case MEDIA_META_DATA:
   2947                 if (mOnTimedMetaDataAvailableListener == null) {
   2948                     return;
   2949                 }
   2950                 if (msg.obj instanceof Parcel) {
   2951                     Parcel parcel = (Parcel) msg.obj;
   2952                     TimedMetaData data = TimedMetaData.createTimedMetaDataFromParcel(parcel);
   2953                     parcel.recycle();
   2954                     mOnTimedMetaDataAvailableListener.onTimedMetaDataAvailable(mMediaPlayer, data);
   2955                 }
   2956                 return;
   2957 
   2958             case MEDIA_NOP: // interface test message - ignore
   2959                 break;
   2960 
   2961             default:
   2962                 Log.e(TAG, "Unknown message type " + msg.what);
   2963                 return;
   2964             }
   2965         }
   2966     }
   2967 
   2968     /*
   2969      * Called from native code when an interesting event happens.  This method
   2970      * just uses the EventHandler system to post the event back to the main app thread.
   2971      * We use a weak reference to the original MediaPlayer object so that the native
   2972      * code is safe from the object disappearing from underneath it.  (This is
   2973      * the cookie passed to native_setup().)
   2974      */
   2975     private static void postEventFromNative(Object mediaplayer_ref,
   2976                                             int what, int arg1, int arg2, Object obj)
   2977     {
   2978         MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
   2979         if (mp == null) {
   2980             return;
   2981         }
   2982 
   2983         if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
   2984             // this acquires the wakelock if needed, and sets the client side state
   2985             mp.start();
   2986         }
   2987         if (mp.mEventHandler != null) {
   2988             Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
   2989             mp.mEventHandler.sendMessage(m);
   2990         }
   2991     }
   2992 
   2993     /**
   2994      * Interface definition for a callback to be invoked when the media
   2995      * source is ready for playback.
   2996      */
   2997     public interface OnPreparedListener
   2998     {
   2999         /**
   3000          * Called when the media file is ready for playback.
   3001          *
   3002          * @param mp the MediaPlayer that is ready for playback
   3003          */
   3004         void onPrepared(MediaPlayer mp);
   3005     }
   3006 
   3007     /**
   3008      * Register a callback to be invoked when the media source is ready
   3009      * for playback.
   3010      *
   3011      * @param listener the callback that will be run
   3012      */
   3013     public void setOnPreparedListener(OnPreparedListener listener)
   3014     {
   3015         mOnPreparedListener = listener;
   3016     }
   3017 
   3018     private OnPreparedListener mOnPreparedListener;
   3019 
   3020     /**
   3021      * Interface definition for a callback to be invoked when playback of
   3022      * a media source has completed.
   3023      */
   3024     public interface OnCompletionListener
   3025     {
   3026         /**
   3027          * Called when the end of a media source is reached during playback.
   3028          *
   3029          * @param mp the MediaPlayer that reached the end of the file
   3030          */
   3031         void onCompletion(MediaPlayer mp);
   3032     }
   3033 
   3034     /**
   3035      * Register a callback to be invoked when the end of a media source
   3036      * has been reached during playback.
   3037      *
   3038      * @param listener the callback that will be run
   3039      */
   3040     public void setOnCompletionListener(OnCompletionListener listener)
   3041     {
   3042         mOnCompletionListener = listener;
   3043     }
   3044 
   3045     private OnCompletionListener mOnCompletionListener;
   3046 
   3047     /**
   3048      * Interface definition of a callback to be invoked indicating buffering
   3049      * status of a media resource being streamed over the network.
   3050      */
   3051     public interface OnBufferingUpdateListener
   3052     {
   3053         /**
   3054          * Called to update status in buffering a media stream received through
   3055          * progressive HTTP download. The received buffering percentage
   3056          * indicates how much of the content has been buffered or played.
   3057          * For example a buffering update of 80 percent when half the content
   3058          * has already been played indicates that the next 30 percent of the
   3059          * content to play has been buffered.
   3060          *
   3061          * @param mp      the MediaPlayer the update pertains to
   3062          * @param percent the percentage (0-100) of the content
   3063          *                that has been buffered or played thus far
   3064          */
   3065         void onBufferingUpdate(MediaPlayer mp, int percent);
   3066     }
   3067 
   3068     /**
   3069      * Register a callback to be invoked when the status of a network
   3070      * stream's buffer has changed.
   3071      *
   3072      * @param listener the callback that will be run.
   3073      */
   3074     public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener)
   3075     {
   3076         mOnBufferingUpdateListener = listener;
   3077     }
   3078 
   3079     private OnBufferingUpdateListener mOnBufferingUpdateListener;
   3080 
   3081     /**
   3082      * Interface definition of a callback to be invoked indicating
   3083      * the completion of a seek operation.
   3084      */
   3085     public interface OnSeekCompleteListener
   3086     {
   3087         /**
   3088          * Called to indicate the completion of a seek operation.
   3089          *
   3090          * @param mp the MediaPlayer that issued the seek operation
   3091          */
   3092         public void onSeekComplete(MediaPlayer mp);
   3093     }
   3094 
   3095     /**
   3096      * Register a callback to be invoked when a seek operation has been
   3097      * completed.
   3098      *
   3099      * @param listener the callback that will be run
   3100      */
   3101     public void setOnSeekCompleteListener(OnSeekCompleteListener listener)
   3102     {
   3103         mOnSeekCompleteListener = listener;
   3104     }
   3105 
   3106     private OnSeekCompleteListener mOnSeekCompleteListener;
   3107 
   3108     /**
   3109      * Interface definition of a callback to be invoked when the
   3110      * video size is first known or updated
   3111      */
   3112     public interface OnVideoSizeChangedListener
   3113     {
   3114         /**
   3115          * Called to indicate the video size
   3116          *
   3117          * The video size (width and height) could be 0 if there was no video,
   3118          * no display surface was set, or the value was not determined yet.
   3119          *
   3120          * @param mp        the MediaPlayer associated with this callback
   3121          * @param width     the width of the video
   3122          * @param height    the height of the video
   3123          */
   3124         public void onVideoSizeChanged(MediaPlayer mp, int width, int height);
   3125     }
   3126 
   3127     /**
   3128      * Register a callback to be invoked when the video size is
   3129      * known or updated.
   3130      *
   3131      * @param listener the callback that will be run
   3132      */
   3133     public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener)
   3134     {
   3135         mOnVideoSizeChangedListener = listener;
   3136     }
   3137 
   3138     private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
   3139 
   3140     /**
   3141      * Interface definition of a callback to be invoked when a
   3142      * timed text is available for display.
   3143      */
   3144     public interface OnTimedTextListener
   3145     {
   3146         /**
   3147          * Called to indicate an avaliable timed text
   3148          *
   3149          * @param mp             the MediaPlayer associated with this callback
   3150          * @param text           the timed text sample which contains the text
   3151          *                       needed to be displayed and the display format.
   3152          */
   3153         public void onTimedText(MediaPlayer mp, TimedText text);
   3154     }
   3155 
   3156     /**
   3157      * Register a callback to be invoked when a timed text is available
   3158      * for display.
   3159      *
   3160      * @param listener the callback that will be run
   3161      */
   3162     public void setOnTimedTextListener(OnTimedTextListener listener)
   3163     {
   3164         mOnTimedTextListener = listener;
   3165     }
   3166 
   3167     private OnTimedTextListener mOnTimedTextListener;
   3168 
   3169     /**
   3170      * Interface definition of a callback to be invoked when a
   3171      * track has data available.
   3172      *
   3173      * @hide
   3174      */
   3175     public interface OnSubtitleDataListener
   3176     {
   3177         public void onSubtitleData(MediaPlayer mp, SubtitleData data);
   3178     }
   3179 
   3180     /**
   3181      * Register a callback to be invoked when a track has data available.
   3182      *
   3183      * @param listener the callback that will be run
   3184      *
   3185      * @hide
   3186      */
   3187     public void setOnSubtitleDataListener(OnSubtitleDataListener listener)
   3188     {
   3189         mOnSubtitleDataListener = listener;
   3190     }
   3191 
   3192     private OnSubtitleDataListener mOnSubtitleDataListener;
   3193 
   3194     /**
   3195      * Interface definition of a callback to be invoked when a
   3196      * track has timed metadata available.
   3197      *
   3198      * @see MediaPlayer#setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener)
   3199      */
   3200     public interface OnTimedMetaDataAvailableListener
   3201     {
   3202         /**
   3203          * Called to indicate avaliable timed metadata
   3204          * <p>
   3205          * This method will be called as timed metadata is extracted from the media,
   3206          * in the same order as it occurs in the media. The timing of this event is
   3207          * not controlled by the associated timestamp.
   3208          *
   3209          * @param mp             the MediaPlayer associated with this callback
   3210          * @param data           the timed metadata sample associated with this event
   3211          */
   3212         public void onTimedMetaDataAvailable(MediaPlayer mp, TimedMetaData data);
   3213     }
   3214 
   3215     /**
   3216      * Register a callback to be invoked when a selected track has timed metadata available.
   3217      * <p>
   3218      * Currently only HTTP live streaming data URI's embedded with timed ID3 tags generates
   3219      * {@link TimedMetaData}.
   3220      *
   3221      * @see MediaPlayer#selectTrack(int)
   3222      * @see MediaPlayer.OnTimedMetaDataAvailableListener
   3223      * @see TimedMetaData
   3224      *
   3225      * @param listener the callback that will be run
   3226      */
   3227     public void setOnTimedMetaDataAvailableListener(OnTimedMetaDataAvailableListener listener)
   3228     {
   3229         mOnTimedMetaDataAvailableListener = listener;
   3230     }
   3231 
   3232     private OnTimedMetaDataAvailableListener mOnTimedMetaDataAvailableListener;
   3233 
   3234     /* Do not change these values without updating their counterparts
   3235      * in include/media/mediaplayer.h!
   3236      */
   3237     /** Unspecified media player error.
   3238      * @see android.media.MediaPlayer.OnErrorListener
   3239      */
   3240     public static final int MEDIA_ERROR_UNKNOWN = 1;
   3241 
   3242     /** Media server died. In this case, the application must release the
   3243      * MediaPlayer object and instantiate a new one.
   3244      * @see android.media.MediaPlayer.OnErrorListener
   3245      */
   3246     public static final int MEDIA_ERROR_SERVER_DIED = 100;
   3247 
   3248     /** The video is streamed and its container is not valid for progressive
   3249      * playback i.e the video's index (e.g moov atom) is not at the start of the
   3250      * file.
   3251      * @see android.media.MediaPlayer.OnErrorListener
   3252      */
   3253     public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
   3254 
   3255     /** File or network related operation errors. */
   3256     public static final int MEDIA_ERROR_IO = -1004;
   3257     /** Bitstream is not conforming to the related coding standard or file spec. */
   3258     public static final int MEDIA_ERROR_MALFORMED = -1007;
   3259     /** Bitstream is conforming to the related coding standard or file spec, but
   3260      * the media framework does not support the feature. */
   3261     public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
   3262     /** Some operation takes too long to complete, usually more than 3-5 seconds. */
   3263     public static final int MEDIA_ERROR_TIMED_OUT = -110;
   3264 
   3265     /** Unspecified low-level system error. This value originated from UNKNOWN_ERROR in
   3266      * system/core/include/utils/Errors.h
   3267      * @see android.media.MediaPlayer.OnErrorListener
   3268      * @hide
   3269      */
   3270     public static final int MEDIA_ERROR_SYSTEM = -2147483648;
   3271 
   3272     /**
   3273      * Interface definition of a callback to be invoked when there
   3274      * has been an error during an asynchronous operation (other errors
   3275      * will throw exceptions at method call time).
   3276      */
   3277     public interface OnErrorListener
   3278     {
   3279         /**
   3280          * Called to indicate an error.
   3281          *
   3282          * @param mp      the MediaPlayer the error pertains to
   3283          * @param what    the type of error that has occurred:
   3284          * <ul>
   3285          * <li>{@link #MEDIA_ERROR_UNKNOWN}
   3286          * <li>{@link #MEDIA_ERROR_SERVER_DIED}
   3287          * </ul>
   3288          * @param extra an extra code, specific to the error. Typically
   3289          * implementation dependent.
   3290          * <ul>
   3291          * <li>{@link #MEDIA_ERROR_IO}
   3292          * <li>{@link #MEDIA_ERROR_MALFORMED}
   3293          * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
   3294          * <li>{@link #MEDIA_ERROR_TIMED_OUT}
   3295          * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error.
   3296          * </ul>
   3297          * @return True if the method handled the error, false if it didn't.
   3298          * Returning false, or not having an OnErrorListener at all, will
   3299          * cause the OnCompletionListener to be called.
   3300          */
   3301         boolean onError(MediaPlayer mp, int what, int extra);
   3302     }
   3303 
   3304     /**
   3305      * Register a callback to be invoked when an error has happened
   3306      * during an asynchronous operation.
   3307      *
   3308      * @param listener the callback that will be run
   3309      */
   3310     public void setOnErrorListener(OnErrorListener listener)
   3311     {
   3312         mOnErrorListener = listener;
   3313     }
   3314 
   3315     private OnErrorListener mOnErrorListener;
   3316 
   3317 
   3318     /* Do not change these values without updating their counterparts
   3319      * in include/media/mediaplayer.h!
   3320      */
   3321     /** Unspecified media player info.
   3322      * @see android.media.MediaPlayer.OnInfoListener
   3323      */
   3324     public static final int MEDIA_INFO_UNKNOWN = 1;
   3325 
   3326     /** The player was started because it was used as the next player for another
   3327      * player, which just completed playback.
   3328      * @see android.media.MediaPlayer.OnInfoListener
   3329      * @hide
   3330      */
   3331     public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
   3332 
   3333     /** The player just pushed the very first video frame for rendering.
   3334      * @see android.media.MediaPlayer.OnInfoListener
   3335      */
   3336     public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
   3337 
   3338     /** The video is too complex for the decoder: it can't decode frames fast
   3339      *  enough. Possibly only the audio plays fine at this stage.
   3340      * @see android.media.MediaPlayer.OnInfoListener
   3341      */
   3342     public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
   3343 
   3344     /** MediaPlayer is temporarily pausing playback internally in order to
   3345      * buffer more data.
   3346      * @see android.media.MediaPlayer.OnInfoListener
   3347      */
   3348     public static final int MEDIA_INFO_BUFFERING_START = 701;
   3349 
   3350     /** MediaPlayer is resuming playback after filling buffers.
   3351      * @see android.media.MediaPlayer.OnInfoListener
   3352      */
   3353     public static final int MEDIA_INFO_BUFFERING_END = 702;
   3354 
   3355     /** Estimated network bandwidth information (kbps) is available; currently this event fires
   3356      * simultaneously as {@link #MEDIA_INFO_BUFFERING_START} and {@link #MEDIA_INFO_BUFFERING_END}
   3357      * when playing network files.
   3358      * @see android.media.MediaPlayer.OnInfoListener
   3359      * @hide
   3360      */
   3361     public static final int MEDIA_INFO_NETWORK_BANDWIDTH = 703;
   3362 
   3363     /** Bad interleaving means that a media has been improperly interleaved or
   3364      * not interleaved at all, e.g has all the video samples first then all the
   3365      * audio ones. Video is playing but a lot of disk seeks may be happening.
   3366      * @see android.media.MediaPlayer.OnInfoListener
   3367      */
   3368     public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
   3369 
   3370     /** The media cannot be seeked (e.g live stream)
   3371      * @see android.media.MediaPlayer.OnInfoListener
   3372      */
   3373     public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
   3374 
   3375     /** A new set of metadata is available.
   3376      * @see android.media.MediaPlayer.OnInfoListener
   3377      */
   3378     public static final int MEDIA_INFO_METADATA_UPDATE = 802;
   3379 
   3380     /** A new set of external-only metadata is available.  Used by
   3381      *  JAVA framework to avoid triggering track scanning.
   3382      * @hide
   3383      */
   3384     public static final int MEDIA_INFO_EXTERNAL_METADATA_UPDATE = 803;
   3385 
   3386     /** Failed to handle timed text track properly.
   3387      * @see android.media.MediaPlayer.OnInfoListener
   3388      *
   3389      * {@hide}
   3390      */
   3391     public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
   3392 
   3393     /** Subtitle track was not supported by the media framework.
   3394      * @see android.media.MediaPlayer.OnInfoListener
   3395      */
   3396     public static final int MEDIA_INFO_UNSUPPORTED_SUBTITLE = 901;
   3397 
   3398     /** Reading the subtitle track takes too long.
   3399      * @see android.media.MediaPlayer.OnInfoListener
   3400      */
   3401     public static final int MEDIA_INFO_SUBTITLE_TIMED_OUT = 902;
   3402 
   3403     /**
   3404      * Interface definition of a callback to be invoked to communicate some
   3405      * info and/or warning about the media or its playback.
   3406      */
   3407     public interface OnInfoListener
   3408     {
   3409         /**
   3410          * Called to indicate an info or a warning.
   3411          *
   3412          * @param mp      the MediaPlayer the info pertains to.
   3413          * @param what    the type of info or warning.
   3414          * <ul>
   3415          * <li>{@link #MEDIA_INFO_UNKNOWN}
   3416          * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
   3417          * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
   3418          * <li>{@link #MEDIA_INFO_BUFFERING_START}
   3419          * <li>{@link #MEDIA_INFO_BUFFERING_END}
   3420          * <li><code>MEDIA_INFO_NETWORK_BANDWIDTH (703)</code> -
   3421          *     bandwidth information is available (as <code>extra</code> kbps)
   3422          * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
   3423          * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
   3424          * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
   3425          * <li>{@link #MEDIA_INFO_UNSUPPORTED_SUBTITLE}
   3426          * <li>{@link #MEDIA_INFO_SUBTITLE_TIMED_OUT}
   3427          * </ul>
   3428          * @param extra an extra code, specific to the info. Typically
   3429          * implementation dependent.
   3430          * @return True if the method handled the info, false if it didn't.
   3431          * Returning false, or not having an OnErrorListener at all, will
   3432          * cause the info to be discarded.
   3433          */
   3434         boolean onInfo(MediaPlayer mp, int what, int extra);
   3435     }
   3436 
   3437     /**
   3438      * Register a callback to be invoked when an info/warning is available.
   3439      *
   3440      * @param listener the callback that will be run
   3441      */
   3442     public void setOnInfoListener(OnInfoListener listener)
   3443     {
   3444         mOnInfoListener = listener;
   3445     }
   3446 
   3447     private OnInfoListener mOnInfoListener;
   3448 
   3449     /*
   3450      * Test whether a given video scaling mode is supported.
   3451      */
   3452     private boolean isVideoScalingModeSupported(int mode) {
   3453         return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
   3454                 mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
   3455     }
   3456 
   3457     /** @hide */
   3458     static class TimeProvider implements MediaPlayer.OnSeekCompleteListener,
   3459             MediaTimeProvider {
   3460         private static final String TAG = "MTP";
   3461         private static final long MAX_NS_WITHOUT_POSITION_CHECK = 5000000000L;
   3462         private static final long MAX_EARLY_CALLBACK_US = 1000;
   3463         private static final long TIME_ADJUSTMENT_RATE = 2;  /* meaning 1/2 */
   3464         private long mLastTimeUs = 0;
   3465         private MediaPlayer mPlayer;
   3466         private boolean mPaused = true;
   3467         private boolean mStopped = true;
   3468         private boolean mBuffering;
   3469         private long mLastReportedTime;
   3470         private long mTimeAdjustment;
   3471         // since we are expecting only a handful listeners per stream, there is
   3472         // no need for log(N) search performance
   3473         private MediaTimeProvider.OnMediaTimeListener mListeners[];
   3474         private long mTimes[];
   3475         private long mLastNanoTime;
   3476         private Handler mEventHandler;
   3477         private boolean mRefresh = false;
   3478         private boolean mPausing = false;
   3479         private boolean mSeeking = false;
   3480         private static final int NOTIFY = 1;
   3481         private static final int NOTIFY_TIME = 0;
   3482         private static final int REFRESH_AND_NOTIFY_TIME = 1;
   3483         private static final int NOTIFY_STOP = 2;
   3484         private static final int NOTIFY_SEEK = 3;
   3485         private HandlerThread mHandlerThread;
   3486 
   3487         /** @hide */
   3488         public boolean DEBUG = false;
   3489 
   3490         public TimeProvider(MediaPlayer mp) {
   3491             mPlayer = mp;
   3492             try {
   3493                 getCurrentTimeUs(true, false);
   3494             } catch (IllegalStateException e) {
   3495                 // we assume starting position
   3496                 mRefresh = true;
   3497             }
   3498 
   3499             Looper looper;
   3500             if ((looper = Looper.myLooper()) == null &&
   3501                 (looper = Looper.getMainLooper()) == null) {
   3502                 // Create our own looper here in case MP was created without one
   3503                 mHandlerThread = new HandlerThread("MediaPlayerMTPEventThread",
   3504                       Process.THREAD_PRIORITY_FOREGROUND);
   3505                 mHandlerThread.start();
   3506                 looper = mHandlerThread.getLooper();
   3507             }
   3508             mEventHandler = new EventHandler(looper);
   3509 
   3510             mListeners = new MediaTimeProvider.OnMediaTimeListener[0];
   3511             mTimes = new long[0];
   3512             mLastTimeUs = 0;
   3513             mTimeAdjustment = 0;
   3514         }
   3515 
   3516         private void scheduleNotification(int type, long delayUs) {
   3517             // ignore time notifications until seek is handled
   3518             if (mSeeking &&
   3519                     (type == NOTIFY_TIME || type == REFRESH_AND_NOTIFY_TIME)) {
   3520                 return;
   3521             }
   3522 
   3523             if (DEBUG) Log.v(TAG, "scheduleNotification " + type + " in " + delayUs);
   3524             mEventHandler.removeMessages(NOTIFY);
   3525             Message msg = mEventHandler.obtainMessage(NOTIFY, type, 0);
   3526             mEventHandler.sendMessageDelayed(msg, (int) (delayUs / 1000));
   3527         }
   3528 
   3529         /** @hide */
   3530         public void close() {
   3531             mEventHandler.removeMessages(NOTIFY);
   3532             if (mHandlerThread != null) {
   3533                 mHandlerThread.quitSafely();
   3534                 mHandlerThread = null;
   3535             }
   3536         }
   3537 
   3538         /** @hide */
   3539         protected void finalize() {
   3540             if (mHandlerThread != null) {
   3541                 mHandlerThread.quitSafely();
   3542             }
   3543         }
   3544 
   3545         /** @hide */
   3546         public void onPaused(boolean paused) {
   3547             synchronized(this) {
   3548                 if (DEBUG) Log.d(TAG, "onPaused: " + paused);
   3549                 if (mStopped) { // handle as seek if we were stopped
   3550                     mStopped = false;
   3551                     mSeeking = true;
   3552                     scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   3553                 } else {
   3554                     mPausing = paused;  // special handling if player disappeared
   3555                     mSeeking = false;
   3556                     scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
   3557                 }
   3558             }
   3559         }
   3560 
   3561         /** @hide */
   3562         public void onBuffering(boolean buffering) {
   3563             synchronized (this) {
   3564                 if (DEBUG) Log.d(TAG, "onBuffering: " + buffering);
   3565                 mBuffering = buffering;
   3566                 scheduleNotification(REFRESH_AND_NOTIFY_TIME, 0 /* delay */);
   3567             }
   3568         }
   3569 
   3570         /** @hide */
   3571         public void onStopped() {
   3572             synchronized(this) {
   3573                 if (DEBUG) Log.d(TAG, "onStopped");
   3574                 mPaused = true;
   3575                 mStopped = true;
   3576                 mSeeking = false;
   3577                 mBuffering = false;
   3578                 scheduleNotification(NOTIFY_STOP, 0 /* delay */);
   3579             }
   3580         }
   3581 
   3582         /** @hide */
   3583         @Override
   3584         public void onSeekComplete(MediaPlayer mp) {
   3585             synchronized(this) {
   3586                 mStopped = false;
   3587                 mSeeking = true;
   3588                 scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   3589             }
   3590         }
   3591 
   3592         /** @hide */
   3593         public void onNewPlayer() {
   3594             if (mRefresh) {
   3595                 synchronized(this) {
   3596                     mStopped = false;
   3597                     mSeeking = true;
   3598                     mBuffering = false;
   3599                     scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   3600                 }
   3601             }
   3602         }
   3603 
   3604         private synchronized void notifySeek() {
   3605             mSeeking = false;
   3606             try {
   3607                 long timeUs = getCurrentTimeUs(true, false);
   3608                 if (DEBUG) Log.d(TAG, "onSeekComplete at " + timeUs);
   3609 
   3610                 for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) {
   3611                     if (listener == null) {
   3612                         break;
   3613                     }
   3614                     listener.onSeek(timeUs);
   3615                 }
   3616             } catch (IllegalStateException e) {
   3617                 // we should not be there, but at least signal pause
   3618                 if (DEBUG) Log.d(TAG, "onSeekComplete but no player");
   3619                 mPausing = true;  // special handling if player disappeared
   3620                 notifyTimedEvent(false /* refreshTime */);
   3621             }
   3622         }
   3623 
   3624         private synchronized void notifyStop() {
   3625             for (MediaTimeProvider.OnMediaTimeListener listener: mListeners) {
   3626                 if (listener == null) {
   3627                     break;
   3628                 }
   3629                 listener.onStop();
   3630             }
   3631         }
   3632 
   3633         private int registerListener(MediaTimeProvider.OnMediaTimeListener listener) {
   3634             int i = 0;
   3635             for (; i < mListeners.length; i++) {
   3636                 if (mListeners[i] == listener || mListeners[i] == null) {
   3637                     break;
   3638                 }
   3639             }
   3640 
   3641             // new listener
   3642             if (i >= mListeners.length) {
   3643                 MediaTimeProvider.OnMediaTimeListener[] newListeners =
   3644                     new MediaTimeProvider.OnMediaTimeListener[i + 1];
   3645                 long[] newTimes = new long[i + 1];
   3646                 System.arraycopy(mListeners, 0, newListeners, 0, mListeners.length);
   3647                 System.arraycopy(mTimes, 0, newTimes, 0, mTimes.length);
   3648                 mListeners = newListeners;
   3649                 mTimes = newTimes;
   3650             }
   3651 
   3652             if (mListeners[i] == null) {
   3653                 mListeners[i] = listener;
   3654                 mTimes[i] = MediaTimeProvider.NO_TIME;
   3655             }
   3656             return i;
   3657         }
   3658 
   3659         public void notifyAt(
   3660                 long timeUs, MediaTimeProvider.OnMediaTimeListener listener) {
   3661             synchronized(this) {
   3662                 if (DEBUG) Log.d(TAG, "notifyAt " + timeUs);
   3663                 mTimes[registerListener(listener)] = timeUs;
   3664                 scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   3665             }
   3666         }
   3667 
   3668         public void scheduleUpdate(MediaTimeProvider.OnMediaTimeListener listener) {
   3669             synchronized(this) {
   3670                 if (DEBUG) Log.d(TAG, "scheduleUpdate");
   3671                 int i = registerListener(listener);
   3672 
   3673                 if (!mStopped) {
   3674                     mTimes[i] = 0;
   3675                     scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   3676                 }
   3677             }
   3678         }
   3679 
   3680         public void cancelNotifications(
   3681                 MediaTimeProvider.OnMediaTimeListener listener) {
   3682             synchronized(this) {
   3683                 int i = 0;
   3684                 for (; i < mListeners.length; i++) {
   3685                     if (mListeners[i] == listener) {
   3686                         System.arraycopy(mListeners, i + 1,
   3687                                 mListeners, i, mListeners.length - i - 1);
   3688                         System.arraycopy(mTimes, i + 1,
   3689                                 mTimes, i, mTimes.length - i - 1);
   3690                         mListeners[mListeners.length - 1] = null;
   3691                         mTimes[mTimes.length - 1] = NO_TIME;
   3692                         break;
   3693                     } else if (mListeners[i] == null) {
   3694                         break;
   3695                     }
   3696                 }
   3697 
   3698                 scheduleNotification(NOTIFY_TIME, 0 /* delay */);
   3699             }
   3700         }
   3701 
   3702         private synchronized void notifyTimedEvent(boolean refreshTime) {
   3703             // figure out next callback
   3704             long nowUs;
   3705             try {
   3706                 nowUs = getCurrentTimeUs(refreshTime, true);
   3707             } catch (IllegalStateException e) {
   3708                 // assume we paused until new player arrives
   3709                 mRefresh = true;
   3710                 mPausing = true; // this ensures that call succeeds
   3711                 nowUs = getCurrentTimeUs(refreshTime, true);
   3712             }
   3713             long nextTimeUs = nowUs;
   3714 
   3715             if (mSeeking) {
   3716                 // skip timed-event notifications until seek is complete
   3717                 return;
   3718             }
   3719 
   3720             if (DEBUG) {
   3721                 StringBuilder sb = new StringBuilder();
   3722                 sb.append("notifyTimedEvent(").append(mLastTimeUs).append(" -> ")
   3723                         .append(nowUs).append(") from {");
   3724                 boolean first = true;
   3725                 for (long time: mTimes) {
   3726                     if (time == NO_TIME) {
   3727                         continue;
   3728                     }
   3729                     if (!first) sb.append(", ");
   3730                     sb.append(time);
   3731                     first = false;
   3732                 }
   3733                 sb.append("}");
   3734                 Log.d(TAG, sb.toString());
   3735             }
   3736 
   3737             Vector<MediaTimeProvider.OnMediaTimeListener> activatedListeners =
   3738                 new Vector<MediaTimeProvider.OnMediaTimeListener>();
   3739             for (int ix = 0; ix < mTimes.length; ix++) {
   3740                 if (mListeners[ix] == null) {
   3741                     break;
   3742                 }
   3743                 if (mTimes[ix] <= NO_TIME) {
   3744                     // ignore, unless we were stopped
   3745                 } else if (mTimes[ix] <= nowUs + MAX_EARLY_CALLBACK_US) {
   3746                     activatedListeners.add(mListeners[ix]);
   3747                     if (DEBUG) Log.d(TAG, "removed");
   3748                     mTimes[ix] = NO_TIME;
   3749                 } else if (nextTimeUs == nowUs || mTimes[ix] < nextTimeUs) {
   3750                     nextTimeUs = mTimes[ix];
   3751                 }
   3752             }
   3753 
   3754             if (nextTimeUs > nowUs && !mPaused) {
   3755                 // schedule callback at nextTimeUs
   3756                 if (DEBUG) Log.d(TAG, "scheduling for " + nextTimeUs + " and " + nowUs);
   3757                 scheduleNotification(NOTIFY_TIME, nextTimeUs - nowUs);
   3758             } else {
   3759                 mEventHandler.removeMessages(NOTIFY);
   3760                 // no more callbacks
   3761             }
   3762 
   3763             for (MediaTimeProvider.OnMediaTimeListener listener: activatedListeners) {
   3764                 listener.onTimedEvent(nowUs);
   3765             }
   3766         }
   3767 
   3768         private long getEstimatedTime(long nanoTime, boolean monotonic) {
   3769             if (mPaused) {
   3770                 mLastReportedTime = mLastTimeUs + mTimeAdjustment;
   3771             } else {
   3772                 long timeSinceRead = (nanoTime - mLastNanoTime) / 1000;
   3773                 mLastReportedTime = mLastTimeUs + timeSinceRead;
   3774                 if (mTimeAdjustment > 0) {
   3775                     long adjustment =
   3776                         mTimeAdjustment - timeSinceRead / TIME_ADJUSTMENT_RATE;
   3777                     if (adjustment <= 0) {
   3778                         mTimeAdjustment = 0;
   3779                     } else {
   3780                         mLastReportedTime += adjustment;
   3781                     }
   3782                 }
   3783             }
   3784             return mLastReportedTime;
   3785         }
   3786 
   3787         public long getCurrentTimeUs(boolean refreshTime, boolean monotonic)
   3788                 throws IllegalStateException {
   3789             synchronized (this) {
   3790                 // we always refresh the time when the paused-state changes, because
   3791                 // we expect to have received the pause-change event delayed.
   3792                 if (mPaused && !refreshTime) {
   3793                     return mLastReportedTime;
   3794                 }
   3795 
   3796                 long nanoTime = System.nanoTime();
   3797                 if (refreshTime ||
   3798                         nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
   3799                     try {
   3800                         mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
   3801                         mPaused = !mPlayer.isPlaying() || mBuffering;
   3802                         if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
   3803                     } catch (IllegalStateException e) {
   3804                         if (mPausing) {
   3805                             // if we were pausing, get last estimated timestamp
   3806                             mPausing = false;
   3807                             getEstimatedTime(nanoTime, monotonic);
   3808                             mPaused = true;
   3809                             if (DEBUG) Log.d(TAG, "illegal state, but pausing: estimating at " + mLastReportedTime);
   3810                             return mLastReportedTime;
   3811                         }
   3812                         // TODO get time when prepared
   3813                         throw e;
   3814                     }
   3815                     mLastNanoTime = nanoTime;
   3816                     if (monotonic && mLastTimeUs < mLastReportedTime) {
   3817                         /* have to adjust time */
   3818                         mTimeAdjustment = mLastReportedTime - mLastTimeUs;
   3819                         if (mTimeAdjustment > 1000000) {
   3820                             // schedule seeked event if time jumped significantly
   3821                             // TODO: do this properly by introducing an exception
   3822                             mStopped = false;
   3823                             mSeeking = true;
   3824                             scheduleNotification(NOTIFY_SEEK, 0 /* delay */);
   3825                         }
   3826                     } else {
   3827                         mTimeAdjustment = 0;
   3828                     }
   3829                 }
   3830 
   3831                 return getEstimatedTime(nanoTime, monotonic);
   3832             }
   3833         }
   3834 
   3835         private class EventHandler extends Handler {
   3836             public EventHandler(Looper looper) {
   3837                 super(looper);
   3838             }
   3839 
   3840             @Override
   3841             public void handleMessage(Message msg) {
   3842                 if (msg.what == NOTIFY) {
   3843                     switch (msg.arg1) {
   3844                     case NOTIFY_TIME:
   3845                         notifyTimedEvent(false /* refreshTime */);
   3846                         break;
   3847                     case REFRESH_AND_NOTIFY_TIME:
   3848                         notifyTimedEvent(true /* refreshTime */);
   3849                         break;
   3850                     case NOTIFY_STOP:
   3851                         notifyStop();
   3852                         break;
   3853                     case NOTIFY_SEEK:
   3854                         notifySeek();
   3855                         break;
   3856                     }
   3857                 }
   3858             }
   3859         }
   3860     }
   3861 }
   3862