OSDN Git Service

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