OSDN Git Service

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