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