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