android_media_MediaPlayer.cpp revision e00b6f3a57d4b480ce674468de93555269bbd71a
1/*
2**
3** Copyright 2007, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18//#define LOG_NDEBUG 0
19#define LOG_TAG "MediaPlayer-JNI"
20#include "utils/Log.h"
21
22#include <media/mediaplayer.h>
23#include <media/MediaPlayerInterface.h>
24#include <stdio.h>
25#include <assert.h>
26#include <limits.h>
27#include <unistd.h>
28#include <fcntl.h>
29#include <utils/threads.h>
30#include "jni.h"
31#include "JNIHelp.h"
32#include "android_runtime/AndroidRuntime.h"
33#include "android_runtime/android_view_Surface.h"
34#include "utils/Errors.h"  // for status_t
35#include "utils/KeyedVector.h"
36#include "utils/String8.h"
37#include "android_media_Utils.h"
38
39#include "android_os_Parcel.h"
40#include "android_util_Binder.h"
41#include <binder/Parcel.h>
42#include <gui/ISurfaceTexture.h>
43#include <gui/Surface.h>
44#include <binder/IPCThreadState.h>
45#include <binder/IServiceManager.h>
46
47// ----------------------------------------------------------------------------
48
49using namespace android;
50
51// ----------------------------------------------------------------------------
52
53struct fields_t {
54    jfieldID    context;
55    jfieldID    surface_texture;
56
57    jmethodID   post_event;
58};
59static fields_t fields;
60
61static Mutex sLock;
62
63// ----------------------------------------------------------------------------
64// ref-counted object for callbacks
65class JNIMediaPlayerListener: public MediaPlayerListener
66{
67public:
68    JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
69    ~JNIMediaPlayerListener();
70    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
71private:
72    JNIMediaPlayerListener();
73    jclass      mClass;     // Reference to MediaPlayer class
74    jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
75};
76
77JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
78{
79
80    // Hold onto the MediaPlayer class for use in calling the static method
81    // that posts events to the application thread.
82    jclass clazz = env->GetObjectClass(thiz);
83    if (clazz == NULL) {
84        ALOGE("Can't find android/media/MediaPlayer");
85        jniThrowException(env, "java/lang/Exception", NULL);
86        return;
87    }
88    mClass = (jclass)env->NewGlobalRef(clazz);
89
90    // We use a weak reference so the MediaPlayer object can be garbage collected.
91    // The reference is only used as a proxy for callbacks.
92    mObject  = env->NewGlobalRef(weak_thiz);
93}
94
95JNIMediaPlayerListener::~JNIMediaPlayerListener()
96{
97    // remove global references
98    JNIEnv *env = AndroidRuntime::getJNIEnv();
99    env->DeleteGlobalRef(mObject);
100    env->DeleteGlobalRef(mClass);
101}
102
103void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
104{
105    JNIEnv *env = AndroidRuntime::getJNIEnv();
106    if (obj && obj->dataSize() > 0) {
107        jbyteArray jArray = env->NewByteArray(obj->dataSize());
108        if (jArray != NULL) {
109            jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
110            memcpy(nArray, obj->data(), obj->dataSize());
111            env->ReleaseByteArrayElements(jArray, nArray, 0);
112            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
113                    msg, ext1, ext2, jArray);
114            env->DeleteLocalRef(jArray);
115        }
116    } else {
117        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
118                msg, ext1, ext2, NULL);
119    }
120}
121
122// ----------------------------------------------------------------------------
123
124static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
125{
126    Mutex::Autolock l(sLock);
127    MediaPlayer* const p = (MediaPlayer*)env->GetIntField(thiz, fields.context);
128    return sp<MediaPlayer>(p);
129}
130
131static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
132{
133    Mutex::Autolock l(sLock);
134    sp<MediaPlayer> old = (MediaPlayer*)env->GetIntField(thiz, fields.context);
135    if (player.get()) {
136        player->incStrong(thiz);
137    }
138    if (old != 0) {
139        old->decStrong(thiz);
140    }
141    env->SetIntField(thiz, fields.context, (int)player.get());
142    return old;
143}
144
145// If exception is NULL and opStatus is not OK, this method sends an error
146// event to the client application; otherwise, if exception is not NULL and
147// opStatus is not OK, this method throws the given exception to the client
148// application.
149static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
150{
151    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
152        if (opStatus != (status_t) OK) {
153            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
154            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
155        }
156    } else {  // Throw exception!
157        if ( opStatus == (status_t) INVALID_OPERATION ) {
158            jniThrowException(env, "java/lang/IllegalStateException", NULL);
159        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
160            jniThrowException(env, "java/lang/SecurityException", NULL);
161        } else if ( opStatus != (status_t) OK ) {
162            if (strlen(message) > 230) {
163               // if the message is too long, don't bother displaying the status code
164               jniThrowException( env, exception, message);
165            } else {
166               char msg[256];
167                // append the status code to the message
168               sprintf(msg, "%s: status=0x%X", message, opStatus);
169               jniThrowException( env, exception, msg);
170            }
171        }
172    }
173}
174
175static void
176android_media_MediaPlayer_setDataSourceAndHeaders(
177        JNIEnv *env, jobject thiz, jstring path,
178        jobjectArray keys, jobjectArray values) {
179
180    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
181    if (mp == NULL ) {
182        jniThrowException(env, "java/lang/IllegalStateException", NULL);
183        return;
184    }
185
186    if (path == NULL) {
187        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
188        return;
189    }
190
191    const char *tmp = env->GetStringUTFChars(path, NULL);
192    if (tmp == NULL) {  // Out of memory
193        return;
194    }
195    ALOGV("setDataSource: path %s", tmp);
196
197    String8 pathStr(tmp);
198    env->ReleaseStringUTFChars(path, tmp);
199    tmp = NULL;
200
201    // We build a KeyedVector out of the key and val arrays
202    KeyedVector<String8, String8> headersVector;
203    if (!ConvertKeyValueArraysToKeyedVector(
204            env, keys, values, &headersVector)) {
205        return;
206    }
207
208    status_t opStatus =
209        mp->setDataSource(
210                pathStr,
211                headersVector.size() > 0? &headersVector : NULL);
212
213    process_media_player_call(
214            env, thiz, opStatus, "java/io/IOException",
215            "setDataSource failed." );
216}
217
218static void
219android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
220{
221    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
222    if (mp == NULL ) {
223        jniThrowException(env, "java/lang/IllegalStateException", NULL);
224        return;
225    }
226
227    if (fileDescriptor == NULL) {
228        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
229        return;
230    }
231    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
232    ALOGV("setDataSourceFD: fd %d", fd);
233    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
234}
235
236static sp<ISurfaceTexture>
237getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
238    ISurfaceTexture * const p = (ISurfaceTexture*)env->GetIntField(thiz, fields.surface_texture);
239    return sp<ISurfaceTexture>(p);
240}
241
242static void
243decVideoSurfaceRef(JNIEnv *env, jobject thiz)
244{
245    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
246    if (mp == NULL) {
247        return;
248    }
249
250    sp<ISurfaceTexture> old_st = getVideoSurfaceTexture(env, thiz);
251    if (old_st != NULL) {
252        old_st->decStrong(thiz);
253    }
254}
255
256static void
257setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
258{
259    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
260    if (mp == NULL) {
261        if (mediaPlayerMustBeAlive) {
262            jniThrowException(env, "java/lang/IllegalStateException", NULL);
263        }
264        return;
265    }
266
267    decVideoSurfaceRef(env, thiz);
268
269    sp<ISurfaceTexture> new_st;
270    if (jsurface) {
271        sp<Surface> surface(Surface_getSurface(env, jsurface));
272        if (surface != NULL) {
273            new_st = surface->getSurfaceTexture();
274            new_st->incStrong(thiz);
275        } else {
276            jniThrowException(env, "java/lang/IllegalArgumentException",
277                    "The surface has been released");
278            return;
279        }
280    }
281
282    env->SetIntField(thiz, fields.surface_texture, (int)new_st.get());
283
284    // This will fail if the media player has not been initialized yet. This
285    // can be the case if setDisplay() on MediaPlayer.java has been called
286    // before setDataSource(). The redundant call to setVideoSurfaceTexture()
287    // in prepare/prepareAsync covers for this case.
288    mp->setVideoSurfaceTexture(new_st);
289}
290
291static void
292android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
293{
294    setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
295}
296
297static void
298android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
299{
300    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
301    if (mp == NULL ) {
302        jniThrowException(env, "java/lang/IllegalStateException", NULL);
303        return;
304    }
305
306    // Handle the case where the display surface was set before the mp was
307    // initialized. We try again to make it stick.
308    sp<ISurfaceTexture> st = getVideoSurfaceTexture(env, thiz);
309    mp->setVideoSurfaceTexture(st);
310
311    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
312}
313
314static void
315android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
316{
317    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
318    if (mp == NULL ) {
319        jniThrowException(env, "java/lang/IllegalStateException", NULL);
320        return;
321    }
322
323    // Handle the case where the display surface was set before the mp was
324    // initialized. We try again to make it stick.
325    sp<ISurfaceTexture> st = getVideoSurfaceTexture(env, thiz);
326    mp->setVideoSurfaceTexture(st);
327
328    process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
329}
330
331static void
332android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
333{
334    ALOGV("start");
335    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
336    if (mp == NULL ) {
337        jniThrowException(env, "java/lang/IllegalStateException", NULL);
338        return;
339    }
340    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
341}
342
343static void
344android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
345{
346    ALOGV("stop");
347    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
348    if (mp == NULL ) {
349        jniThrowException(env, "java/lang/IllegalStateException", NULL);
350        return;
351    }
352    process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
353}
354
355static void
356android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz)
357{
358    ALOGV("pause");
359    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
360    if (mp == NULL ) {
361        jniThrowException(env, "java/lang/IllegalStateException", NULL);
362        return;
363    }
364    process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
365}
366
367static jboolean
368android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
369{
370    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
371    if (mp == NULL ) {
372        jniThrowException(env, "java/lang/IllegalStateException", NULL);
373        return false;
374    }
375    const jboolean is_playing = mp->isPlaying();
376
377    ALOGV("isPlaying: %d", is_playing);
378    return is_playing;
379}
380
381static void
382android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, int msec)
383{
384    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
385    if (mp == NULL ) {
386        jniThrowException(env, "java/lang/IllegalStateException", NULL);
387        return;
388    }
389    ALOGV("seekTo: %d(msec)", msec);
390    process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL );
391}
392
393static int
394android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
395{
396    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
397    if (mp == NULL ) {
398        jniThrowException(env, "java/lang/IllegalStateException", NULL);
399        return 0;
400    }
401    int w;
402    if (0 != mp->getVideoWidth(&w)) {
403        ALOGE("getVideoWidth failed");
404        w = 0;
405    }
406    ALOGV("getVideoWidth: %d", w);
407    return w;
408}
409
410static int
411android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
412{
413    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
414    if (mp == NULL ) {
415        jniThrowException(env, "java/lang/IllegalStateException", NULL);
416        return 0;
417    }
418    int h;
419    if (0 != mp->getVideoHeight(&h)) {
420        ALOGE("getVideoHeight failed");
421        h = 0;
422    }
423    ALOGV("getVideoHeight: %d", h);
424    return h;
425}
426
427
428static int
429android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
430{
431    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
432    if (mp == NULL ) {
433        jniThrowException(env, "java/lang/IllegalStateException", NULL);
434        return 0;
435    }
436    int msec;
437    process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
438    ALOGV("getCurrentPosition: %d (msec)", msec);
439    return msec;
440}
441
442static int
443android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
444{
445    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
446    if (mp == NULL ) {
447        jniThrowException(env, "java/lang/IllegalStateException", NULL);
448        return 0;
449    }
450    int msec;
451    process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
452    ALOGV("getDuration: %d (msec)", msec);
453    return msec;
454}
455
456static void
457android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
458{
459    ALOGV("reset");
460    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
461    if (mp == NULL ) {
462        jniThrowException(env, "java/lang/IllegalStateException", NULL);
463        return;
464    }
465    process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
466}
467
468static void
469android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, int streamtype)
470{
471    ALOGV("setAudioStreamType: %d", streamtype);
472    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
473    if (mp == NULL ) {
474        jniThrowException(env, "java/lang/IllegalStateException", NULL);
475        return;
476    }
477    process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
478}
479
480static void
481android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
482{
483    ALOGV("setLooping: %d", looping);
484    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
485    if (mp == NULL ) {
486        jniThrowException(env, "java/lang/IllegalStateException", NULL);
487        return;
488    }
489    process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
490}
491
492static jboolean
493android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz)
494{
495    ALOGV("isLooping");
496    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
497    if (mp == NULL ) {
498        jniThrowException(env, "java/lang/IllegalStateException", NULL);
499        return false;
500    }
501    return mp->isLooping();
502}
503
504static void
505android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, float leftVolume, float rightVolume)
506{
507    ALOGV("setVolume: left %f  right %f", leftVolume, rightVolume);
508    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
509    if (mp == NULL ) {
510        jniThrowException(env, "java/lang/IllegalStateException", NULL);
511        return;
512    }
513    process_media_player_call( env, thiz, mp->setVolume(leftVolume, rightVolume), NULL, NULL );
514}
515
516// FIXME: deprecated
517static jobject
518android_media_MediaPlayer_getFrameAt(JNIEnv *env, jobject thiz, jint msec)
519{
520    return NULL;
521}
522
523
524// Sends the request and reply parcels to the media player via the
525// binder interface.
526static jint
527android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
528                                 jobject java_request, jobject java_reply)
529{
530    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
531    if (media_player == NULL ) {
532        jniThrowException(env, "java/lang/IllegalStateException", NULL);
533        return UNKNOWN_ERROR;
534    }
535
536
537    Parcel *request = parcelForJavaObject(env, java_request);
538    Parcel *reply = parcelForJavaObject(env, java_reply);
539
540    // Don't use process_media_player_call which use the async loop to
541    // report errors, instead returns the status.
542    return media_player->invoke(*request, reply);
543}
544
545// Sends the new filter to the client.
546static jint
547android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
548{
549    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
550    if (media_player == NULL ) {
551        jniThrowException(env, "java/lang/IllegalStateException", NULL);
552        return UNKNOWN_ERROR;
553    }
554
555    Parcel *filter = parcelForJavaObject(env, request);
556
557    if (filter == NULL ) {
558        jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
559        return UNKNOWN_ERROR;
560    }
561
562    return media_player->setMetadataFilter(*filter);
563}
564
565static jboolean
566android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
567                                      jboolean apply_filter, jobject reply)
568{
569    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
570    if (media_player == NULL ) {
571        jniThrowException(env, "java/lang/IllegalStateException", NULL);
572        return false;
573    }
574
575    Parcel *metadata = parcelForJavaObject(env, reply);
576
577    if (metadata == NULL ) {
578        jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
579        return false;
580    }
581
582    metadata->freeData();
583    // On return metadata is positioned at the beginning of the
584    // metadata. Note however that the parcel actually starts with the
585    // return code so you should not rewind the parcel using
586    // setDataPosition(0).
587    return media_player->getMetadata(update_only, apply_filter, metadata) == OK;
588}
589
590// This function gets some field IDs, which in turn causes class initialization.
591// It is called from a static block in MediaPlayer, which won't run until the
592// first time an instance of this class is used.
593static void
594android_media_MediaPlayer_native_init(JNIEnv *env)
595{
596    jclass clazz;
597
598    clazz = env->FindClass("android/media/MediaPlayer");
599    if (clazz == NULL) {
600        return;
601    }
602
603    fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
604    if (fields.context == NULL) {
605        return;
606    }
607
608    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
609                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
610    if (fields.post_event == NULL) {
611        return;
612    }
613
614    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "I");
615    if (fields.surface_texture == NULL) {
616        return;
617    }
618}
619
620static void
621android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
622{
623    ALOGV("native_setup");
624    sp<MediaPlayer> mp = new MediaPlayer();
625    if (mp == NULL) {
626        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
627        return;
628    }
629
630    // create new listener and give it to MediaPlayer
631    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
632    mp->setListener(listener);
633
634    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
635    setMediaPlayer(env, thiz, mp);
636}
637
638static void
639android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
640{
641    ALOGV("release");
642    decVideoSurfaceRef(env, thiz);
643    sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
644    if (mp != NULL) {
645        // this prevents native callbacks after the object is released
646        mp->setListener(0);
647        mp->disconnect();
648    }
649}
650
651static void
652android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
653{
654    ALOGV("native_finalize");
655    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
656    if (mp != NULL) {
657        ALOGW("MediaPlayer finalized without being released");
658    }
659    android_media_MediaPlayer_release(env, thiz);
660}
661
662static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env,  jobject thiz, jint sessionId) {
663    ALOGV("set_session_id(): %d", sessionId);
664    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
665    if (mp == NULL ) {
666        jniThrowException(env, "java/lang/IllegalStateException", NULL);
667        return;
668    }
669    process_media_player_call( env, thiz, mp->setAudioSessionId(sessionId), NULL, NULL );
670}
671
672static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env,  jobject thiz) {
673    ALOGV("get_session_id()");
674    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
675    if (mp == NULL ) {
676        jniThrowException(env, "java/lang/IllegalStateException", NULL);
677        return 0;
678    }
679
680    return mp->getAudioSessionId();
681}
682
683static void
684android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
685{
686    ALOGV("setAuxEffectSendLevel: level %f", level);
687    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
688    if (mp == NULL ) {
689        jniThrowException(env, "java/lang/IllegalStateException", NULL);
690        return;
691    }
692    process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
693}
694
695static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env,  jobject thiz, jint effectId) {
696    ALOGV("attachAuxEffect(): %d", effectId);
697    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
698    if (mp == NULL ) {
699        jniThrowException(env, "java/lang/IllegalStateException", NULL);
700        return;
701    }
702    process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
703}
704
705static jint
706android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject java_reply)
707{
708    sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player"));
709    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
710    if (service.get() == NULL) {
711        jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService");
712        return UNKNOWN_ERROR;
713    }
714
715    Parcel *reply = parcelForJavaObject(env, java_reply);
716
717    return service->pullBatteryData(reply);
718}
719
720static jint
721android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
722                                                jstring addrString, jint port) {
723    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
724    if (mp == NULL ) {
725        jniThrowException(env, "java/lang/IllegalStateException", NULL);
726        return INVALID_OPERATION;
727    }
728
729    const char *cAddrString = NULL;
730
731    if (NULL != addrString) {
732        cAddrString = env->GetStringUTFChars(addrString, NULL);
733        if (cAddrString == NULL) {  // Out of memory
734            return NO_MEMORY;
735        }
736    }
737    ALOGV("setRetransmitEndpoint: %s:%d",
738            cAddrString ? cAddrString : "(null)", port);
739
740    status_t ret;
741    if (cAddrString && (port > 0xFFFF)) {
742        ret = BAD_VALUE;
743    } else {
744        ret = mp->setRetransmitEndpoint(cAddrString,
745                static_cast<uint16_t>(port));
746    }
747
748    if (NULL != addrString) {
749        env->ReleaseStringUTFChars(addrString, cAddrString);
750    }
751
752    if (ret == INVALID_OPERATION ) {
753        jniThrowException(env, "java/lang/IllegalStateException", NULL);
754    }
755
756    return ret;
757}
758
759static jboolean
760android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
761{
762    ALOGV("setParameter: key %d", key);
763    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
764    if (mp == NULL ) {
765        jniThrowException(env, "java/lang/IllegalStateException", NULL);
766        return false;
767    }
768
769    Parcel *request = parcelForJavaObject(env, java_request);
770    status_t err = mp->setParameter(key, *request);
771    if (err == OK) {
772        return true;
773    } else {
774        return false;
775    }
776}
777
778static void
779android_media_MediaPlayer_getParameter(JNIEnv *env, jobject thiz, jint key, jobject java_reply)
780{
781    ALOGV("getParameter: key %d", key);
782    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
783    if (mp == NULL ) {
784        jniThrowException(env, "java/lang/IllegalStateException", NULL);
785        return;
786    }
787
788    Parcel *reply = parcelForJavaObject(env, java_reply);
789    process_media_player_call(env, thiz, mp->getParameter(key, reply), NULL, NULL );
790}
791
792static void
793android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
794{
795    ALOGV("setNextMediaPlayer");
796    sp<MediaPlayer> thisplayer = getMediaPlayer(env, thiz);
797    if (thisplayer == NULL) {
798        jniThrowException(env, "java/lang/IllegalStateException", "This player not initialized");
799        return;
800    }
801    sp<MediaPlayer> nextplayer = (java_player == NULL) ? NULL : getMediaPlayer(env, java_player);
802    if (nextplayer == NULL && java_player != NULL) {
803        jniThrowException(env, "java/lang/IllegalStateException", "That player not initialized");
804        return;
805    }
806
807    if (nextplayer == thisplayer) {
808        jniThrowException(env, "java/lang/IllegalArgumentException", "Next player can't be self");
809        return;
810    }
811    // tie the two players together
812    process_media_player_call(
813            env, thiz, thisplayer->setNextMediaPlayer(nextplayer),
814            "java/lang/IllegalArgumentException",
815            "setNextMediaPlayer failed." );
816    ;
817}
818
819// ----------------------------------------------------------------------------
820
821static JNINativeMethod gMethods[] = {
822    {
823        "_setDataSource",
824        "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
825        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
826    },
827
828    {"setDataSource",       "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
829    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
830    {"prepare",             "()V",                              (void *)android_media_MediaPlayer_prepare},
831    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
832    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
833    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
834    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
835    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
836    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
837    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
838    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
839    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
840    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
841    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
842    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
843    {"setAudioStreamType",  "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
844    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
845    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
846    {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
847    {"getFrameAt",          "(I)Landroid/graphics/Bitmap;",     (void *)android_media_MediaPlayer_getFrameAt},
848    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
849    {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
850    {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
851    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
852    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
853    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
854    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
855    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
856    {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
857    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
858    {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
859    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
860    {"getParameter",        "(ILandroid/os/Parcel;)V",          (void *)android_media_MediaPlayer_getParameter},
861    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
862    {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
863};
864
865static const char* const kClassPathName = "android/media/MediaPlayer";
866
867// This function only registers the native methods
868static int register_android_media_MediaPlayer(JNIEnv *env)
869{
870    return AndroidRuntime::registerNativeMethods(env,
871                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
872}
873
874extern int register_android_media_Crypto(JNIEnv *env);
875extern int register_android_media_MediaCodec(JNIEnv *env);
876extern int register_android_media_MediaExtractor(JNIEnv *env);
877extern int register_android_media_MediaCodecList(JNIEnv *env);
878extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
879extern int register_android_media_MediaRecorder(JNIEnv *env);
880extern int register_android_media_MediaScanner(JNIEnv *env);
881extern int register_android_media_ResampleInputStream(JNIEnv *env);
882extern int register_android_media_MediaProfiles(JNIEnv *env);
883extern int register_android_media_AmrInputStream(JNIEnv *env);
884extern int register_android_mtp_MtpDatabase(JNIEnv *env);
885extern int register_android_mtp_MtpDevice(JNIEnv *env);
886extern int register_android_mtp_MtpServer(JNIEnv *env);
887
888jint JNI_OnLoad(JavaVM* vm, void* reserved)
889{
890    JNIEnv* env = NULL;
891    jint result = -1;
892
893    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
894        ALOGE("ERROR: GetEnv failed\n");
895        goto bail;
896    }
897    assert(env != NULL);
898
899    if (register_android_media_MediaPlayer(env) < 0) {
900        ALOGE("ERROR: MediaPlayer native registration failed\n");
901        goto bail;
902    }
903
904    if (register_android_media_MediaRecorder(env) < 0) {
905        ALOGE("ERROR: MediaRecorder native registration failed\n");
906        goto bail;
907    }
908
909    if (register_android_media_MediaScanner(env) < 0) {
910        ALOGE("ERROR: MediaScanner native registration failed\n");
911        goto bail;
912    }
913
914    if (register_android_media_MediaMetadataRetriever(env) < 0) {
915        ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
916        goto bail;
917    }
918
919    if (register_android_media_AmrInputStream(env) < 0) {
920        ALOGE("ERROR: AmrInputStream native registration failed\n");
921        goto bail;
922    }
923
924    if (register_android_media_ResampleInputStream(env) < 0) {
925        ALOGE("ERROR: ResampleInputStream native registration failed\n");
926        goto bail;
927    }
928
929    if (register_android_media_MediaProfiles(env) < 0) {
930        ALOGE("ERROR: MediaProfiles native registration failed");
931        goto bail;
932    }
933
934    if (register_android_mtp_MtpDatabase(env) < 0) {
935        ALOGE("ERROR: MtpDatabase native registration failed");
936        goto bail;
937    }
938
939    if (register_android_mtp_MtpDevice(env) < 0) {
940        ALOGE("ERROR: MtpDevice native registration failed");
941        goto bail;
942    }
943
944    if (register_android_mtp_MtpServer(env) < 0) {
945        ALOGE("ERROR: MtpServer native registration failed");
946        goto bail;
947    }
948
949    if (register_android_media_MediaCodec(env) < 0) {
950        ALOGE("ERROR: MediaCodec native registration failed");
951        goto bail;
952    }
953
954    if (register_android_media_MediaExtractor(env) < 0) {
955        ALOGE("ERROR: MediaCodec native registration failed");
956        goto bail;
957    }
958
959    if (register_android_media_MediaCodecList(env) < 0) {
960        ALOGE("ERROR: MediaCodec native registration failed");
961        goto bail;
962    }
963
964    if (register_android_media_Crypto(env) < 0) {
965        ALOGE("ERROR: MediaCodec native registration failed");
966        goto bail;
967    }
968
969    /* success -- return valid version number */
970    result = JNI_VERSION_1_4;
971
972bail:
973    return result;
974}
975
976// KTHXBYE
977