OSDN Git Service

DO NOT MERGE. KEY_INTENT shouldn't grant permissions.
[android-x86/frameworks-base.git] / media / java / android / media / MediaPlayer.java
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 }