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/AudioResamplerPublic.h>
24#include <media/IMediaHTTPService.h>
25#include <media/MediaPlayerInterface.h>
26#include <stdio.h>
27#include <assert.h>
28#include <limits.h>
29#include <unistd.h>
30#include <fcntl.h>
31#include <utils/threads.h>
32#include "jni.h"
33#include "JNIHelp.h"
34#include "android_runtime/AndroidRuntime.h"
35#include "android_runtime/android_view_Surface.h"
36#include "android_runtime/Log.h"
37#include "utils/Errors.h"  // for status_t
38#include "utils/KeyedVector.h"
39#include "utils/String8.h"
40#include "android_media_MediaDataSource.h"
41#include "android_media_PlaybackParams.h"
42#include "android_media_SyncParams.h"
43#include "android_media_Utils.h"
44
45#include "android_os_Parcel.h"
46#include "android_util_Binder.h"
47#include <binder/Parcel.h>
48#include <gui/IGraphicBufferProducer.h>
49#include <gui/Surface.h>
50#include <binder/IPCThreadState.h>
51#include <binder/IServiceManager.h>
52
53#include "android_util_Binder.h"
54// ----------------------------------------------------------------------------
55
56using namespace android;
57
58// ----------------------------------------------------------------------------
59
60struct fields_t {
61    jfieldID    context;
62    jfieldID    surface_texture;
63
64    jmethodID   post_event;
65
66    jmethodID   proxyConfigGetHost;
67    jmethodID   proxyConfigGetPort;
68    jmethodID   proxyConfigGetExclusionList;
69};
70static fields_t fields;
71
72static PlaybackParams::fields_t gPlaybackParamsFields;
73static SyncParams::fields_t gSyncParamsFields;
74
75static Mutex sLock;
76
77// ----------------------------------------------------------------------------
78// ref-counted object for callbacks
79class JNIMediaPlayerListener: public MediaPlayerListener
80{
81public:
82    JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
83    ~JNIMediaPlayerListener();
84    virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
85private:
86    JNIMediaPlayerListener();
87    jclass      mClass;     // Reference to MediaPlayer class
88    jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
89};
90
91JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
92{
93
94    // Hold onto the MediaPlayer class for use in calling the static method
95    // that posts events to the application thread.
96    jclass clazz = env->GetObjectClass(thiz);
97    if (clazz == NULL) {
98        ALOGE("Can't find android/media/MediaPlayer");
99        jniThrowException(env, "java/lang/Exception", NULL);
100        return;
101    }
102    mClass = (jclass)env->NewGlobalRef(clazz);
103
104    // We use a weak reference so the MediaPlayer object can be garbage collected.
105    // The reference is only used as a proxy for callbacks.
106    mObject  = env->NewGlobalRef(weak_thiz);
107}
108
109JNIMediaPlayerListener::~JNIMediaPlayerListener()
110{
111    // remove global references
112    JNIEnv *env = AndroidRuntime::getJNIEnv();
113    env->DeleteGlobalRef(mObject);
114    env->DeleteGlobalRef(mClass);
115}
116
117void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
118{
119    JNIEnv *env = AndroidRuntime::getJNIEnv();
120    if (obj && obj->dataSize() > 0) {
121        jobject jParcel = createJavaParcelObject(env);
122        if (jParcel != NULL) {
123            Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
124            nativeParcel->setData(obj->data(), obj->dataSize());
125            env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
126                    msg, ext1, ext2, jParcel);
127            env->DeleteLocalRef(jParcel);
128        }
129    } else {
130        env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
131                msg, ext1, ext2, NULL);
132    }
133    if (env->ExceptionCheck()) {
134        ALOGW("An exception occurred while notifying an event.");
135        LOGW_EX(env);
136        env->ExceptionClear();
137    }
138}
139
140// ----------------------------------------------------------------------------
141
142static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
143{
144    Mutex::Autolock l(sLock);
145    MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
146    return sp<MediaPlayer>(p);
147}
148
149static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
150{
151    Mutex::Autolock l(sLock);
152    sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
153    if (player.get()) {
154        player->incStrong((void*)setMediaPlayer);
155    }
156    if (old != 0) {
157        old->decStrong((void*)setMediaPlayer);
158    }
159    env->SetLongField(thiz, fields.context, (jlong)player.get());
160    return old;
161}
162
163// If exception is NULL and opStatus is not OK, this method sends an error
164// event to the client application; otherwise, if exception is not NULL and
165// opStatus is not OK, this method throws the given exception to the client
166// application.
167static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
168{
169    if (exception == NULL) {  // Don't throw exception. Instead, send an event.
170        if (opStatus != (status_t) OK) {
171            sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
172            if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
173        }
174    } else {  // Throw exception!
175        if ( opStatus == (status_t) INVALID_OPERATION ) {
176            jniThrowException(env, "java/lang/IllegalStateException", NULL);
177        } else if ( opStatus == (status_t) BAD_VALUE ) {
178            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
179        } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
180            jniThrowException(env, "java/lang/SecurityException", NULL);
181        } else if ( opStatus != (status_t) OK ) {
182            if (strlen(message) > 230) {
183               // if the message is too long, don't bother displaying the status code
184               jniThrowException( env, exception, message);
185            } else {
186               char msg[256];
187                // append the status code to the message
188               sprintf(msg, "%s: status=0x%X", message, opStatus);
189               jniThrowException( env, exception, msg);
190            }
191        }
192    }
193}
194
195static void
196android_media_MediaPlayer_setDataSourceAndHeaders(
197        JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
198        jobjectArray keys, jobjectArray values) {
199
200    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
201    if (mp == NULL ) {
202        jniThrowException(env, "java/lang/IllegalStateException", NULL);
203        return;
204    }
205
206    if (path == NULL) {
207        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
208        return;
209    }
210
211    const char *tmp = env->GetStringUTFChars(path, NULL);
212    if (tmp == NULL) {  // Out of memory
213        return;
214    }
215    ALOGV("setDataSource: path %s", tmp);
216
217    String8 pathStr(tmp);
218    env->ReleaseStringUTFChars(path, tmp);
219    tmp = NULL;
220
221    // We build a KeyedVector out of the key and val arrays
222    KeyedVector<String8, String8> headersVector;
223    if (!ConvertKeyValueArraysToKeyedVector(
224            env, keys, values, &headersVector)) {
225        return;
226    }
227
228    sp<IMediaHTTPService> httpService;
229    if (httpServiceBinderObj != NULL) {
230        sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
231        httpService = interface_cast<IMediaHTTPService>(binder);
232    }
233
234    status_t opStatus =
235        mp->setDataSource(
236                httpService,
237                pathStr,
238                headersVector.size() > 0? &headersVector : NULL);
239
240    process_media_player_call(
241            env, thiz, opStatus, "java/io/IOException",
242            "setDataSource failed." );
243}
244
245static void
246android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
247{
248    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
249    if (mp == NULL ) {
250        jniThrowException(env, "java/lang/IllegalStateException", NULL);
251        return;
252    }
253
254    if (fileDescriptor == NULL) {
255        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
256        return;
257    }
258    int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
259    ALOGV("setDataSourceFD: fd %d", fd);
260    process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
261}
262
263static void
264android_media_MediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
265{
266    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
267    if (mp == NULL ) {
268        jniThrowException(env, "java/lang/IllegalStateException", NULL);
269        return;
270    }
271
272    if (dataSource == NULL) {
273        jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
274        return;
275    }
276    sp<IDataSource> callbackDataSource = new JMediaDataSource(env, dataSource);
277    process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
278}
279
280static sp<IGraphicBufferProducer>
281getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
282    IGraphicBufferProducer * const p = (IGraphicBufferProducer*)env->GetLongField(thiz, fields.surface_texture);
283    return sp<IGraphicBufferProducer>(p);
284}
285
286static void
287decVideoSurfaceRef(JNIEnv *env, jobject thiz)
288{
289    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
290    if (mp == NULL) {
291        return;
292    }
293
294    sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
295    if (old_st != NULL) {
296        old_st->decStrong((void*)decVideoSurfaceRef);
297    }
298}
299
300static void
301setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
302{
303    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
304    if (mp == NULL) {
305        if (mediaPlayerMustBeAlive) {
306            jniThrowException(env, "java/lang/IllegalStateException", NULL);
307        }
308        return;
309    }
310
311    decVideoSurfaceRef(env, thiz);
312
313    sp<IGraphicBufferProducer> new_st;
314    if (jsurface) {
315        sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
316        if (surface != NULL) {
317            new_st = surface->getIGraphicBufferProducer();
318            if (new_st == NULL) {
319                jniThrowException(env, "java/lang/IllegalArgumentException",
320                    "The surface does not have a binding SurfaceTexture!");
321                return;
322            }
323            new_st->incStrong((void*)decVideoSurfaceRef);
324        } else {
325            jniThrowException(env, "java/lang/IllegalArgumentException",
326                    "The surface has been released");
327            return;
328        }
329    }
330
331    env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());
332
333    // This will fail if the media player has not been initialized yet. This
334    // can be the case if setDisplay() on MediaPlayer.java has been called
335    // before setDataSource(). The redundant call to setVideoSurfaceTexture()
336    // in prepare/prepareAsync covers for this case.
337    mp->setVideoSurfaceTexture(new_st);
338}
339
340static void
341android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
342{
343    setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
344}
345
346static void
347android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
348{
349    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
350    if (mp == NULL ) {
351        jniThrowException(env, "java/lang/IllegalStateException", NULL);
352        return;
353    }
354
355    // Handle the case where the display surface was set before the mp was
356    // initialized. We try again to make it stick.
357    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
358    mp->setVideoSurfaceTexture(st);
359
360    process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
361}
362
363static void
364android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
365{
366    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
367    if (mp == NULL ) {
368        jniThrowException(env, "java/lang/IllegalStateException", NULL);
369        return;
370    }
371
372    // Handle the case where the display surface was set before the mp was
373    // initialized. We try again to make it stick.
374    sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
375    mp->setVideoSurfaceTexture(st);
376
377    process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
378}
379
380static void
381android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
382{
383    ALOGV("start");
384    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
385    if (mp == NULL ) {
386        jniThrowException(env, "java/lang/IllegalStateException", NULL);
387        return;
388    }
389    process_media_player_call( env, thiz, mp->start(), NULL, NULL );
390}
391
392static void
393android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
394{
395    ALOGV("stop");
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->stop(), NULL, NULL );
402}
403
404static void
405android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz)
406{
407    ALOGV("pause");
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->pause(), NULL, NULL );
414}
415
416static jboolean
417android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
418{
419    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
420    if (mp == NULL ) {
421        jniThrowException(env, "java/lang/IllegalStateException", NULL);
422        return JNI_FALSE;
423    }
424    const jboolean is_playing = mp->isPlaying();
425
426    ALOGV("isPlaying: %d", is_playing);
427    return is_playing;
428}
429
430static void
431android_media_MediaPlayer_setPlaybackParams(JNIEnv *env, jobject thiz, jobject params)
432{
433    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
434    if (mp == NULL) {
435        jniThrowException(env, "java/lang/IllegalStateException", NULL);
436        return;
437    }
438
439    PlaybackParams pbp;
440    pbp.fillFromJobject(env, gPlaybackParamsFields, params);
441    ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
442            pbp.speedSet, pbp.audioRate.mSpeed,
443            pbp.pitchSet, pbp.audioRate.mPitch,
444            pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
445            pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
446
447    AudioPlaybackRate rate;
448    status_t err = mp->getPlaybackSettings(&rate);
449    if (err == OK) {
450        bool updatedRate = false;
451        if (pbp.speedSet) {
452            rate.mSpeed = pbp.audioRate.mSpeed;
453            updatedRate = true;
454        }
455        if (pbp.pitchSet) {
456            rate.mPitch = pbp.audioRate.mPitch;
457            updatedRate = true;
458        }
459        if (pbp.audioFallbackModeSet) {
460            rate.mFallbackMode = pbp.audioRate.mFallbackMode;
461            updatedRate = true;
462        }
463        if (pbp.audioStretchModeSet) {
464            rate.mStretchMode = pbp.audioRate.mStretchMode;
465            updatedRate = true;
466        }
467        if (updatedRate) {
468            err = mp->setPlaybackSettings(rate);
469        }
470    }
471    process_media_player_call(
472            env, thiz, err,
473            "java/lang/IllegalStateException", "unexpected error");
474}
475
476static jobject
477android_media_MediaPlayer_getPlaybackParams(JNIEnv *env, jobject thiz)
478{
479    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
480    if (mp == NULL) {
481        jniThrowException(env, "java/lang/IllegalStateException", NULL);
482        return NULL;
483    }
484
485    PlaybackParams pbp;
486    AudioPlaybackRate &audioRate = pbp.audioRate;
487    process_media_player_call(
488            env, thiz, mp->getPlaybackSettings(&audioRate),
489            "java/lang/IllegalStateException", "unexpected error");
490    ALOGV("getPlaybackSettings: %f %f %d %d",
491            audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
492
493    pbp.speedSet = true;
494    pbp.pitchSet = true;
495    pbp.audioFallbackModeSet = true;
496    pbp.audioStretchModeSet = true;
497
498    return pbp.asJobject(env, gPlaybackParamsFields);
499}
500
501static void
502android_media_MediaPlayer_setSyncParams(JNIEnv *env, jobject thiz, jobject params)
503{
504    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
505    if (mp == NULL) {
506        jniThrowException(env, "java/lang/IllegalStateException", NULL);
507        return;
508    }
509
510    SyncParams scp;
511    scp.fillFromJobject(env, gSyncParamsFields, params);
512    ALOGV("setSyncParams: %d:%d %d:%d %d:%f %d:%f",
513          scp.syncSourceSet, scp.sync.mSource,
514          scp.audioAdjustModeSet, scp.sync.mAudioAdjustMode,
515          scp.toleranceSet, scp.sync.mTolerance,
516          scp.frameRateSet, scp.frameRate);
517
518    AVSyncSettings avsync;
519    float videoFrameRate;
520    status_t err = mp->getSyncSettings(&avsync, &videoFrameRate);
521    if (err == OK) {
522        bool updatedSync = scp.frameRateSet;
523        if (scp.syncSourceSet) {
524            avsync.mSource = scp.sync.mSource;
525            updatedSync = true;
526        }
527        if (scp.audioAdjustModeSet) {
528            avsync.mAudioAdjustMode = scp.sync.mAudioAdjustMode;
529            updatedSync = true;
530        }
531        if (scp.toleranceSet) {
532            avsync.mTolerance = scp.sync.mTolerance;
533            updatedSync = true;
534        }
535        if (updatedSync) {
536            err = mp->setSyncSettings(avsync, scp.frameRateSet ? scp.frameRate : -1.f);
537        }
538    }
539    process_media_player_call(
540            env, thiz, err,
541            "java/lang/IllegalStateException", "unexpected error");
542}
543
544static jobject
545android_media_MediaPlayer_getSyncParams(JNIEnv *env, jobject thiz)
546{
547    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
548    if (mp == NULL) {
549        jniThrowException(env, "java/lang/IllegalStateException", NULL);
550        return NULL;
551    }
552
553    SyncParams scp;
554    scp.frameRate = -1.f;
555    process_media_player_call(
556            env, thiz, mp->getSyncSettings(&scp.sync, &scp.frameRate),
557            "java/lang/IllegalStateException", "unexpected error");
558
559    ALOGV("getSyncSettings: %d %d %f %f",
560            scp.sync.mSource, scp.sync.mAudioAdjustMode, scp.sync.mTolerance, scp.frameRate);
561
562    // sanity check params
563    if (scp.sync.mSource >= AVSYNC_SOURCE_MAX
564            || scp.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
565            || scp.sync.mTolerance < 0.f
566            || scp.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
567        jniThrowException(env,  "java/lang/IllegalStateException", NULL);
568        return NULL;
569    }
570
571    scp.syncSourceSet = true;
572    scp.audioAdjustModeSet = true;
573    scp.toleranceSet = true;
574    scp.frameRateSet = scp.frameRate >= 0.f;
575
576    return scp.asJobject(env, gSyncParamsFields);
577}
578
579static void
580android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, jint msec)
581{
582    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
583    if (mp == NULL ) {
584        jniThrowException(env, "java/lang/IllegalStateException", NULL);
585        return;
586    }
587    ALOGV("seekTo: %d(msec)", msec);
588    process_media_player_call( env, thiz, mp->seekTo(msec), NULL, NULL );
589}
590
591static jint
592android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
593{
594    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
595    if (mp == NULL ) {
596        jniThrowException(env, "java/lang/IllegalStateException", NULL);
597        return 0;
598    }
599    int w;
600    if (0 != mp->getVideoWidth(&w)) {
601        ALOGE("getVideoWidth failed");
602        w = 0;
603    }
604    ALOGV("getVideoWidth: %d", w);
605    return (jint) w;
606}
607
608static jint
609android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
610{
611    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
612    if (mp == NULL ) {
613        jniThrowException(env, "java/lang/IllegalStateException", NULL);
614        return 0;
615    }
616    int h;
617    if (0 != mp->getVideoHeight(&h)) {
618        ALOGE("getVideoHeight failed");
619        h = 0;
620    }
621    ALOGV("getVideoHeight: %d", h);
622    return (jint) h;
623}
624
625
626static jint
627android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
628{
629    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
630    if (mp == NULL ) {
631        jniThrowException(env, "java/lang/IllegalStateException", NULL);
632        return 0;
633    }
634    int msec;
635    process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
636    ALOGV("getCurrentPosition: %d (msec)", msec);
637    return (jint) msec;
638}
639
640static jint
641android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
642{
643    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
644    if (mp == NULL ) {
645        jniThrowException(env, "java/lang/IllegalStateException", NULL);
646        return 0;
647    }
648    int msec;
649    process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
650    ALOGV("getDuration: %d (msec)", msec);
651    return (jint) msec;
652}
653
654static void
655android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
656{
657    ALOGV("reset");
658    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
659    if (mp == NULL ) {
660        jniThrowException(env, "java/lang/IllegalStateException", NULL);
661        return;
662    }
663    process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
664}
665
666static void
667android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint streamtype)
668{
669    ALOGV("setAudioStreamType: %d", streamtype);
670    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
671    if (mp == NULL ) {
672        jniThrowException(env, "java/lang/IllegalStateException", NULL);
673        return;
674    }
675    process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
676}
677
678static jint
679android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
680{
681    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
682    if (mp == NULL ) {
683        jniThrowException(env, "java/lang/IllegalStateException", NULL);
684        return 0;
685    }
686    audio_stream_type_t streamtype;
687    process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
688    ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
689    return (jint) streamtype;
690}
691
692static jboolean
693android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
694{
695    ALOGV("setParameter: key %d", key);
696    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
697    if (mp == NULL ) {
698        jniThrowException(env, "java/lang/IllegalStateException", NULL);
699        return false;
700    }
701
702    Parcel *request = parcelForJavaObject(env, java_request);
703    status_t err = mp->setParameter(key, *request);
704    if (err == OK) {
705        return true;
706    } else {
707        return false;
708    }
709}
710
711static void
712android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
713{
714    ALOGV("setLooping: %d", looping);
715    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
716    if (mp == NULL ) {
717        jniThrowException(env, "java/lang/IllegalStateException", NULL);
718        return;
719    }
720    process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
721}
722
723static jboolean
724android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz)
725{
726    ALOGV("isLooping");
727    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
728    if (mp == NULL ) {
729        jniThrowException(env, "java/lang/IllegalStateException", NULL);
730        return JNI_FALSE;
731    }
732    return mp->isLooping() ? JNI_TRUE : JNI_FALSE;
733}
734
735static void
736android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, jfloat leftVolume, jfloat rightVolume)
737{
738    ALOGV("setVolume: left %f  right %f", (float) leftVolume, (float) rightVolume);
739    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
740    if (mp == NULL ) {
741        jniThrowException(env, "java/lang/IllegalStateException", NULL);
742        return;
743    }
744    process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL );
745}
746
747// Sends the request and reply parcels to the media player via the
748// binder interface.
749static jint
750android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
751                                 jobject java_request, jobject java_reply)
752{
753    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
754    if (media_player == NULL ) {
755        jniThrowException(env, "java/lang/IllegalStateException", NULL);
756        return UNKNOWN_ERROR;
757    }
758
759    Parcel *request = parcelForJavaObject(env, java_request);
760    Parcel *reply = parcelForJavaObject(env, java_reply);
761
762    // Don't use process_media_player_call which use the async loop to
763    // report errors, instead returns the status.
764    return (jint) media_player->invoke(*request, reply);
765}
766
767// Sends the new filter to the client.
768static jint
769android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
770{
771    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
772    if (media_player == NULL ) {
773        jniThrowException(env, "java/lang/IllegalStateException", NULL);
774        return UNKNOWN_ERROR;
775    }
776
777    Parcel *filter = parcelForJavaObject(env, request);
778
779    if (filter == NULL ) {
780        jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
781        return UNKNOWN_ERROR;
782    }
783
784    return (jint) media_player->setMetadataFilter(*filter);
785}
786
787static jboolean
788android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
789                                      jboolean apply_filter, jobject reply)
790{
791    sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
792    if (media_player == NULL ) {
793        jniThrowException(env, "java/lang/IllegalStateException", NULL);
794        return JNI_FALSE;
795    }
796
797    Parcel *metadata = parcelForJavaObject(env, reply);
798
799    if (metadata == NULL ) {
800        jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
801        return JNI_FALSE;
802    }
803
804    metadata->freeData();
805    // On return metadata is positioned at the beginning of the
806    // metadata. Note however that the parcel actually starts with the
807    // return code so you should not rewind the parcel using
808    // setDataPosition(0).
809    if (media_player->getMetadata(update_only, apply_filter, metadata) == OK) {
810        return JNI_TRUE;
811    } else {
812        return JNI_FALSE;
813    }
814}
815
816// This function gets some field IDs, which in turn causes class initialization.
817// It is called from a static block in MediaPlayer, which won't run until the
818// first time an instance of this class is used.
819static void
820android_media_MediaPlayer_native_init(JNIEnv *env)
821{
822    jclass clazz;
823
824    clazz = env->FindClass("android/media/MediaPlayer");
825    if (clazz == NULL) {
826        return;
827    }
828
829    fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
830    if (fields.context == NULL) {
831        return;
832    }
833
834    fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
835                                               "(Ljava/lang/Object;IIILjava/lang/Object;)V");
836    if (fields.post_event == NULL) {
837        return;
838    }
839
840    fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
841    if (fields.surface_texture == NULL) {
842        return;
843    }
844
845    env->DeleteLocalRef(clazz);
846
847    clazz = env->FindClass("android/net/ProxyInfo");
848    if (clazz == NULL) {
849        return;
850    }
851
852    fields.proxyConfigGetHost =
853        env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
854
855    fields.proxyConfigGetPort =
856        env->GetMethodID(clazz, "getPort", "()I");
857
858    fields.proxyConfigGetExclusionList =
859        env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
860
861    env->DeleteLocalRef(clazz);
862
863    gPlaybackParamsFields.init(env);
864    gSyncParamsFields.init(env);
865}
866
867static void
868android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
869{
870    ALOGV("native_setup");
871    sp<MediaPlayer> mp = new MediaPlayer();
872    if (mp == NULL) {
873        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
874        return;
875    }
876
877    // create new listener and give it to MediaPlayer
878    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
879    mp->setListener(listener);
880
881    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
882    setMediaPlayer(env, thiz, mp);
883}
884
885static void
886android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
887{
888    ALOGV("release");
889    decVideoSurfaceRef(env, thiz);
890    sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
891    if (mp != NULL) {
892        // this prevents native callbacks after the object is released
893        mp->setListener(0);
894        mp->disconnect();
895    }
896}
897
898static void
899android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
900{
901    ALOGV("native_finalize");
902    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
903    if (mp != NULL) {
904        ALOGW("MediaPlayer finalized without being released");
905    }
906    android_media_MediaPlayer_release(env, thiz);
907}
908
909static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env,  jobject thiz,
910        jint sessionId) {
911    ALOGV("set_session_id(): %d", sessionId);
912    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
913    if (mp == NULL ) {
914        jniThrowException(env, "java/lang/IllegalStateException", NULL);
915        return;
916    }
917    process_media_player_call( env, thiz, mp->setAudioSessionId((audio_session_t) sessionId), NULL,
918            NULL);
919}
920
921static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env,  jobject thiz) {
922    ALOGV("get_session_id()");
923    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
924    if (mp == NULL ) {
925        jniThrowException(env, "java/lang/IllegalStateException", NULL);
926        return 0;
927    }
928
929    return (jint) mp->getAudioSessionId();
930}
931
932static void
933android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
934{
935    ALOGV("setAuxEffectSendLevel: level %f", level);
936    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
937    if (mp == NULL ) {
938        jniThrowException(env, "java/lang/IllegalStateException", NULL);
939        return;
940    }
941    process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
942}
943
944static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env,  jobject thiz, jint effectId) {
945    ALOGV("attachAuxEffect(): %d", effectId);
946    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
947    if (mp == NULL ) {
948        jniThrowException(env, "java/lang/IllegalStateException", NULL);
949        return;
950    }
951    process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
952}
953
954static jint
955android_media_MediaPlayer_pullBatteryData(
956        JNIEnv *env, jobject /* thiz */, jobject java_reply)
957{
958    sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player"));
959    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
960    if (service.get() == NULL) {
961        jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService");
962        return UNKNOWN_ERROR;
963    }
964
965    Parcel *reply = parcelForJavaObject(env, java_reply);
966
967    return (jint) service->pullBatteryData(reply);
968}
969
970static jint
971android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
972                                                jstring addrString, jint port) {
973    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
974    if (mp == NULL ) {
975        jniThrowException(env, "java/lang/IllegalStateException", NULL);
976        return INVALID_OPERATION;
977    }
978
979    const char *cAddrString = NULL;
980
981    if (NULL != addrString) {
982        cAddrString = env->GetStringUTFChars(addrString, NULL);
983        if (cAddrString == NULL) {  // Out of memory
984            return NO_MEMORY;
985        }
986    }
987    ALOGV("setRetransmitEndpoint: %s:%d",
988            cAddrString ? cAddrString : "(null)", port);
989
990    status_t ret;
991    if (cAddrString && (port > 0xFFFF)) {
992        ret = BAD_VALUE;
993    } else {
994        ret = mp->setRetransmitEndpoint(cAddrString,
995                static_cast<uint16_t>(port));
996    }
997
998    if (NULL != addrString) {
999        env->ReleaseStringUTFChars(addrString, cAddrString);
1000    }
1001
1002    if (ret == INVALID_OPERATION ) {
1003        jniThrowException(env, "java/lang/IllegalStateException", NULL);
1004    }
1005
1006    return (jint) ret;
1007}
1008
1009static void
1010android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
1011{
1012    ALOGV("setNextMediaPlayer");
1013    sp<MediaPlayer> thisplayer = getMediaPlayer(env, thiz);
1014    if (thisplayer == NULL) {
1015        jniThrowException(env, "java/lang/IllegalStateException", "This player not initialized");
1016        return;
1017    }
1018    sp<MediaPlayer> nextplayer = (java_player == NULL) ? NULL : getMediaPlayer(env, java_player);
1019    if (nextplayer == NULL && java_player != NULL) {
1020        jniThrowException(env, "java/lang/IllegalStateException", "That player not initialized");
1021        return;
1022    }
1023
1024    if (nextplayer == thisplayer) {
1025        jniThrowException(env, "java/lang/IllegalArgumentException", "Next player can't be self");
1026        return;
1027    }
1028    // tie the two players together
1029    process_media_player_call(
1030            env, thiz, thisplayer->setNextMediaPlayer(nextplayer),
1031            "java/lang/IllegalArgumentException",
1032            "setNextMediaPlayer failed." );
1033    ;
1034}
1035
1036// ----------------------------------------------------------------------------
1037
1038static const JNINativeMethod gMethods[] = {
1039    {
1040        "nativeSetDataSource",
1041        "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
1042        "[Ljava/lang/String;)V",
1043        (void *)android_media_MediaPlayer_setDataSourceAndHeaders
1044    },
1045
1046    {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
1047    {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
1048    {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
1049    {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
1050    {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
1051    {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
1052    {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
1053    {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
1054    {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
1055    {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
1056    {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
1057    {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer_setSyncParams},
1058    {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer_getSyncParams},
1059    {"seekTo",              "(I)V",                             (void *)android_media_MediaPlayer_seekTo},
1060    {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
1061    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
1062    {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
1063    {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
1064    {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
1065    {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
1066    {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
1067    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},
1068    {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
1069    {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
1070    {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
1071    {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
1072    {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
1073    {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
1074    {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
1075    {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
1076    {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
1077    {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
1078    {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
1079    {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
1080    {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
1081    {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
1082    {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
1083    {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
1084    {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
1085};
1086
1087// This function only registers the native methods
1088static int register_android_media_MediaPlayer(JNIEnv *env)
1089{
1090    return AndroidRuntime::registerNativeMethods(env,
1091                "android/media/MediaPlayer", gMethods, NELEM(gMethods));
1092}
1093extern int register_android_media_ExifInterface(JNIEnv *env);
1094extern int register_android_media_ImageReader(JNIEnv *env);
1095extern int register_android_media_ImageWriter(JNIEnv *env);
1096extern int register_android_media_Crypto(JNIEnv *env);
1097extern int register_android_media_Drm(JNIEnv *env);
1098extern int register_android_media_MediaCodec(JNIEnv *env);
1099extern int register_android_media_MediaExtractor(JNIEnv *env);
1100extern int register_android_media_MediaCodecList(JNIEnv *env);
1101extern int register_android_media_MediaHTTPConnection(JNIEnv *env);
1102extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
1103extern int register_android_media_MediaMuxer(JNIEnv *env);
1104extern int register_android_media_MediaRecorder(JNIEnv *env);
1105extern int register_android_media_MediaScanner(JNIEnv *env);
1106extern int register_android_media_MediaSync(JNIEnv *env);
1107extern int register_android_media_ResampleInputStream(JNIEnv *env);
1108extern int register_android_media_MediaProfiles(JNIEnv *env);
1109extern int register_android_media_AmrInputStream(JNIEnv *env);
1110extern int register_android_mtp_MtpDatabase(JNIEnv *env);
1111extern int register_android_mtp_MtpDevice(JNIEnv *env);
1112extern int register_android_mtp_MtpServer(JNIEnv *env);
1113
1114jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
1115{
1116    JNIEnv* env = NULL;
1117    jint result = -1;
1118
1119    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
1120        ALOGE("ERROR: GetEnv failed\n");
1121        goto bail;
1122    }
1123    assert(env != NULL);
1124
1125    if (register_android_media_ImageWriter(env) != JNI_OK) {
1126        ALOGE("ERROR: ImageWriter native registration failed");
1127        goto bail;
1128    }
1129
1130    if (register_android_media_ImageReader(env) < 0) {
1131        ALOGE("ERROR: ImageReader native registration failed");
1132        goto bail;
1133    }
1134
1135    if (register_android_media_MediaPlayer(env) < 0) {
1136        ALOGE("ERROR: MediaPlayer native registration failed\n");
1137        goto bail;
1138    }
1139
1140    if (register_android_media_MediaRecorder(env) < 0) {
1141        ALOGE("ERROR: MediaRecorder native registration failed\n");
1142        goto bail;
1143    }
1144
1145    if (register_android_media_MediaScanner(env) < 0) {
1146        ALOGE("ERROR: MediaScanner native registration failed\n");
1147        goto bail;
1148    }
1149
1150    if (register_android_media_MediaMetadataRetriever(env) < 0) {
1151        ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
1152        goto bail;
1153    }
1154
1155    if (register_android_media_AmrInputStream(env) < 0) {
1156        ALOGE("ERROR: AmrInputStream native registration failed\n");
1157        goto bail;
1158    }
1159
1160    if (register_android_media_ResampleInputStream(env) < 0) {
1161        ALOGE("ERROR: ResampleInputStream native registration failed\n");
1162        goto bail;
1163    }
1164
1165    if (register_android_media_MediaProfiles(env) < 0) {
1166        ALOGE("ERROR: MediaProfiles native registration failed");
1167        goto bail;
1168    }
1169
1170    if (register_android_mtp_MtpDatabase(env) < 0) {
1171        ALOGE("ERROR: MtpDatabase native registration failed");
1172        goto bail;
1173    }
1174
1175    if (register_android_mtp_MtpDevice(env) < 0) {
1176        ALOGE("ERROR: MtpDevice native registration failed");
1177        goto bail;
1178    }
1179
1180    if (register_android_mtp_MtpServer(env) < 0) {
1181        ALOGE("ERROR: MtpServer native registration failed");
1182        goto bail;
1183    }
1184
1185    if (register_android_media_MediaCodec(env) < 0) {
1186        ALOGE("ERROR: MediaCodec native registration failed");
1187        goto bail;
1188    }
1189
1190    if (register_android_media_MediaSync(env) < 0) {
1191        ALOGE("ERROR: MediaSync native registration failed");
1192        goto bail;
1193    }
1194
1195    if (register_android_media_MediaExtractor(env) < 0) {
1196        ALOGE("ERROR: MediaCodec native registration failed");
1197        goto bail;
1198    }
1199
1200    if (register_android_media_MediaMuxer(env) < 0) {
1201        ALOGE("ERROR: MediaMuxer native registration failed");
1202        goto bail;
1203    }
1204
1205    if (register_android_media_MediaCodecList(env) < 0) {
1206        ALOGE("ERROR: MediaCodec native registration failed");
1207        goto bail;
1208    }
1209
1210    if (register_android_media_Crypto(env) < 0) {
1211        ALOGE("ERROR: MediaCodec native registration failed");
1212        goto bail;
1213    }
1214
1215    if (register_android_media_Drm(env) < 0) {
1216        ALOGE("ERROR: MediaDrm native registration failed");
1217        goto bail;
1218    }
1219
1220    if (register_android_media_MediaHTTPConnection(env) < 0) {
1221        ALOGE("ERROR: MediaHTTPConnection native registration failed");
1222        goto bail;
1223    }
1224
1225    if (register_android_media_ExifInterface(env) < 0) {
1226        ALOGE("ERROR: ExifInterface native registration failed");
1227        goto bail;
1228    }
1229
1230    /* success -- return valid version number */
1231    result = JNI_VERSION_1_4;
1232
1233bail:
1234    return result;
1235}
1236
1237// KTHXBYE
1238