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