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