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