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