MediaPlayer.java revision d5f9fa574da2ee210ac86154ab0aea9fee5e8278
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
17package android.media;
18
19import android.content.BroadcastReceiver;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.res.AssetFileDescriptor;
25import android.net.Proxy;
26import android.net.ProxyProperties;
27import android.net.Uri;
28import android.os.Handler;
29import android.os.Looper;
30import android.os.Message;
31import android.os.Parcel;
32import android.os.Parcelable;
33import android.os.ParcelFileDescriptor;
34import android.os.PowerManager;
35import android.util.Log;
36import android.view.Surface;
37import android.view.SurfaceHolder;
38import android.graphics.Bitmap;
39import android.graphics.SurfaceTexture;
40import android.media.AudioManager;
41
42import java.io.File;
43import java.io.FileDescriptor;
44import java.io.FileInputStream;
45import java.io.IOException;
46import java.net.InetSocketAddress;
47import java.util.Map;
48import java.util.Set;
49import java.lang.ref.WeakReference;
50
51/**
52 * MediaPlayer class can be used to control playback
53 * of audio/video files and streams. An example on how to use the methods in
54 * this class can be found in {@link android.widget.VideoView}.
55 *
56 * <p>Topics covered here are:
57 * <ol>
58 * <li><a href="#StateDiagram">State Diagram</a>
59 * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
60 * <li><a href="#Permissions">Permissions</a>
61 * <li><a href="#Callbacks">Register informational and error callbacks</a>
62 * </ol>
63 *
64 * <div class="special reference">
65 * <h3>Developer Guides</h3>
66 * <p>For more information about how to use MediaPlayer, read the
67 * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
68 * </div>
69 *
70 * <a name="StateDiagram"></a>
71 * <h3>State Diagram</h3>
72 *
73 * <p>Playback control of audio/video files and streams is managed as a state
74 * machine. The following diagram shows the life cycle and the states of a
75 * MediaPlayer object driven by the supported playback control operations.
76 * The ovals represent the states a MediaPlayer object may reside
77 * in. The arcs represent the playback control operations that drive the object
78 * state transition. There are two types of arcs. The arcs with a single arrow
79 * head represent synchronous method calls, while those with
80 * a double arrow head represent asynchronous method calls.</p>
81 *
82 * <p><img src="../../../images/mediaplayer_state_diagram.gif"
83 *         alt="MediaPlayer State diagram"
84 *         border="0" /></p>
85 *
86 * <p>From this state diagram, one can see that a MediaPlayer object has the
87 *    following states:</p>
88 * <ul>
89 *     <li>When a MediaPlayer object is just created using <code>new</code> or
90 *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
91 *         {@link #release()} is called, it is in the <em>End</em> state. Between these
92 *         two states is the life cycle of the MediaPlayer object.
93 *         <ul>
94 *         <li>There is a subtle but important difference between a newly constructed
95 *         MediaPlayer object and the MediaPlayer object after {@link #reset()}
96 *         is called. It is a programming error to invoke methods such
97 *         as {@link #getCurrentPosition()},
98 *         {@link #getDuration()}, {@link #getVideoHeight()},
99 *         {@link #getVideoWidth()}, {@link #setAudioStreamType(int)},
100 *         {@link #setLooping(boolean)},
101 *         {@link #setVolume(float, float)}, {@link #pause()}, {@link #start()},
102 *         {@link #stop()}, {@link #seekTo(int)}, {@link #prepare()} or
103 *         {@link #prepareAsync()} in the <em>Idle</em> state for both cases. If any of these
104 *         methods is called right after a MediaPlayer object is constructed,
105 *         the user supplied callback method OnErrorListener.onError() won't be
106 *         called by the internal player engine and the object state remains
107 *         unchanged; but if these methods are called right after {@link #reset()},
108 *         the user supplied callback method OnErrorListener.onError() will be
109 *         invoked by the internal player engine and the object will be
110 *         transfered to the <em>Error</em> state. </li>
111 *         <li>It is also recommended that once
112 *         a MediaPlayer object is no longer being used, call {@link #release()} immediately
113 *         so that resources used by the internal player engine associated with the
114 *         MediaPlayer object can be released immediately. Resource may include
115 *         singleton resources such as hardware acceleration components and
116 *         failure to call {@link #release()} may cause subsequent instances of
117 *         MediaPlayer objects to fallback to software implementations or fail
118 *         altogether. Once the MediaPlayer
119 *         object is in the <em>End</em> state, it can no longer be used and
120 *         there is no way to bring it back to any other state. </li>
121 *         <li>Furthermore,
122 *         the MediaPlayer objects created using <code>new</code> is in the
123 *         <em>Idle</em> state, while those created with one
124 *         of the overloaded convenient <code>create</code> methods are <em>NOT</em>
125 *         in the <em>Idle</em> state. In fact, the objects are in the <em>Prepared</em>
126 *         state if the creation using <code>create</code> method is successful.
127 *         </li>
128 *         </ul>
129 *         </li>
130 *     <li>In general, some playback control operation may fail due to various
131 *         reasons, such as unsupported audio/video format, poorly interleaved
132 *         audio/video, resolution too high, streaming timeout, and the like.
133 *         Thus, error reporting and recovery is an important concern under
134 *         these circumstances. Sometimes, due to programming errors, invoking a playback
135 *         control operation in an invalid state may also occur. Under all these
136 *         error conditions, the internal player engine invokes a user supplied
137 *         OnErrorListener.onError() method if an OnErrorListener has been
138 *         registered beforehand via
139 *         {@link #setOnErrorListener(android.media.MediaPlayer.OnErrorListener)}.
140 *         <ul>
141 *         <li>It is important to note that once an error occurs, the
142 *         MediaPlayer object enters the <em>Error</em> state (except as noted
143 *         above), even if an error listener has not been registered by the application.</li>
144 *         <li>In order to reuse a MediaPlayer object that is in the <em>
145 *         Error</em> state and recover from the error,
146 *         {@link #reset()} can be called to restore the object to its <em>Idle</em>
147 *         state.</li>
148 *         <li>It is good programming practice to have your application
149 *         register a OnErrorListener to look out for error notifications from
150 *         the internal player engine.</li>
151 *         <li>IllegalStateException is
152 *         thrown to prevent programming errors such as calling {@link #prepare()},
153 *         {@link #prepareAsync()}, or one of the overloaded <code>setDataSource
154 *         </code> methods in an invalid state. </li>
155 *         </ul>
156 *         </li>
157 *     <li>Calling
158 *         {@link #setDataSource(FileDescriptor)}, or
159 *         {@link #setDataSource(String)}, or
160 *         {@link #setDataSource(Context, Uri)}, or
161 *         {@link #setDataSource(FileDescriptor, long, long)} transfers a
162 *         MediaPlayer object in the <em>Idle</em> state to the
163 *         <em>Initialized</em> state.
164 *         <ul>
165 *         <li>An IllegalStateException is thrown if
166 *         setDataSource() is called in any other state.</li>
167 *         <li>It is good programming
168 *         practice to always look out for <code>IllegalArgumentException</code>
169 *         and <code>IOException</code> that may be thrown from the overloaded
170 *         <code>setDataSource</code> methods.</li>
171 *         </ul>
172 *         </li>
173 *     <li>A MediaPlayer object must first enter the <em>Prepared</em> state
174 *         before playback can be started.
175 *         <ul>
176 *         <li>There are two ways (synchronous vs.
177 *         asynchronous) that the <em>Prepared</em> state can be reached:
178 *         either a call to {@link #prepare()} (synchronous) which
179 *         transfers the object to the <em>Prepared</em> state once the method call
180 *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
181 *         first transfers the object to the <em>Preparing</em> state after the
182 *         call returns (which occurs almost right way) while the internal
183 *         player engine continues working on the rest of preparation work
184 *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
185 *         the internal player engine then calls a user supplied callback method,
186 *         onPrepared() of the OnPreparedListener interface, if an
187 *         OnPreparedListener is registered beforehand via {@link
188 *         #setOnPreparedListener(android.media.MediaPlayer.OnPreparedListener)}.</li>
189 *         <li>It is important to note that
190 *         the <em>Preparing</em> state is a transient state, and the behavior
191 *         of calling any method with side effect while a MediaPlayer object is
192 *         in the <em>Preparing</em> state is undefined.</li>
193 *         <li>An IllegalStateException is
194 *         thrown if {@link #prepare()} or {@link #prepareAsync()} is called in
195 *         any other state.</li>
196 *         <li>While in the <em>Prepared</em> state, properties
197 *         such as audio/sound volume, screenOnWhilePlaying, looping can be
198 *         adjusted by invoking the corresponding set methods.</li>
199 *         </ul>
200 *         </li>
201 *     <li>To start the playback, {@link #start()} must be called. After
202 *         {@link #start()} returns successfully, the MediaPlayer object is in the
203 *         <em>Started</em> state. {@link #isPlaying()} can be called to test
204 *         whether the MediaPlayer object is in the <em>Started</em> state.
205 *         <ul>
206 *         <li>While in the <em>Started</em> state, the internal player engine calls
207 *         a user supplied OnBufferingUpdateListener.onBufferingUpdate() callback
208 *         method if a OnBufferingUpdateListener has been registered beforehand
209 *         via {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}.
210 *         This callback allows applications to keep track of the buffering status
211 *         while streaming audio/video.</li>
212 *         <li>Calling {@link #start()} has not effect
213 *         on a MediaPlayer object that is already in the <em>Started</em> state.</li>
214 *         </ul>
215 *         </li>
216 *     <li>Playback can be paused and stopped, and the current playback position
217 *         can be adjusted. Playback can be paused via {@link #pause()}. When the call to
218 *         {@link #pause()} returns, the MediaPlayer object enters the
219 *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
220 *         state to the <em>Paused</em> state and vice versa happens
221 *         asynchronously in the player engine. It may take some time before
222 *         the state is updated in calls to {@link #isPlaying()}, and it can be
223 *         a number of seconds in the case of streamed content.
224 *         <ul>
225 *         <li>Calling {@link #start()} to resume playback for a paused
226 *         MediaPlayer object, and the resumed playback
227 *         position is the same as where it was paused. When the call to
228 *         {@link #start()} returns, the paused MediaPlayer object goes back to
229 *         the <em>Started</em> state.</li>
230 *         <li>Calling {@link #pause()} has no effect on
231 *         a MediaPlayer object that is already in the <em>Paused</em> state.</li>
232 *         </ul>
233 *         </li>
234 *     <li>Calling  {@link #stop()} stops playback and causes a
235 *         MediaPlayer in the <em>Started</em>, <em>Paused</em>, <em>Prepared
236 *         </em> or <em>PlaybackCompleted</em> state to enter the
237 *         <em>Stopped</em> state.
238 *         <ul>
239 *         <li>Once in the <em>Stopped</em> state, playback cannot be started
240 *         until {@link #prepare()} or {@link #prepareAsync()} are called to set
241 *         the MediaPlayer object to the <em>Prepared</em> state again.</li>
242 *         <li>Calling {@link #stop()} has no effect on a MediaPlayer
243 *         object that is already in the <em>Stopped</em> state.</li>
244 *         </ul>
245 *         </li>
246 *     <li>The playback position can be adjusted with a call to
247 *         {@link #seekTo(int)}.
248 *         <ul>
249 *         <li>Although the asynchronuous {@link #seekTo(int)}
250 *         call returns right way, the actual seek operation may take a while to
251 *         finish, especially for audio/video being streamed. When the actual
252 *         seek operation completes, the internal player engine calls a user
253 *         supplied OnSeekComplete.onSeekComplete() if an OnSeekCompleteListener
254 *         has been registered beforehand via
255 *         {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}.</li>
256 *         <li>Please
257 *         note that {@link #seekTo(int)} can also be called in the other states,
258 *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
259 *         </em> state.</li>
260 *         <li>Furthermore, the actual current playback position
261 *         can be retrieved with a call to {@link #getCurrentPosition()}, which
262 *         is helpful for applications such as a Music player that need to keep
263 *         track of the playback progress.</li>
264 *         </ul>
265 *         </li>
266 *     <li>When the playback reaches the end of stream, the playback completes.
267 *         <ul>
268 *         <li>If the looping mode was being set to <var>true</var>with
269 *         {@link #setLooping(boolean)}, the MediaPlayer object shall remain in
270 *         the <em>Started</em> state.</li>
271 *         <li>If the looping mode was set to <var>false
272 *         </var>, the player engine calls a user supplied callback method,
273 *         OnCompletion.onCompletion(), if a OnCompletionListener is registered
274 *         beforehand via {@link #setOnCompletionListener(OnCompletionListener)}.
275 *         The invoke of the callback signals that the object is now in the <em>
276 *         PlaybackCompleted</em> state.</li>
277 *         <li>While in the <em>PlaybackCompleted</em>
278 *         state, calling {@link #start()} can restart the playback from the
279 *         beginning of the audio/video source.</li>
280 * </ul>
281 *
282 *
283 * <a name="Valid_and_Invalid_States"></a>
284 * <h3>Valid and invalid states</h3>
285 *
286 * <table border="0" cellspacing="0" cellpadding="0">
287 * <tr><td>Method Name </p></td>
288 *     <td>Valid Sates </p></td>
289 *     <td>Invalid States </p></td>
290 *     <td>Comments </p></td></tr>
291 * <tr><td>attachAuxEffect </p></td>
292 *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
293 *     <td>{Idle, Error} </p></td>
294 *     <td>This method must be called after setDataSource.
295 *     Calling it does not change the object state. </p></td></tr>
296 * <tr><td>getAudioSessionId </p></td>
297 *     <td>any </p></td>
298 *     <td>{} </p></td>
299 *     <td>This method can be called in any state and calling it does not change
300 *         the object state. </p></td></tr>
301 * <tr><td>getCurrentPosition </p></td>
302 *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
303 *         PlaybackCompleted} </p></td>
304 *     <td>{Error}</p></td>
305 *     <td>Successful invoke of this method in a valid state does not change the
306 *         state. Calling this method in an invalid state transfers the object
307 *         to the <em>Error</em> state. </p></td></tr>
308 * <tr><td>getDuration </p></td>
309 *     <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
310 *     <td>{Idle, Initialized, Error} </p></td>
311 *     <td>Successful invoke of this method in a valid state does not change the
312 *         state. Calling this method in an invalid state transfers the object
313 *         to the <em>Error</em> state. </p></td></tr>
314 * <tr><td>getVideoHeight </p></td>
315 *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
316 *         PlaybackCompleted}</p></td>
317 *     <td>{Error}</p></td>
318 *     <td>Successful invoke of this method in a valid state does not change the
319 *         state. Calling this method in an invalid state transfers the object
320 *         to the <em>Error</em> state.  </p></td></tr>
321 * <tr><td>getVideoWidth </p></td>
322 *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
323 *         PlaybackCompleted}</p></td>
324 *     <td>{Error}</p></td>
325 *     <td>Successful invoke of this method in a valid state does not change
326 *         the state. Calling this method in an invalid state transfers the
327 *         object to the <em>Error</em> state. </p></td></tr>
328 * <tr><td>isPlaying </p></td>
329 *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
330 *          PlaybackCompleted}</p></td>
331 *     <td>{Error}</p></td>
332 *     <td>Successful invoke of this method in a valid state does not change
333 *         the state. Calling this method in an invalid state transfers the
334 *         object to the <em>Error</em> state. </p></td></tr>
335 * <tr><td>pause </p></td>
336 *     <td>{Started, Paused, PlaybackCompleted}</p></td>
337 *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
338 *     <td>Successful invoke of this method in a valid state transfers the
339 *         object to the <em>Paused</em> state. Calling this method in an
340 *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
341 * <tr><td>prepare </p></td>
342 *     <td>{Initialized, Stopped} </p></td>
343 *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
344 *     <td>Successful invoke of this method in a valid state transfers the
345 *         object to the <em>Prepared</em> state. Calling this method in an
346 *         invalid state throws an IllegalStateException.</p></td></tr>
347 * <tr><td>prepareAsync </p></td>
348 *     <td>{Initialized, Stopped} </p></td>
349 *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
350 *     <td>Successful invoke of this method in a valid state transfers the
351 *         object to the <em>Preparing</em> state. Calling this method in an
352 *         invalid state throws an IllegalStateException.</p></td></tr>
353 * <tr><td>release </p></td>
354 *     <td>any </p></td>
355 *     <td>{} </p></td>
356 *     <td>After {@link #release()}, the object is no longer available. </p></td></tr>
357 * <tr><td>reset </p></td>
358 *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
359 *         PlaybackCompleted, Error}</p></td>
360 *     <td>{}</p></td>
361 *     <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
362 * <tr><td>seekTo </p></td>
363 *     <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
364 *     <td>{Idle, Initialized, Stopped, Error}</p></td>
365 *     <td>Successful invoke of this method in a valid state does not change
366 *         the state. Calling this method in an invalid state transfers the
367 *         object to the <em>Error</em> state. </p></td></tr>
368 * <tr><td>setAudioSessionId </p></td>
369 *     <td>{Idle} </p></td>
370 *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
371 *          Error} </p></td>
372 *     <td>This method must be called in idle state as the audio session ID must be known before
373 *         calling setDataSource. Calling it does not change the object state. </p></td></tr>
374 * <tr><td>setAudioStreamType </p></td>
375 *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
376 *          PlaybackCompleted}</p></td>
377 *     <td>{Error}</p></td>
378 *     <td>Successful invoke of this method does not change the state. In order for the
379 *         target audio stream type to become effective, this method must be called before
380 *         prepare() or prepareAsync().</p></td></tr>
381 * <tr><td>setAuxEffectSendLevel </p></td>
382 *     <td>any</p></td>
383 *     <td>{} </p></td>
384 *     <td>Calling this method does not change the object state. </p></td></tr>
385 * <tr><td>setDataSource </p></td>
386 *     <td>{Idle} </p></td>
387 *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
388 *          Error} </p></td>
389 *     <td>Successful invoke of this method in a valid state transfers the
390 *         object to the <em>Initialized</em> state. Calling this method in an
391 *         invalid state throws an IllegalStateException.</p></td></tr>
392 * <tr><td>setDisplay </p></td>
393 *     <td>any </p></td>
394 *     <td>{} </p></td>
395 *     <td>This method can be called in any state and calling it does not change
396 *         the object state. </p></td></tr>
397 * <tr><td>setSurface </p></td>
398 *     <td>any </p></td>
399 *     <td>{} </p></td>
400 *     <td>This method can be called in any state and calling it does not change
401 *         the object state. </p></td></tr>
402 * <tr><td>setVideoScalingMode </p></td>
403 *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
404 *     <td>{Idle, Error}</p></td>
405 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
406 * <tr><td>setLooping </p></td>
407 *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
408 *         PlaybackCompleted}</p></td>
409 *     <td>{Error}</p></td>
410 *     <td>Successful invoke of this method in a valid state does not change
411 *         the state. Calling this method in an
412 *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
413 * <tr><td>isLooping </p></td>
414 *     <td>any </p></td>
415 *     <td>{} </p></td>
416 *     <td>This method can be called in any state and calling it does not change
417 *         the object state. </p></td></tr>
418 * <tr><td>setOnBufferingUpdateListener </p></td>
419 *     <td>any </p></td>
420 *     <td>{} </p></td>
421 *     <td>This method can be called in any state and calling it does not change
422 *         the object state. </p></td></tr>
423 * <tr><td>setOnCompletionListener </p></td>
424 *     <td>any </p></td>
425 *     <td>{} </p></td>
426 *     <td>This method can be called in any state and calling it does not change
427 *         the object state. </p></td></tr>
428 * <tr><td>setOnErrorListener </p></td>
429 *     <td>any </p></td>
430 *     <td>{} </p></td>
431 *     <td>This method can be called in any state and calling it does not change
432 *         the object state. </p></td></tr>
433 * <tr><td>setOnPreparedListener </p></td>
434 *     <td>any </p></td>
435 *     <td>{} </p></td>
436 *     <td>This method can be called in any state and calling it does not change
437 *         the object state. </p></td></tr>
438 * <tr><td>setOnSeekCompleteListener </p></td>
439 *     <td>any </p></td>
440 *     <td>{} </p></td>
441 *     <td>This method can be called in any state and calling it does not change
442 *         the object state. </p></td></tr>
443 * <tr><td>setScreenOnWhilePlaying</></td>
444 *     <td>any </p></td>
445 *     <td>{} </p></td>
446 *     <td>This method can be called in any state and calling it does not change
447 *         the object state.  </p></td></tr>
448 * <tr><td>setVolume </p></td>
449 *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
450 *          PlaybackCompleted}</p></td>
451 *     <td>{Error}</p></td>
452 *     <td>Successful invoke of this method does not change the state.
453 * <tr><td>setWakeMode </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>start </p></td>
459 *     <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
460 *     <td>{Idle, Initialized, Stopped, Error}</p></td>
461 *     <td>Successful invoke of this method in a valid state transfers the
462 *         object to the <em>Started</em> state. Calling this method in an
463 *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
464 * <tr><td>stop </p></td>
465 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
466 *     <td>{Idle, Initialized, Error}</p></td>
467 *     <td>Successful invoke of this method in a valid state transfers the
468 *         object to the <em>Stopped</em> state. Calling this method in an
469 *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
470 * <tr><td>getTrackInfo </p></td>
471 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
472 *     <td>{Idle, Initialized, Error}</p></td>
473 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
474 * <tr><td>addTimedTextSource </p></td>
475 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
476 *     <td>{Idle, Initialized, Error}</p></td>
477 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
478 * <tr><td>selectTrack </p></td>
479 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
480 *     <td>{Idle, Initialized, Error}</p></td>
481 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
482 * <tr><td>deselectTrack </p></td>
483 *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
484 *     <td>{Idle, Initialized, Error}</p></td>
485 *     <td>Successful invoke of this method does not change the state.</p></td></tr>
486 *
487 * </table>
488 *
489 * <a name="Permissions"></a>
490 * <h3>Permissions</h3>
491 * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
492 * android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
493 * element.
494 *
495 * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
496 * when used with network-based content.
497 *
498 * <a name="Callbacks"></a>
499 * <h3>Callbacks</h3>
500 * <p>Applications may want to register for informational and error
501 * events in order to be informed of some internal state update and
502 * possible runtime errors during playback or streaming. Registration for
503 * these events is done by properly setting the appropriate listeners (via calls
504 * to
505 * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener,
506 * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener,
507 * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener,
508 * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener,
509 * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener,
510 * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener,
511 * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc).
512 * In order to receive the respective callback
513 * associated with these listeners, applications are required to create
514 * MediaPlayer objects on a thread with its own Looper running (main UI
515 * thread by default has a Looper running).
516 *
517 */
518public class MediaPlayer
519{
520    /**
521       Constant to retrieve only the new metadata since the last
522       call.
523       // FIXME: unhide.
524       // FIXME: add link to getMetadata(boolean, boolean)
525       {@hide}
526     */
527    public static final boolean METADATA_UPDATE_ONLY = true;
528
529    /**
530       Constant to retrieve all the metadata.
531       // FIXME: unhide.
532       // FIXME: add link to getMetadata(boolean, boolean)
533       {@hide}
534     */
535    public static final boolean METADATA_ALL = false;
536
537    /**
538       Constant to enable the metadata filter during retrieval.
539       // FIXME: unhide.
540       // FIXME: add link to getMetadata(boolean, boolean)
541       {@hide}
542     */
543    public static final boolean APPLY_METADATA_FILTER = true;
544
545    /**
546       Constant to disable the metadata filter during retrieval.
547       // FIXME: unhide.
548       // FIXME: add link to getMetadata(boolean, boolean)
549       {@hide}
550     */
551    public static final boolean BYPASS_METADATA_FILTER = false;
552
553    static {
554        System.loadLibrary("media_jni");
555        native_init();
556    }
557
558    private final static String TAG = "MediaPlayer";
559    // Name of the remote interface for the media player. Must be kept
560    // in sync with the 2nd parameter of the IMPLEMENT_META_INTERFACE
561    // macro invocation in IMediaPlayer.cpp
562    private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";
563
564    private int mNativeContext; // accessed by native methods
565    private int mNativeSurfaceTexture;  // accessed by native methods
566    private int mListenerContext; // accessed by native methods
567    private SurfaceHolder mSurfaceHolder;
568    private EventHandler mEventHandler;
569    private PowerManager.WakeLock mWakeLock = null;
570    private boolean mScreenOnWhilePlaying;
571    private boolean mStayAwake;
572
573    /**
574     * Default constructor. Consider using one of the create() methods for
575     * synchronously instantiating a MediaPlayer from a Uri or resource.
576     * <p>When done with the MediaPlayer, you should call  {@link #release()},
577     * to free the resources. If not released, too many MediaPlayer instances may
578     * result in an exception.</p>
579     */
580    public MediaPlayer() {
581
582        Looper looper;
583        if ((looper = Looper.myLooper()) != null) {
584            mEventHandler = new EventHandler(this, looper);
585        } else if ((looper = Looper.getMainLooper()) != null) {
586            mEventHandler = new EventHandler(this, looper);
587        } else {
588            mEventHandler = null;
589        }
590
591        /* Native setup requires a weak reference to our object.
592         * It's easier to create it here than in C++.
593         */
594        native_setup(new WeakReference<MediaPlayer>(this));
595    }
596
597    /*
598     * Update the MediaPlayer SurfaceTexture.
599     * Call after setting a new display surface.
600     */
601    private native void _setVideoSurface(Surface surface);
602
603    /* Do not change these values (starting with INVOKE_ID) without updating
604     * their counterparts in include/media/mediaplayer.h!
605     */
606    private static final int INVOKE_ID_GET_TRACK_INFO = 1;
607    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;
608    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;
609    private static final int INVOKE_ID_SELECT_TRACK = 4;
610    private static final int INVOKE_ID_DESELECT_TRACK = 5;
611    private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
612
613    /**
614     * Create a request parcel which can be routed to the native media
615     * player using {@link #invoke(Parcel, Parcel)}. The Parcel
616     * returned has the proper InterfaceToken set. The caller should
617     * not overwrite that token, i.e it can only append data to the
618     * Parcel.
619     *
620     * @return A parcel suitable to hold a request for the native
621     * player.
622     * {@hide}
623     */
624    public Parcel newRequest() {
625        Parcel parcel = Parcel.obtain();
626        parcel.writeInterfaceToken(IMEDIA_PLAYER);
627        return parcel;
628    }
629
630    /**
631     * Invoke a generic method on the native player using opaque
632     * parcels for the request and reply. Both payloads' format is a
633     * convention between the java caller and the native player.
634     * Must be called after setDataSource to make sure a native player
635     * exists. On failure, a RuntimeException is thrown.
636     *
637     * @param request Parcel with the data for the extension. The
638     * caller must use {@link #newRequest()} to get one.
639     *
640     * @param reply Output parcel with the data returned by the
641     * native player.
642     *
643     * {@hide}
644     */
645    public void invoke(Parcel request, Parcel reply) {
646        int retcode = native_invoke(request, reply);
647        reply.setDataPosition(0);
648        if (retcode != 0) {
649            throw new RuntimeException("failure code: " + retcode);
650        }
651    }
652
653    /**
654     * Sets the {@link SurfaceHolder} to use for displaying the video
655     * portion of the media.
656     *
657     * Either a surface holder or surface must be set if a display or video sink
658     * is needed.  Not calling this method or {@link #setSurface(Surface)}
659     * when playing back a video will result in only the audio track being played.
660     * A null surface holder or surface will result in only the audio track being
661     * played.
662     *
663     * @param sh the SurfaceHolder to use for video display
664     */
665    public void setDisplay(SurfaceHolder sh) {
666        mSurfaceHolder = sh;
667        Surface surface;
668        if (sh != null) {
669            surface = sh.getSurface();
670        } else {
671            surface = null;
672        }
673        _setVideoSurface(surface);
674        updateSurfaceScreenOn();
675    }
676
677    /**
678     * Sets the {@link Surface} to be used as the sink for the video portion of
679     * the media. This is similar to {@link #setDisplay(SurfaceHolder)}, but
680     * does not support {@link #setScreenOnWhilePlaying(boolean)}.  Setting a
681     * Surface will un-set any Surface or SurfaceHolder that was previously set.
682     * A null surface will result in only the audio track being played.
683     *
684     * If the Surface sends frames to a {@link SurfaceTexture}, the timestamps
685     * returned from {@link SurfaceTexture#getTimestamp()} will have an
686     * unspecified zero point.  These timestamps cannot be directly compared
687     * between different media sources, different instances of the same media
688     * source, or multiple runs of the same program.  The timestamp is normally
689     * monotonically increasing and is unaffected by time-of-day adjustments,
690     * but it is reset when the position is set.
691     *
692     * @param surface The {@link Surface} to be used for the video portion of
693     * the media.
694     */
695    public void setSurface(Surface surface) {
696        if (mScreenOnWhilePlaying && surface != null) {
697            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for Surface");
698        }
699        mSurfaceHolder = null;
700        _setVideoSurface(surface);
701        updateSurfaceScreenOn();
702    }
703
704    /* Do not change these video scaling mode values below without updating
705     * their counterparts in system/window.h! Please do not forget to update
706     * {@link #isVideoScalingModeSupported} when new video scaling modes
707     * are added.
708     */
709    /**
710     * Specifies a video scaling mode. The content is stretched to the
711     * surface rendering area. When the surface has the same aspect ratio
712     * as the content, the aspect ratio of the content is maintained;
713     * otherwise, the aspect ratio of the content is not maintained when video
714     * is being rendered. Unlike {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING},
715     * there is no content cropping with this video scaling mode.
716     */
717    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT = 1;
718
719    /**
720     * Specifies a video scaling mode. The content is scaled, maintaining
721     * its aspect ratio. The whole surface area is always used. When the
722     * aspect ratio of the content is the same as the surface, no content
723     * is cropped; otherwise, content is cropped to fit the surface.
724     */
725    public static final int VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING = 2;
726    /**
727     * Sets video scaling mode. To make the target video scaling mode
728     * effective during playback, this method must be called after
729     * data source is set. If not called, the default video
730     * scaling mode is {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}.
731     *
732     * <p> The supported video scaling modes are:
733     * <ul>
734     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT}
735     * <li> {@link #VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING}
736     * </ul>
737     *
738     * @param mode target video scaling mode. Most be one of the supported
739     * video scaling modes; otherwise, IllegalArgumentException will be thrown.
740     *
741     * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT
742     * @see MediaPlayer#VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
743     */
744    public void setVideoScalingMode(int mode) {
745        if (!isVideoScalingModeSupported(mode)) {
746            final String msg = "Scaling mode " + mode + " is not supported";
747            throw new IllegalArgumentException(msg);
748        }
749        Parcel request = Parcel.obtain();
750        Parcel reply = Parcel.obtain();
751        try {
752            request.writeInterfaceToken(IMEDIA_PLAYER);
753            request.writeInt(INVOKE_ID_SET_VIDEO_SCALE_MODE);
754            request.writeInt(mode);
755            invoke(request, reply);
756        } finally {
757            request.recycle();
758            reply.recycle();
759        }
760    }
761
762    /**
763     * Convenience method to create a MediaPlayer for a given Uri.
764     * On success, {@link #prepare()} will already have been called and must not be called again.
765     * <p>When done with the MediaPlayer, you should call  {@link #release()},
766     * to free the resources. If not released, too many MediaPlayer instances will
767     * result in an exception.</p>
768     *
769     * @param context the Context to use
770     * @param uri the Uri from which to get the datasource
771     * @return a MediaPlayer object, or null if creation failed
772     */
773    public static MediaPlayer create(Context context, Uri uri) {
774        return create (context, uri, null);
775    }
776
777    /**
778     * Convenience method to create a MediaPlayer for a given Uri.
779     * On success, {@link #prepare()} will already have been called and must not be called again.
780     * <p>When done with the MediaPlayer, you should call  {@link #release()},
781     * to free the resources. If not released, too many MediaPlayer instances will
782     * result in an exception.</p>
783     *
784     * @param context the Context to use
785     * @param uri the Uri from which to get the datasource
786     * @param holder the SurfaceHolder to use for displaying the video
787     * @return a MediaPlayer object, or null if creation failed
788     */
789    public static MediaPlayer create(Context context, Uri uri, SurfaceHolder holder) {
790
791        try {
792            MediaPlayer mp = new MediaPlayer();
793            mp.setDataSource(context, uri);
794            if (holder != null) {
795                mp.setDisplay(holder);
796            }
797            mp.prepare();
798            return mp;
799        } catch (IOException ex) {
800            Log.d(TAG, "create failed:", ex);
801            // fall through
802        } catch (IllegalArgumentException ex) {
803            Log.d(TAG, "create failed:", ex);
804            // fall through
805        } catch (SecurityException ex) {
806            Log.d(TAG, "create failed:", ex);
807            // fall through
808        }
809
810        return null;
811    }
812
813    // Note no convenience method to create a MediaPlayer with SurfaceTexture sink.
814
815    /**
816     * Convenience method to create a MediaPlayer for a given resource id.
817     * On success, {@link #prepare()} will already have been called and must not be called again.
818     * <p>When done with the MediaPlayer, you should call  {@link #release()},
819     * to free the resources. If not released, too many MediaPlayer instances will
820     * result in an exception.</p>
821     *
822     * @param context the Context to use
823     * @param resid the raw resource id (<var>R.raw.&lt;something></var>) for
824     *              the resource to use as the datasource
825     * @return a MediaPlayer object, or null if creation failed
826     */
827    public static MediaPlayer create(Context context, int resid) {
828        try {
829            AssetFileDescriptor afd = context.getResources().openRawResourceFd(resid);
830            if (afd == null) return null;
831
832            MediaPlayer mp = new MediaPlayer();
833            mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
834            afd.close();
835            mp.prepare();
836            return mp;
837        } catch (IOException ex) {
838            Log.d(TAG, "create failed:", ex);
839            // fall through
840        } catch (IllegalArgumentException ex) {
841            Log.d(TAG, "create failed:", ex);
842           // fall through
843        } catch (SecurityException ex) {
844            Log.d(TAG, "create failed:", ex);
845            // fall through
846        }
847        return null;
848    }
849
850    /**
851     * Sets the data source as a content Uri.
852     *
853     * @param context the Context to use when resolving the Uri
854     * @param uri the Content URI of the data you want to play
855     * @throws IllegalStateException if it is called in an invalid state
856     */
857    public void setDataSource(Context context, Uri uri)
858        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
859        setDataSource(context, uri, null);
860    }
861
862    /**
863     * Sets the data source as a content Uri.
864     *
865     * @param context the Context to use when resolving the Uri
866     * @param uri the Content URI of the data you want to play
867     * @param headers the headers to be sent together with the request for the data
868     * @throws IllegalStateException if it is called in an invalid state
869     */
870    public void setDataSource(Context context, Uri uri, Map<String, String> headers)
871        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
872        disableProxyListener();
873
874        String scheme = uri.getScheme();
875        if(scheme == null || scheme.equals("file")) {
876            setDataSource(uri.getPath());
877            return;
878        }
879
880        AssetFileDescriptor fd = null;
881        try {
882            ContentResolver resolver = context.getContentResolver();
883            fd = resolver.openAssetFileDescriptor(uri, "r");
884            if (fd == null) {
885                return;
886            }
887            // Note: using getDeclaredLength so that our behavior is the same
888            // as previous versions when the content provider is returning
889            // a full file.
890            if (fd.getDeclaredLength() < 0) {
891                setDataSource(fd.getFileDescriptor());
892            } else {
893                setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getDeclaredLength());
894            }
895            return;
896        } catch (SecurityException ex) {
897        } catch (IOException ex) {
898        } finally {
899            if (fd != null) {
900                fd.close();
901            }
902        }
903
904        Log.d(TAG, "Couldn't open file on client side, trying server side");
905
906        setDataSource(uri.toString(), headers);
907
908        if (scheme.equalsIgnoreCase("http")
909                || scheme.equalsIgnoreCase("https")) {
910            setupProxyListener(context);
911        }
912    }
913
914    /**
915     * Sets the data source (file-path or http/rtsp URL) to use.
916     *
917     * @param path the path of the file, or the http/rtsp URL of the stream you want to play
918     * @throws IllegalStateException if it is called in an invalid state
919     *
920     * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
921     * process other than the calling application.  This implies that the pathname
922     * should be an absolute path (as any other process runs with unspecified current working
923     * directory), and that the pathname should reference a world-readable file.
924     * As an alternative, the application could first open the file for reading,
925     * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
926     */
927    public void setDataSource(String path)
928            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
929        setDataSource(path, null, null);
930    }
931
932    /**
933     * Sets the data source (file-path or http/rtsp URL) to use.
934     *
935     * @param path the path of the file, or the http/rtsp URL of the stream you want to play
936     * @param headers the headers associated with the http request for the stream you want to play
937     * @throws IllegalStateException if it is called in an invalid state
938     * @hide pending API council
939     */
940    public void setDataSource(String path, Map<String, String> headers)
941            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException
942    {
943        String[] keys = null;
944        String[] values = null;
945
946        if (headers != null) {
947            keys = new String[headers.size()];
948            values = new String[headers.size()];
949
950            int i = 0;
951            for (Map.Entry<String, String> entry: headers.entrySet()) {
952                keys[i] = entry.getKey();
953                values[i] = entry.getValue();
954                ++i;
955            }
956        }
957        setDataSource(path, keys, values);
958    }
959
960    private void setDataSource(String path, String[] keys, String[] values)
961            throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
962        disableProxyListener();
963
964        final Uri uri = Uri.parse(path);
965        if ("file".equals(uri.getScheme())) {
966            path = uri.getPath();
967        }
968
969        final File file = new File(path);
970        if (file.exists()) {
971            FileInputStream is = new FileInputStream(file);
972            FileDescriptor fd = is.getFD();
973            setDataSource(fd);
974            is.close();
975        } else {
976            _setDataSource(path, keys, values);
977        }
978    }
979
980    private native void _setDataSource(
981        String path, String[] keys, String[] values)
982        throws IOException, IllegalArgumentException, SecurityException, IllegalStateException;
983
984    /**
985     * Sets the data source (FileDescriptor) to use. It is the caller's responsibility
986     * to close the file descriptor. It is safe to do so as soon as this call returns.
987     *
988     * @param fd the FileDescriptor for the file you want to play
989     * @throws IllegalStateException if it is called in an invalid state
990     */
991    public void setDataSource(FileDescriptor fd)
992            throws IOException, IllegalArgumentException, IllegalStateException {
993        // intentionally less than LONG_MAX
994        setDataSource(fd, 0, 0x7ffffffffffffffL);
995    }
996
997    /**
998     * Sets the data source (FileDescriptor) to use.  The FileDescriptor must be
999     * seekable (N.B. a LocalSocket is not seekable). It is the caller's responsibility
1000     * to close the file descriptor. It is safe to do so as soon as this call returns.
1001     *
1002     * @param fd the FileDescriptor for the file you want to play
1003     * @param offset the offset into the file where the data to be played starts, in bytes
1004     * @param length the length in bytes of the data to be played
1005     * @throws IllegalStateException if it is called in an invalid state
1006     */
1007    public void setDataSource(FileDescriptor fd, long offset, long length)
1008            throws IOException, IllegalArgumentException, IllegalStateException {
1009        disableProxyListener();
1010        _setDataSource(fd, offset, length);
1011    }
1012
1013    private native void _setDataSource(FileDescriptor fd, long offset, long length)
1014            throws IOException, IllegalArgumentException, IllegalStateException;
1015
1016    /**
1017     * Prepares the player for playback, synchronously.
1018     *
1019     * After setting the datasource and the display surface, you need to either
1020     * call prepare() or prepareAsync(). For files, it is OK to call prepare(),
1021     * which blocks until MediaPlayer is ready for playback.
1022     *
1023     * @throws IllegalStateException if it is called in an invalid state
1024     */
1025    public native void prepare() throws IOException, IllegalStateException;
1026
1027    /**
1028     * Prepares the player for playback, asynchronously.
1029     *
1030     * After setting the datasource and the display surface, you need to either
1031     * call prepare() or prepareAsync(). For streams, you should call prepareAsync(),
1032     * which returns immediately, rather than blocking until enough data has been
1033     * buffered.
1034     *
1035     * @throws IllegalStateException if it is called in an invalid state
1036     */
1037    public native void prepareAsync() throws IllegalStateException;
1038
1039    /**
1040     * Starts or resumes playback. If playback had previously been paused,
1041     * playback will continue from where it was paused. If playback had
1042     * been stopped, or never started before, playback will start at the
1043     * beginning.
1044     *
1045     * @throws IllegalStateException if it is called in an invalid state
1046     */
1047    public  void start() throws IllegalStateException {
1048        stayAwake(true);
1049        _start();
1050    }
1051
1052    private native void _start() throws IllegalStateException;
1053
1054    /**
1055     * Stops playback after playback has been stopped or paused.
1056     *
1057     * @throws IllegalStateException if the internal player engine has not been
1058     * initialized.
1059     */
1060    public void stop() throws IllegalStateException {
1061        stayAwake(false);
1062        _stop();
1063    }
1064
1065    private native void _stop() throws IllegalStateException;
1066
1067    /**
1068     * Pauses playback. Call start() to resume.
1069     *
1070     * @throws IllegalStateException if the internal player engine has not been
1071     * initialized.
1072     */
1073    public void pause() throws IllegalStateException {
1074        stayAwake(false);
1075        _pause();
1076    }
1077
1078    private native void _pause() throws IllegalStateException;
1079
1080    /**
1081     * Set the low-level power management behavior for this MediaPlayer.  This
1082     * can be used when the MediaPlayer is not playing through a SurfaceHolder
1083     * set with {@link #setDisplay(SurfaceHolder)} and thus can use the
1084     * high-level {@link #setScreenOnWhilePlaying(boolean)} feature.
1085     *
1086     * <p>This function has the MediaPlayer access the low-level power manager
1087     * service to control the device's power usage while playing is occurring.
1088     * The parameter is a combination of {@link android.os.PowerManager} wake flags.
1089     * Use of this method requires {@link android.Manifest.permission#WAKE_LOCK}
1090     * permission.
1091     * By default, no attempt is made to keep the device awake during playback.
1092     *
1093     * @param context the Context to use
1094     * @param mode    the power/wake mode to set
1095     * @see android.os.PowerManager
1096     */
1097    public void setWakeMode(Context context, int mode) {
1098        boolean washeld = false;
1099        if (mWakeLock != null) {
1100            if (mWakeLock.isHeld()) {
1101                washeld = true;
1102                mWakeLock.release();
1103            }
1104            mWakeLock = null;
1105        }
1106
1107        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
1108        mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
1109        mWakeLock.setReferenceCounted(false);
1110        if (washeld) {
1111            mWakeLock.acquire();
1112        }
1113    }
1114
1115    /**
1116     * Control whether we should use the attached SurfaceHolder to keep the
1117     * screen on while video playback is occurring.  This is the preferred
1118     * method over {@link #setWakeMode} where possible, since it doesn't
1119     * require that the application have permission for low-level wake lock
1120     * access.
1121     *
1122     * @param screenOn Supply true to keep the screen on, false to allow it
1123     * to turn off.
1124     */
1125    public void setScreenOnWhilePlaying(boolean screenOn) {
1126        if (mScreenOnWhilePlaying != screenOn) {
1127            if (screenOn && mSurfaceHolder == null) {
1128                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective without a SurfaceHolder");
1129            }
1130            mScreenOnWhilePlaying = screenOn;
1131            updateSurfaceScreenOn();
1132        }
1133    }
1134
1135    private void stayAwake(boolean awake) {
1136        if (mWakeLock != null) {
1137            if (awake && !mWakeLock.isHeld()) {
1138                mWakeLock.acquire();
1139            } else if (!awake && mWakeLock.isHeld()) {
1140                mWakeLock.release();
1141            }
1142        }
1143        mStayAwake = awake;
1144        updateSurfaceScreenOn();
1145    }
1146
1147    private void updateSurfaceScreenOn() {
1148        if (mSurfaceHolder != null) {
1149            mSurfaceHolder.setKeepScreenOn(mScreenOnWhilePlaying && mStayAwake);
1150        }
1151    }
1152
1153    /**
1154     * Returns the width of the video.
1155     *
1156     * @return the width of the video, or 0 if there is no video,
1157     * no display surface was set, or the width has not been determined
1158     * yet. The OnVideoSizeChangedListener can be registered via
1159     * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
1160     * to provide a notification when the width is available.
1161     */
1162    public native int getVideoWidth();
1163
1164    /**
1165     * Returns the height of the video.
1166     *
1167     * @return the height of the video, or 0 if there is no video,
1168     * no display surface was set, or the height has not been determined
1169     * yet. The OnVideoSizeChangedListener can be registered via
1170     * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}
1171     * to provide a notification when the height is available.
1172     */
1173    public native int getVideoHeight();
1174
1175    /**
1176     * Checks whether the MediaPlayer is playing.
1177     *
1178     * @return true if currently playing, false otherwise
1179     * @throws IllegalStateException if the internal player engine has not been
1180     * initialized or has been released.
1181     */
1182    public native boolean isPlaying();
1183
1184    /**
1185     * Seeks to specified time position.
1186     *
1187     * @param msec the offset in milliseconds from the start to seek to
1188     * @throws IllegalStateException if the internal player engine has not been
1189     * initialized
1190     */
1191    public native void seekTo(int msec) throws IllegalStateException;
1192
1193    /**
1194     * Gets the current playback position.
1195     *
1196     * @return the current position in milliseconds
1197     */
1198    public native int getCurrentPosition();
1199
1200    /**
1201     * Gets the duration of the file.
1202     *
1203     * @return the duration in milliseconds, if no duration is available
1204     *         (for example, if streaming live content), -1 is returned.
1205     */
1206    public native int getDuration();
1207
1208    /**
1209     * Gets the media metadata.
1210     *
1211     * @param update_only controls whether the full set of available
1212     * metadata is returned or just the set that changed since the
1213     * last call. See {@see #METADATA_UPDATE_ONLY} and {@see
1214     * #METADATA_ALL}.
1215     *
1216     * @param apply_filter if true only metadata that matches the
1217     * filter is returned. See {@see #APPLY_METADATA_FILTER} and {@see
1218     * #BYPASS_METADATA_FILTER}.
1219     *
1220     * @return The metadata, possibly empty. null if an error occured.
1221     // FIXME: unhide.
1222     * {@hide}
1223     */
1224    public Metadata getMetadata(final boolean update_only,
1225                                final boolean apply_filter) {
1226        Parcel reply = Parcel.obtain();
1227        Metadata data = new Metadata();
1228
1229        if (!native_getMetadata(update_only, apply_filter, reply)) {
1230            reply.recycle();
1231            return null;
1232        }
1233
1234        // Metadata takes over the parcel, don't recycle it unless
1235        // there is an error.
1236        if (!data.parse(reply)) {
1237            reply.recycle();
1238            return null;
1239        }
1240        return data;
1241    }
1242
1243    /**
1244     * Set a filter for the metadata update notification and update
1245     * retrieval. The caller provides 2 set of metadata keys, allowed
1246     * and blocked. The blocked set always takes precedence over the
1247     * allowed one.
1248     * Metadata.MATCH_ALL and Metadata.MATCH_NONE are 2 sets available as
1249     * shorthands to allow/block all or no metadata.
1250     *
1251     * By default, there is no filter set.
1252     *
1253     * @param allow Is the set of metadata the client is interested
1254     *              in receiving new notifications for.
1255     * @param block Is the set of metadata the client is not interested
1256     *              in receiving new notifications for.
1257     * @return The call status code.
1258     *
1259     // FIXME: unhide.
1260     * {@hide}
1261     */
1262    public int setMetadataFilter(Set<Integer> allow, Set<Integer> block) {
1263        // Do our serialization manually instead of calling
1264        // Parcel.writeArray since the sets are made of the same type
1265        // we avoid paying the price of calling writeValue (used by
1266        // writeArray) which burns an extra int per element to encode
1267        // the type.
1268        Parcel request =  newRequest();
1269
1270        // The parcel starts already with an interface token. There
1271        // are 2 filters. Each one starts with a 4bytes number to
1272        // store the len followed by a number of int (4 bytes as well)
1273        // representing the metadata type.
1274        int capacity = request.dataSize() + 4 * (1 + allow.size() + 1 + block.size());
1275
1276        if (request.dataCapacity() < capacity) {
1277            request.setDataCapacity(capacity);
1278        }
1279
1280        request.writeInt(allow.size());
1281        for(Integer t: allow) {
1282            request.writeInt(t);
1283        }
1284        request.writeInt(block.size());
1285        for(Integer t: block) {
1286            request.writeInt(t);
1287        }
1288        return native_setMetadataFilter(request);
1289    }
1290
1291    /**
1292     * Set the MediaPlayer to start when this MediaPlayer finishes playback
1293     * (i.e. reaches the end of the stream).
1294     * The media framework will attempt to transition from this player to
1295     * the next as seamlessly as possible. The next player can be set at
1296     * any time before completion. The next player must be prepared by the
1297     * app, and the application should not call start() on it.
1298     * The next MediaPlayer must be different from 'this'. An exception
1299     * will be thrown if next == this.
1300     * The application may call setNextMediaPlayer(null) to indicate no
1301     * next player should be started at the end of playback.
1302     * If the current player is looping, it will keep looping and the next
1303     * player will not be started.
1304     *
1305     * @param next the player to start after this one completes playback.
1306     *
1307     */
1308    public native void setNextMediaPlayer(MediaPlayer next);
1309
1310    /**
1311     * Releases resources associated with this MediaPlayer object.
1312     * It is considered good practice to call this method when you're
1313     * done using the MediaPlayer. In particular, whenever an Activity
1314     * of an application is paused (its onPause() method is called),
1315     * or stopped (its onStop() method is called), this method should be
1316     * invoked to release the MediaPlayer object, unless the application
1317     * has a special need to keep the object around. In addition to
1318     * unnecessary resources (such as memory and instances of codecs)
1319     * being held, failure to call this method immediately if a
1320     * MediaPlayer object is no longer needed may also lead to
1321     * continuous battery consumption for mobile devices, and playback
1322     * failure for other applications if no multiple instances of the
1323     * same codec are supported on a device. Even if multiple instances
1324     * of the same codec are supported, some performance degradation
1325     * may be expected when unnecessary multiple instances are used
1326     * at the same time.
1327     */
1328    public void release() {
1329        stayAwake(false);
1330        updateSurfaceScreenOn();
1331        mOnPreparedListener = null;
1332        mOnBufferingUpdateListener = null;
1333        mOnCompletionListener = null;
1334        mOnSeekCompleteListener = null;
1335        mOnErrorListener = null;
1336        mOnInfoListener = null;
1337        mOnVideoSizeChangedListener = null;
1338        mOnTimedTextListener = null;
1339        _release();
1340    }
1341
1342    private native void _release();
1343
1344    /**
1345     * Resets the MediaPlayer to its uninitialized state. After calling
1346     * this method, you will have to initialize it again by setting the
1347     * data source and calling prepare().
1348     */
1349    public void reset() {
1350        stayAwake(false);
1351        _reset();
1352        // make sure none of the listeners get called anymore
1353        mEventHandler.removeCallbacksAndMessages(null);
1354
1355        disableProxyListener();
1356    }
1357
1358    private native void _reset();
1359
1360    /**
1361     * Sets the audio stream type for this MediaPlayer. See {@link AudioManager}
1362     * for a list of stream types. Must call this method before prepare() or
1363     * prepareAsync() in order for the target stream type to become effective
1364     * thereafter.
1365     *
1366     * @param streamtype the audio stream type
1367     * @see android.media.AudioManager
1368     */
1369    public native void setAudioStreamType(int streamtype);
1370
1371    /**
1372     * Sets the player to be looping or non-looping.
1373     *
1374     * @param looping whether to loop or not
1375     */
1376    public native void setLooping(boolean looping);
1377
1378    /**
1379     * Checks whether the MediaPlayer is looping or non-looping.
1380     *
1381     * @return true if the MediaPlayer is currently looping, false otherwise
1382     */
1383    public native boolean isLooping();
1384
1385    /**
1386     * Sets the volume on this player.
1387     * This API is recommended for balancing the output of audio streams
1388     * within an application. Unless you are writing an application to
1389     * control user settings, this API should be used in preference to
1390     * {@link AudioManager#setStreamVolume(int, int, int)} which sets the volume of ALL streams of
1391     * a particular type. Note that the passed volume values are raw scalars in range 0.0 to 1.0.
1392     * UI controls should be scaled logarithmically.
1393     *
1394     * @param leftVolume left volume scalar
1395     * @param rightVolume right volume scalar
1396     */
1397    /*
1398     * FIXME: Merge this into javadoc comment above when setVolume(float) is not @hide.
1399     * The single parameter form below is preferred if the channel volumes don't need
1400     * to be set independently.
1401     */
1402    public native void setVolume(float leftVolume, float rightVolume);
1403
1404    /**
1405     * Similar, excepts sets volume of all channels to same value.
1406     * @hide
1407     */
1408    public void setVolume(float volume) {
1409        setVolume(volume, volume);
1410    }
1411
1412    /**
1413     * Currently not implemented, returns null.
1414     * @deprecated
1415     * @hide
1416     */
1417    public native Bitmap getFrameAt(int msec) throws IllegalStateException;
1418
1419    /**
1420     * Sets the audio session ID.
1421     *
1422     * @param sessionId the audio session ID.
1423     * The audio session ID is a system wide unique identifier for the audio stream played by
1424     * this MediaPlayer instance.
1425     * The primary use of the audio session ID  is to associate audio effects to a particular
1426     * instance of MediaPlayer: if an audio session ID is provided when creating an audio effect,
1427     * this effect will be applied only to the audio content of media players within the same
1428     * audio session and not to the output mix.
1429     * When created, a MediaPlayer instance automatically generates its own audio session ID.
1430     * However, it is possible to force this player to be part of an already existing audio session
1431     * by calling this method.
1432     * This method must be called before one of the overloaded <code> setDataSource </code> methods.
1433     * @throws IllegalStateException if it is called in an invalid state
1434     */
1435    public native void setAudioSessionId(int sessionId)  throws IllegalArgumentException, IllegalStateException;
1436
1437    /**
1438     * Returns the audio session ID.
1439     *
1440     * @return the audio session ID. {@see #setAudioSessionId(int)}
1441     * Note that the audio session ID is 0 only if a problem occured when the MediaPlayer was contructed.
1442     */
1443    public native int getAudioSessionId();
1444
1445    /**
1446     * Attaches an auxiliary effect to the player. A typical auxiliary effect is a reverberation
1447     * effect which can be applied on any sound source that directs a certain amount of its
1448     * energy to this effect. This amount is defined by setAuxEffectSendLevel().
1449     * {@see #setAuxEffectSendLevel(float)}.
1450     * <p>After creating an auxiliary effect (e.g.
1451     * {@link android.media.audiofx.EnvironmentalReverb}), retrieve its ID with
1452     * {@link android.media.audiofx.AudioEffect#getId()} and use it when calling this method
1453     * to attach the player to the effect.
1454     * <p>To detach the effect from the player, call this method with a null effect id.
1455     * <p>This method must be called after one of the overloaded <code> setDataSource </code>
1456     * methods.
1457     * @param effectId system wide unique id of the effect to attach
1458     */
1459    public native void attachAuxEffect(int effectId);
1460
1461    /* Do not change these values (starting with KEY_PARAMETER) without updating
1462     * their counterparts in include/media/mediaplayer.h!
1463     */
1464
1465    // There are currently no defined keys usable from Java with get*Parameter.
1466    // But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
1467    // private static final int KEY_PARAMETER_... = ...;
1468
1469    /**
1470     * Sets the parameter indicated by key.
1471     * @param key key indicates the parameter to be set.
1472     * @param value value of the parameter to be set.
1473     * @return true if the parameter is set successfully, false otherwise
1474     * {@hide}
1475     */
1476    public native boolean setParameter(int key, Parcel value);
1477
1478    /**
1479     * Sets the parameter indicated by key.
1480     * @param key key indicates the parameter to be set.
1481     * @param value value of the parameter to be set.
1482     * @return true if the parameter is set successfully, false otherwise
1483     * {@hide}
1484     */
1485    public boolean setParameter(int key, String value) {
1486        Parcel p = Parcel.obtain();
1487        p.writeString(value);
1488        boolean ret = setParameter(key, p);
1489        p.recycle();
1490        return ret;
1491    }
1492
1493    /**
1494     * Sets the parameter indicated by key.
1495     * @param key key indicates the parameter to be set.
1496     * @param value value of the parameter to be set.
1497     * @return true if the parameter is set successfully, false otherwise
1498     * {@hide}
1499     */
1500    public boolean setParameter(int key, int value) {
1501        Parcel p = Parcel.obtain();
1502        p.writeInt(value);
1503        boolean ret = setParameter(key, p);
1504        p.recycle();
1505        return ret;
1506    }
1507
1508    /*
1509     * Gets the value of the parameter indicated by key.
1510     * @param key key indicates the parameter to get.
1511     * @param reply value of the parameter to get.
1512     */
1513    private native void getParameter(int key, Parcel reply);
1514
1515    /**
1516     * Gets the value of the parameter indicated by key.
1517     * The caller is responsible for recycling the returned parcel.
1518     * @param key key indicates the parameter to get.
1519     * @return value of the parameter.
1520     * {@hide}
1521     */
1522    public Parcel getParcelParameter(int key) {
1523        Parcel p = Parcel.obtain();
1524        getParameter(key, p);
1525        return p;
1526    }
1527
1528    /**
1529     * Gets the value of the parameter indicated by key.
1530     * @param key key indicates the parameter to get.
1531     * @return value of the parameter.
1532     * {@hide}
1533     */
1534    public String getStringParameter(int key) {
1535        Parcel p = Parcel.obtain();
1536        getParameter(key, p);
1537        String ret = p.readString();
1538        p.recycle();
1539        return ret;
1540    }
1541
1542    /**
1543     * Gets the value of the parameter indicated by key.
1544     * @param key key indicates the parameter to get.
1545     * @return value of the parameter.
1546     * {@hide}
1547     */
1548    public int getIntParameter(int key) {
1549        Parcel p = Parcel.obtain();
1550        getParameter(key, p);
1551        int ret = p.readInt();
1552        p.recycle();
1553        return ret;
1554    }
1555
1556    /**
1557     * Sets the send level of the player to the attached auxiliary effect
1558     * {@see #attachAuxEffect(int)}. The level value range is 0 to 1.0.
1559     * <p>By default the send level is 0, so even if an effect is attached to the player
1560     * this method must be called for the effect to be applied.
1561     * <p>Note that the passed level value is a raw scalar. UI controls should be scaled
1562     * logarithmically: the gain applied by audio framework ranges from -72dB to 0dB,
1563     * so an appropriate conversion from linear UI input x to level is:
1564     * x == 0 -> level = 0
1565     * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
1566     * @param level send level scalar
1567     */
1568    public native void setAuxEffectSendLevel(float level);
1569
1570    /*
1571     * @param request Parcel destinated to the media player. The
1572     *                Interface token must be set to the IMediaPlayer
1573     *                one to be routed correctly through the system.
1574     * @param reply[out] Parcel that will contain the reply.
1575     * @return The status code.
1576     */
1577    private native final int native_invoke(Parcel request, Parcel reply);
1578
1579
1580    /*
1581     * @param update_only If true fetch only the set of metadata that have
1582     *                    changed since the last invocation of getMetadata.
1583     *                    The set is built using the unfiltered
1584     *                    notifications the native player sent to the
1585     *                    MediaPlayerService during that period of
1586     *                    time. If false, all the metadatas are considered.
1587     * @param apply_filter  If true, once the metadata set has been built based on
1588     *                     the value update_only, the current filter is applied.
1589     * @param reply[out] On return contains the serialized
1590     *                   metadata. Valid only if the call was successful.
1591     * @return The status code.
1592     */
1593    private native final boolean native_getMetadata(boolean update_only,
1594                                                    boolean apply_filter,
1595                                                    Parcel reply);
1596
1597    /*
1598     * @param request Parcel with the 2 serialized lists of allowed
1599     *                metadata types followed by the one to be
1600     *                dropped. Each list starts with an integer
1601     *                indicating the number of metadata type elements.
1602     * @return The status code.
1603     */
1604    private native final int native_setMetadataFilter(Parcel request);
1605
1606    private static native final void native_init();
1607    private native final void native_setup(Object mediaplayer_this);
1608    private native final void native_finalize();
1609
1610    /**
1611     * Class for MediaPlayer to return each audio/video/subtitle track's metadata.
1612     *
1613     * @see android.media.MediaPlayer#getTrackInfo
1614     */
1615    static public class TrackInfo implements Parcelable {
1616        /**
1617         * Gets the track type.
1618         * @return TrackType which indicates if the track is video, audio, timed text.
1619         */
1620        public int getTrackType() {
1621            return mTrackType;
1622        }
1623
1624        /**
1625         * Gets the language code of the track.
1626         * @return a language code in either way of ISO-639-1 or ISO-639-2.
1627         * When the language is unknown or could not be determined,
1628         * ISO-639-2 language code, "und", is returned.
1629         */
1630        public String getLanguage() {
1631            return mLanguage;
1632        }
1633
1634        public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
1635        public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
1636        public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
1637        public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
1638
1639        final int mTrackType;
1640        final String mLanguage;
1641
1642        TrackInfo(Parcel in) {
1643            mTrackType = in.readInt();
1644            mLanguage = in.readString();
1645        }
1646
1647        /**
1648         * {@inheritDoc}
1649         */
1650        @Override
1651        public int describeContents() {
1652            return 0;
1653        }
1654
1655        /**
1656         * {@inheritDoc}
1657         */
1658        @Override
1659        public void writeToParcel(Parcel dest, int flags) {
1660            dest.writeInt(mTrackType);
1661            dest.writeString(mLanguage);
1662        }
1663
1664        /**
1665         * Used to read a TrackInfo from a Parcel.
1666         */
1667        static final Parcelable.Creator<TrackInfo> CREATOR
1668                = new Parcelable.Creator<TrackInfo>() {
1669                    @Override
1670                    public TrackInfo createFromParcel(Parcel in) {
1671                        return new TrackInfo(in);
1672                    }
1673
1674                    @Override
1675                    public TrackInfo[] newArray(int size) {
1676                        return new TrackInfo[size];
1677                    }
1678                };
1679
1680    };
1681
1682    /**
1683     * Returns an array of track information.
1684     *
1685     * @return Array of track info. The total number of tracks is the array length.
1686     * Must be called again if an external timed text source has been added after any of the
1687     * addTimedTextSource methods are called.
1688     * @throws IllegalStateException if it is called in an invalid state.
1689     */
1690    public TrackInfo[] getTrackInfo() throws IllegalStateException {
1691        Parcel request = Parcel.obtain();
1692        Parcel reply = Parcel.obtain();
1693        try {
1694            request.writeInterfaceToken(IMEDIA_PLAYER);
1695            request.writeInt(INVOKE_ID_GET_TRACK_INFO);
1696            invoke(request, reply);
1697            TrackInfo trackInfo[] = reply.createTypedArray(TrackInfo.CREATOR);
1698            return trackInfo;
1699        } finally {
1700            request.recycle();
1701            reply.recycle();
1702        }
1703    }
1704
1705    /* Do not change these values without updating their counterparts
1706     * in include/media/stagefright/MediaDefs.h and media/libstagefright/MediaDefs.cpp!
1707     */
1708    /**
1709     * MIME type for SubRip (SRT) container. Used in addTimedTextSource APIs.
1710     */
1711    public static final String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
1712
1713    /*
1714     * A helper function to check if the mime type is supported by media framework.
1715     */
1716    private static boolean availableMimeTypeForExternalSource(String mimeType) {
1717        if (mimeType == MEDIA_MIMETYPE_TEXT_SUBRIP) {
1718            return true;
1719        }
1720        return false;
1721    }
1722
1723    /* TODO: Limit the total number of external timed text source to a reasonable number.
1724     */
1725    /**
1726     * Adds an external timed text source file.
1727     *
1728     * Currently supported format is SubRip with the file extension .srt, case insensitive.
1729     * Note that a single external timed text source may contain multiple tracks in it.
1730     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
1731     * additional tracks become available after this method call.
1732     *
1733     * @param path The file path of external timed text source file.
1734     * @param mimeType The mime type of the file. Must be one of the mime types listed above.
1735     * @throws IOException if the file cannot be accessed or is corrupted.
1736     * @throws IllegalArgumentException if the mimeType is not supported.
1737     * @throws IllegalStateException if called in an invalid state.
1738     */
1739    public void addTimedTextSource(String path, String mimeType)
1740            throws IOException, IllegalArgumentException, IllegalStateException {
1741        if (!availableMimeTypeForExternalSource(mimeType)) {
1742            final String msg = "Illegal mimeType for timed text source: " + mimeType;
1743            throw new IllegalArgumentException(msg);
1744        }
1745
1746        File file = new File(path);
1747        if (file.exists()) {
1748            FileInputStream is = new FileInputStream(file);
1749            FileDescriptor fd = is.getFD();
1750            addTimedTextSource(fd, mimeType);
1751            is.close();
1752        } else {
1753            // We do not support the case where the path is not a file.
1754            throw new IOException(path);
1755        }
1756    }
1757
1758    /**
1759     * Adds an external timed text source file (Uri).
1760     *
1761     * Currently supported format is SubRip with the file extension .srt, case insensitive.
1762     * Note that a single external timed text source may contain multiple tracks in it.
1763     * One can find the total number of available tracks using {@link #getTrackInfo()} to see what
1764     * additional tracks become available after this method call.
1765     *
1766     * @param context the Context to use when resolving the Uri
1767     * @param uri the Content URI of the data you want to play
1768     * @param mimeType The mime type of the file. Must be one of the mime types listed above.
1769     * @throws IOException if the file cannot be accessed or is corrupted.
1770     * @throws IllegalArgumentException if the mimeType is not supported.
1771     * @throws IllegalStateException if called in an invalid state.
1772     */
1773    public void addTimedTextSource(Context context, Uri uri, String mimeType)
1774            throws IOException, IllegalArgumentException, IllegalStateException {
1775        String scheme = uri.getScheme();
1776        if(scheme == null || scheme.equals("file")) {
1777            addTimedTextSource(uri.getPath(), mimeType);
1778            return;
1779        }
1780
1781        AssetFileDescriptor fd = null;
1782        try {
1783            ContentResolver resolver = context.getContentResolver();
1784            fd = resolver.openAssetFileDescriptor(uri, "r");
1785            if (fd == null) {
1786                return;
1787            }
1788            addTimedTextSource(fd.getFileDescriptor(), mimeType);
1789            return;
1790        } catch (SecurityException ex) {
1791        } catch (IOException ex) {
1792        } finally {
1793            if (fd != null) {
1794                fd.close();
1795            }
1796        }
1797    }
1798
1799    /**
1800     * Adds an external timed text source file (FileDescriptor).
1801     *
1802     * It is the caller's responsibility to close the file descriptor.
1803     * It is safe to do so as soon as this call returns.
1804     *
1805     * Currently supported format is SubRip. Note that a single external timed text source may
1806     * contain multiple tracks in it. One can find the total number of available tracks
1807     * using {@link #getTrackInfo()} to see what additional tracks become available
1808     * after this method call.
1809     *
1810     * @param fd the FileDescriptor for the file you want to play
1811     * @param mimeType The mime type of the file. Must be one of the mime types listed above.
1812     * @throws IllegalArgumentException if the mimeType is not supported.
1813     * @throws IllegalStateException if called in an invalid state.
1814     */
1815    public void addTimedTextSource(FileDescriptor fd, String mimeType)
1816            throws IllegalArgumentException, IllegalStateException {
1817        // intentionally less than LONG_MAX
1818        addTimedTextSource(fd, 0, 0x7ffffffffffffffL, mimeType);
1819    }
1820
1821    /**
1822     * Adds an external timed text file (FileDescriptor).
1823     *
1824     * It is the caller's responsibility to close the file descriptor.
1825     * It is safe to do so as soon as this call returns.
1826     *
1827     * Currently supported format is SubRip. Note that a single external timed text source may
1828     * contain multiple tracks in it. One can find the total number of available tracks
1829     * using {@link #getTrackInfo()} to see what additional tracks become available
1830     * after this method call.
1831     *
1832     * @param fd the FileDescriptor for the file you want to play
1833     * @param offset the offset into the file where the data to be played starts, in bytes
1834     * @param length the length in bytes of the data to be played
1835     * @param mimeType The mime type of the file. Must be one of the mime types listed above.
1836     * @throws IllegalArgumentException if the mimeType is not supported.
1837     * @throws IllegalStateException if called in an invalid state.
1838     */
1839    public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mimeType)
1840            throws IllegalArgumentException, IllegalStateException {
1841        if (!availableMimeTypeForExternalSource(mimeType)) {
1842            throw new IllegalArgumentException("Illegal mimeType for timed text source: " + mimeType);
1843        }
1844
1845        Parcel request = Parcel.obtain();
1846        Parcel reply = Parcel.obtain();
1847        try {
1848            request.writeInterfaceToken(IMEDIA_PLAYER);
1849            request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD);
1850            request.writeFileDescriptor(fd);
1851            request.writeLong(offset);
1852            request.writeLong(length);
1853            request.writeString(mimeType);
1854            invoke(request, reply);
1855        } finally {
1856            request.recycle();
1857            reply.recycle();
1858        }
1859    }
1860
1861    /**
1862     * Selects a track.
1863     * <p>
1864     * If a MediaPlayer is in invalid state, it throws an IllegalStateException exception.
1865     * If a MediaPlayer is in <em>Started</em> state, the selected track is presented immediately.
1866     * If a MediaPlayer is not in Started state, it just marks the track to be played.
1867     * </p>
1868     * <p>
1869     * In any valid state, if it is called multiple times on the same type of track (ie. Video,
1870     * Audio, Timed Text), the most recent one will be chosen.
1871     * </p>
1872     * <p>
1873     * The first audio and video tracks are selected by default if available, even though
1874     * this method is not called. However, no timed text track will be selected until
1875     * this function is called.
1876     * </p>
1877     * <p>
1878     * Currently, only timed text tracks or audio tracks can be selected via this method.
1879     * In addition, the support for selecting an audio track at runtime is pretty limited
1880     * in that an audio track can only be selected in the <em>Prepared</em> state.
1881     * </p>
1882     * @param index the index of the track to be selected. The valid range of the index
1883     * is 0..total number of track - 1. The total number of tracks as well as the type of
1884     * each individual track can be found by calling {@link #getTrackInfo()} method.
1885     * @throws IllegalStateException if called in an invalid state.
1886     *
1887     * @see android.media.MediaPlayer#getTrackInfo
1888     */
1889    public void selectTrack(int index) throws IllegalStateException {
1890        selectOrDeselectTrack(index, true /* select */);
1891    }
1892
1893    /**
1894     * Deselect a track.
1895     * <p>
1896     * Currently, the track must be a timed text track and no audio or video tracks can be
1897     * deselected. If the timed text track identified by index has not been
1898     * selected before, it throws an exception.
1899     * </p>
1900     * @param index the index of the track to be deselected. The valid range of the index
1901     * is 0..total number of tracks - 1. The total number of tracks as well as the type of
1902     * each individual track can be found by calling {@link #getTrackInfo()} method.
1903     * @throws IllegalStateException if called in an invalid state.
1904     *
1905     * @see android.media.MediaPlayer#getTrackInfo
1906     */
1907    public void deselectTrack(int index) throws IllegalStateException {
1908        selectOrDeselectTrack(index, false /* select */);
1909    }
1910
1911    private void selectOrDeselectTrack(int index, boolean select)
1912            throws IllegalStateException {
1913        Parcel request = Parcel.obtain();
1914        Parcel reply = Parcel.obtain();
1915        try {
1916            request.writeInterfaceToken(IMEDIA_PLAYER);
1917            request.writeInt(select? INVOKE_ID_SELECT_TRACK: INVOKE_ID_DESELECT_TRACK);
1918            request.writeInt(index);
1919            invoke(request, reply);
1920        } finally {
1921            request.recycle();
1922            reply.recycle();
1923        }
1924    }
1925
1926
1927    /**
1928     * @param reply Parcel with audio/video duration info for battery
1929                    tracking usage
1930     * @return The status code.
1931     * {@hide}
1932     */
1933    public native static int native_pullBatteryData(Parcel reply);
1934
1935    /**
1936     * Sets the target UDP re-transmit endpoint for the low level player.
1937     * Generally, the address portion of the endpoint is an IP multicast
1938     * address, although a unicast address would be equally valid.  When a valid
1939     * retransmit endpoint has been set, the media player will not decode and
1940     * render the media presentation locally.  Instead, the player will attempt
1941     * to re-multiplex its media data using the Android@Home RTP profile and
1942     * re-transmit to the target endpoint.  Receiver devices (which may be
1943     * either the same as the transmitting device or different devices) may
1944     * instantiate, prepare, and start a receiver player using a setDataSource
1945     * URL of the form...
1946     *
1947     * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
1948     *
1949     * to receive, decode and render the re-transmitted content.
1950     *
1951     * setRetransmitEndpoint may only be called before setDataSource has been
1952     * called; while the player is in the Idle state.
1953     *
1954     * @param endpoint the address and UDP port of the re-transmission target or
1955     * null if no re-transmission is to be performed.
1956     * @throws IllegalStateException if it is called in an invalid state
1957     * @throws IllegalArgumentException if the retransmit endpoint is supplied,
1958     * but invalid.
1959     *
1960     * {@hide} pending API council
1961     */
1962    public void setRetransmitEndpoint(InetSocketAddress endpoint)
1963            throws IllegalStateException, IllegalArgumentException
1964    {
1965        String addrString = null;
1966        int port = 0;
1967
1968        if (null != endpoint) {
1969            addrString = endpoint.getAddress().getHostAddress();
1970            port = endpoint.getPort();
1971        }
1972
1973        int ret = native_setRetransmitEndpoint(addrString, port);
1974        if (ret != 0) {
1975            throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
1976        }
1977    }
1978
1979    private native final int native_setRetransmitEndpoint(String addrString, int port);
1980
1981    @Override
1982    protected void finalize() { native_finalize(); }
1983
1984    /* Do not change these values without updating their counterparts
1985     * in include/media/mediaplayer.h!
1986     */
1987    private static final int MEDIA_NOP = 0; // interface test message
1988    private static final int MEDIA_PREPARED = 1;
1989    private static final int MEDIA_PLAYBACK_COMPLETE = 2;
1990    private static final int MEDIA_BUFFERING_UPDATE = 3;
1991    private static final int MEDIA_SEEK_COMPLETE = 4;
1992    private static final int MEDIA_SET_VIDEO_SIZE = 5;
1993    private static final int MEDIA_TIMED_TEXT = 99;
1994    private static final int MEDIA_ERROR = 100;
1995    private static final int MEDIA_INFO = 200;
1996
1997    private class EventHandler extends Handler
1998    {
1999        private MediaPlayer mMediaPlayer;
2000
2001        public EventHandler(MediaPlayer mp, Looper looper) {
2002            super(looper);
2003            mMediaPlayer = mp;
2004        }
2005
2006        @Override
2007        public void handleMessage(Message msg) {
2008            if (mMediaPlayer.mNativeContext == 0) {
2009                Log.w(TAG, "mediaplayer went away with unhandled events");
2010                return;
2011            }
2012            switch(msg.what) {
2013            case MEDIA_PREPARED:
2014                if (mOnPreparedListener != null)
2015                    mOnPreparedListener.onPrepared(mMediaPlayer);
2016                return;
2017
2018            case MEDIA_PLAYBACK_COMPLETE:
2019                if (mOnCompletionListener != null)
2020                    mOnCompletionListener.onCompletion(mMediaPlayer);
2021                stayAwake(false);
2022                return;
2023
2024            case MEDIA_BUFFERING_UPDATE:
2025                if (mOnBufferingUpdateListener != null)
2026                    mOnBufferingUpdateListener.onBufferingUpdate(mMediaPlayer, msg.arg1);
2027                return;
2028
2029            case MEDIA_SEEK_COMPLETE:
2030              if (mOnSeekCompleteListener != null)
2031                  mOnSeekCompleteListener.onSeekComplete(mMediaPlayer);
2032              return;
2033
2034            case MEDIA_SET_VIDEO_SIZE:
2035              if (mOnVideoSizeChangedListener != null)
2036                  mOnVideoSizeChangedListener.onVideoSizeChanged(mMediaPlayer, msg.arg1, msg.arg2);
2037              return;
2038
2039            case MEDIA_ERROR:
2040                Log.e(TAG, "Error (" + msg.arg1 + "," + msg.arg2 + ")");
2041                boolean error_was_handled = false;
2042                if (mOnErrorListener != null) {
2043                    error_was_handled = mOnErrorListener.onError(mMediaPlayer, msg.arg1, msg.arg2);
2044                }
2045                if (mOnCompletionListener != null && ! error_was_handled) {
2046                    mOnCompletionListener.onCompletion(mMediaPlayer);
2047                }
2048                stayAwake(false);
2049                return;
2050
2051            case MEDIA_INFO:
2052                if (msg.arg1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
2053                    Log.i(TAG, "Info (" + msg.arg1 + "," + msg.arg2 + ")");
2054                }
2055                if (mOnInfoListener != null) {
2056                    mOnInfoListener.onInfo(mMediaPlayer, msg.arg1, msg.arg2);
2057                }
2058                // No real default action so far.
2059                return;
2060            case MEDIA_TIMED_TEXT:
2061                if (mOnTimedTextListener == null)
2062                    return;
2063                if (msg.obj == null) {
2064                    mOnTimedTextListener.onTimedText(mMediaPlayer, null);
2065                } else {
2066                    if (msg.obj instanceof Parcel) {
2067                        Parcel parcel = (Parcel)msg.obj;
2068                        TimedText text = new TimedText(parcel);
2069                        parcel.recycle();
2070                        mOnTimedTextListener.onTimedText(mMediaPlayer, text);
2071                    }
2072                }
2073                return;
2074
2075            case MEDIA_NOP: // interface test message - ignore
2076                break;
2077
2078            default:
2079                Log.e(TAG, "Unknown message type " + msg.what);
2080                return;
2081            }
2082        }
2083    }
2084
2085    /*
2086     * Called from native code when an interesting event happens.  This method
2087     * just uses the EventHandler system to post the event back to the main app thread.
2088     * We use a weak reference to the original MediaPlayer object so that the native
2089     * code is safe from the object disappearing from underneath it.  (This is
2090     * the cookie passed to native_setup().)
2091     */
2092    private static void postEventFromNative(Object mediaplayer_ref,
2093                                            int what, int arg1, int arg2, Object obj)
2094    {
2095        MediaPlayer mp = (MediaPlayer)((WeakReference)mediaplayer_ref).get();
2096        if (mp == null) {
2097            return;
2098        }
2099
2100        if (what == MEDIA_INFO && arg1 == MEDIA_INFO_STARTED_AS_NEXT) {
2101            // this acquires the wakelock if needed, and sets the client side state
2102            mp.start();
2103        }
2104        if (mp.mEventHandler != null) {
2105            Message m = mp.mEventHandler.obtainMessage(what, arg1, arg2, obj);
2106            mp.mEventHandler.sendMessage(m);
2107        }
2108    }
2109
2110    /**
2111     * Interface definition for a callback to be invoked when the media
2112     * source is ready for playback.
2113     */
2114    public interface OnPreparedListener
2115    {
2116        /**
2117         * Called when the media file is ready for playback.
2118         *
2119         * @param mp the MediaPlayer that is ready for playback
2120         */
2121        void onPrepared(MediaPlayer mp);
2122    }
2123
2124    /**
2125     * Register a callback to be invoked when the media source is ready
2126     * for playback.
2127     *
2128     * @param listener the callback that will be run
2129     */
2130    public void setOnPreparedListener(OnPreparedListener listener)
2131    {
2132        mOnPreparedListener = listener;
2133    }
2134
2135    private OnPreparedListener mOnPreparedListener;
2136
2137    /**
2138     * Interface definition for a callback to be invoked when playback of
2139     * a media source has completed.
2140     */
2141    public interface OnCompletionListener
2142    {
2143        /**
2144         * Called when the end of a media source is reached during playback.
2145         *
2146         * @param mp the MediaPlayer that reached the end of the file
2147         */
2148        void onCompletion(MediaPlayer mp);
2149    }
2150
2151    /**
2152     * Register a callback to be invoked when the end of a media source
2153     * has been reached during playback.
2154     *
2155     * @param listener the callback that will be run
2156     */
2157    public void setOnCompletionListener(OnCompletionListener listener)
2158    {
2159        mOnCompletionListener = listener;
2160    }
2161
2162    private OnCompletionListener mOnCompletionListener;
2163
2164    /**
2165     * Interface definition of a callback to be invoked indicating buffering
2166     * status of a media resource being streamed over the network.
2167     */
2168    public interface OnBufferingUpdateListener
2169    {
2170        /**
2171         * Called to update status in buffering a media stream received through
2172         * progressive HTTP download. The received buffering percentage
2173         * indicates how much of the content has been buffered or played.
2174         * For example a buffering update of 80 percent when half the content
2175         * has already been played indicates that the next 30 percent of the
2176         * content to play has been buffered.
2177         *
2178         * @param mp      the MediaPlayer the update pertains to
2179         * @param percent the percentage (0-100) of the content
2180         *                that has been buffered or played thus far
2181         */
2182        void onBufferingUpdate(MediaPlayer mp, int percent);
2183    }
2184
2185    /**
2186     * Register a callback to be invoked when the status of a network
2187     * stream's buffer has changed.
2188     *
2189     * @param listener the callback that will be run.
2190     */
2191    public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener)
2192    {
2193        mOnBufferingUpdateListener = listener;
2194    }
2195
2196    private OnBufferingUpdateListener mOnBufferingUpdateListener;
2197
2198    /**
2199     * Interface definition of a callback to be invoked indicating
2200     * the completion of a seek operation.
2201     */
2202    public interface OnSeekCompleteListener
2203    {
2204        /**
2205         * Called to indicate the completion of a seek operation.
2206         *
2207         * @param mp the MediaPlayer that issued the seek operation
2208         */
2209        public void onSeekComplete(MediaPlayer mp);
2210    }
2211
2212    /**
2213     * Register a callback to be invoked when a seek operation has been
2214     * completed.
2215     *
2216     * @param listener the callback that will be run
2217     */
2218    public void setOnSeekCompleteListener(OnSeekCompleteListener listener)
2219    {
2220        mOnSeekCompleteListener = listener;
2221    }
2222
2223    private OnSeekCompleteListener mOnSeekCompleteListener;
2224
2225    /**
2226     * Interface definition of a callback to be invoked when the
2227     * video size is first known or updated
2228     */
2229    public interface OnVideoSizeChangedListener
2230    {
2231        /**
2232         * Called to indicate the video size
2233         *
2234         * The video size (width and height) could be 0 if there was no video,
2235         * no display surface was set, or the value was not determined yet.
2236         *
2237         * @param mp        the MediaPlayer associated with this callback
2238         * @param width     the width of the video
2239         * @param height    the height of the video
2240         */
2241        public void onVideoSizeChanged(MediaPlayer mp, int width, int height);
2242    }
2243
2244    /**
2245     * Register a callback to be invoked when the video size is
2246     * known or updated.
2247     *
2248     * @param listener the callback that will be run
2249     */
2250    public void setOnVideoSizeChangedListener(OnVideoSizeChangedListener listener)
2251    {
2252        mOnVideoSizeChangedListener = listener;
2253    }
2254
2255    private OnVideoSizeChangedListener mOnVideoSizeChangedListener;
2256
2257    /**
2258     * Interface definition of a callback to be invoked when a
2259     * timed text is available for display.
2260     */
2261    public interface OnTimedTextListener
2262    {
2263        /**
2264         * Called to indicate an avaliable timed text
2265         *
2266         * @param mp             the MediaPlayer associated with this callback
2267         * @param text           the timed text sample which contains the text
2268         *                       needed to be displayed and the display format.
2269         */
2270        public void onTimedText(MediaPlayer mp, TimedText text);
2271    }
2272
2273    /**
2274     * Register a callback to be invoked when a timed text is available
2275     * for display.
2276     *
2277     * @param listener the callback that will be run
2278     */
2279    public void setOnTimedTextListener(OnTimedTextListener listener)
2280    {
2281        mOnTimedTextListener = listener;
2282    }
2283
2284    private OnTimedTextListener mOnTimedTextListener;
2285
2286
2287    /* Do not change these values without updating their counterparts
2288     * in include/media/mediaplayer.h!
2289     */
2290    /** Unspecified media player error.
2291     * @see android.media.MediaPlayer.OnErrorListener
2292     */
2293    public static final int MEDIA_ERROR_UNKNOWN = 1;
2294
2295    /** Media server died. In this case, the application must release the
2296     * MediaPlayer object and instantiate a new one.
2297     * @see android.media.MediaPlayer.OnErrorListener
2298     */
2299    public static final int MEDIA_ERROR_SERVER_DIED = 100;
2300
2301    /** The video is streamed and its container is not valid for progressive
2302     * playback i.e the video's index (e.g moov atom) is not at the start of the
2303     * file.
2304     * @see android.media.MediaPlayer.OnErrorListener
2305     */
2306    public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
2307
2308    /** File or network related operation errors. */
2309    public static final int MEDIA_ERROR_IO = -1004;
2310    /** Bitstream is not conforming to the related coding standard or file spec. */
2311    public static final int MEDIA_ERROR_MALFORMED = -1007;
2312    /** Bitstream is conforming to the related coding standard or file spec, but
2313     * the media framework does not support the feature. */
2314    public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
2315    /** Some operation takes too long to complete, usually more than 3-5 seconds. */
2316    public static final int MEDIA_ERROR_TIMED_OUT = -110;
2317
2318    /**
2319     * Interface definition of a callback to be invoked when there
2320     * has been an error during an asynchronous operation (other errors
2321     * will throw exceptions at method call time).
2322     */
2323    public interface OnErrorListener
2324    {
2325        /**
2326         * Called to indicate an error.
2327         *
2328         * @param mp      the MediaPlayer the error pertains to
2329         * @param what    the type of error that has occurred:
2330         * <ul>
2331         * <li>{@link #MEDIA_ERROR_UNKNOWN}
2332         * <li>{@link #MEDIA_ERROR_SERVER_DIED}
2333         * </ul>
2334         * @param extra an extra code, specific to the error. Typically
2335         * implementation dependent.
2336         * <ul>
2337         * <li>{@link #MEDIA_ERROR_IO}
2338         * <li>{@link #MEDIA_ERROR_MALFORMED}
2339         * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
2340         * <li>{@link #MEDIA_ERROR_TIMED_OUT}
2341         * </ul>
2342         * @return True if the method handled the error, false if it didn't.
2343         * Returning false, or not having an OnErrorListener at all, will
2344         * cause the OnCompletionListener to be called.
2345         */
2346        boolean onError(MediaPlayer mp, int what, int extra);
2347    }
2348
2349    /**
2350     * Register a callback to be invoked when an error has happened
2351     * during an asynchronous operation.
2352     *
2353     * @param listener the callback that will be run
2354     */
2355    public void setOnErrorListener(OnErrorListener listener)
2356    {
2357        mOnErrorListener = listener;
2358    }
2359
2360    private OnErrorListener mOnErrorListener;
2361
2362
2363    /* Do not change these values without updating their counterparts
2364     * in include/media/mediaplayer.h!
2365     */
2366    /** Unspecified media player info.
2367     * @see android.media.MediaPlayer.OnInfoListener
2368     */
2369    public static final int MEDIA_INFO_UNKNOWN = 1;
2370
2371    /** The player was started because it was used as the next player for another
2372     * player, which just completed playback.
2373     * @see android.media.MediaPlayer.OnInfoListener
2374     * @hide
2375     */
2376    public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
2377
2378    /** The player just pushed the very first video frame for rendering.
2379     * @see android.media.MediaPlayer.OnInfoListener
2380     */
2381    public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
2382
2383    /** The video is too complex for the decoder: it can't decode frames fast
2384     *  enough. Possibly only the audio plays fine at this stage.
2385     * @see android.media.MediaPlayer.OnInfoListener
2386     */
2387    public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700;
2388
2389    /** MediaPlayer is temporarily pausing playback internally in order to
2390     * buffer more data.
2391     * @see android.media.MediaPlayer.OnInfoListener
2392     */
2393    public static final int MEDIA_INFO_BUFFERING_START = 701;
2394
2395    /** MediaPlayer is resuming playback after filling buffers.
2396     * @see android.media.MediaPlayer.OnInfoListener
2397     */
2398    public static final int MEDIA_INFO_BUFFERING_END = 702;
2399
2400    /** Bad interleaving means that a media has been improperly interleaved or
2401     * not interleaved at all, e.g has all the video samples first then all the
2402     * audio ones. Video is playing but a lot of disk seeks may be happening.
2403     * @see android.media.MediaPlayer.OnInfoListener
2404     */
2405    public static final int MEDIA_INFO_BAD_INTERLEAVING = 800;
2406
2407    /** The media cannot be seeked (e.g live stream)
2408     * @see android.media.MediaPlayer.OnInfoListener
2409     */
2410    public static final int MEDIA_INFO_NOT_SEEKABLE = 801;
2411
2412    /** A new set of metadata is available.
2413     * @see android.media.MediaPlayer.OnInfoListener
2414     */
2415    public static final int MEDIA_INFO_METADATA_UPDATE = 802;
2416
2417    /** Failed to handle timed text track properly.
2418     * @see android.media.MediaPlayer.OnInfoListener
2419     *
2420     * {@hide}
2421     */
2422    public static final int MEDIA_INFO_TIMED_TEXT_ERROR = 900;
2423
2424    /**
2425     * Interface definition of a callback to be invoked to communicate some
2426     * info and/or warning about the media or its playback.
2427     */
2428    public interface OnInfoListener
2429    {
2430        /**
2431         * Called to indicate an info or a warning.
2432         *
2433         * @param mp      the MediaPlayer the info pertains to.
2434         * @param what    the type of info or warning.
2435         * <ul>
2436         * <li>{@link #MEDIA_INFO_UNKNOWN}
2437         * <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
2438         * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
2439         * <li>{@link #MEDIA_INFO_BUFFERING_START}
2440         * <li>{@link #MEDIA_INFO_BUFFERING_END}
2441         * <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
2442         * <li>{@link #MEDIA_INFO_NOT_SEEKABLE}
2443         * <li>{@link #MEDIA_INFO_METADATA_UPDATE}
2444         * </ul>
2445         * @param extra an extra code, specific to the info. Typically
2446         * implementation dependent.
2447         * @return True if the method handled the info, false if it didn't.
2448         * Returning false, or not having an OnErrorListener at all, will
2449         * cause the info to be discarded.
2450         */
2451        boolean onInfo(MediaPlayer mp, int what, int extra);
2452    }
2453
2454    /**
2455     * Register a callback to be invoked when an info/warning is available.
2456     *
2457     * @param listener the callback that will be run
2458     */
2459    public void setOnInfoListener(OnInfoListener listener)
2460    {
2461        mOnInfoListener = listener;
2462    }
2463
2464    private OnInfoListener mOnInfoListener;
2465
2466    /*
2467     * Test whether a given video scaling mode is supported.
2468     */
2469    private boolean isVideoScalingModeSupported(int mode) {
2470        return (mode == VIDEO_SCALING_MODE_SCALE_TO_FIT ||
2471                mode == VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
2472    }
2473
2474    private Context mProxyContext = null;
2475    private ProxyReceiver mProxyReceiver = null;
2476
2477    private void setupProxyListener(Context context) {
2478        IntentFilter filter = new IntentFilter();
2479        filter.addAction(Proxy.PROXY_CHANGE_ACTION);
2480        mProxyReceiver = new ProxyReceiver();
2481        mProxyContext = context;
2482
2483        Intent currentProxy =
2484            context.getApplicationContext().registerReceiver(mProxyReceiver, filter);
2485
2486        if (currentProxy != null) {
2487            handleProxyBroadcast(currentProxy);
2488        }
2489    }
2490
2491    private void disableProxyListener() {
2492        if (mProxyReceiver == null) {
2493            return;
2494        }
2495
2496        Context appContext = mProxyContext.getApplicationContext();
2497        if (appContext != null) {
2498            appContext.unregisterReceiver(mProxyReceiver);
2499        }
2500
2501        mProxyReceiver = null;
2502        mProxyContext = null;
2503    }
2504
2505    private void handleProxyBroadcast(Intent intent) {
2506        ProxyProperties props =
2507            (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
2508
2509        if (props == null || props.getHost() == null) {
2510            updateProxyConfig(null);
2511        } else {
2512            updateProxyConfig(props);
2513        }
2514    }
2515
2516    private class ProxyReceiver extends BroadcastReceiver {
2517        @Override
2518        public void onReceive(Context context, Intent intent) {
2519            if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
2520                handleProxyBroadcast(intent);
2521            }
2522        }
2523    }
2524
2525    private native void updateProxyConfig(ProxyProperties props);
2526}
2527