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