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