• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <media/MediaAnalyticsItem.h>
27 #include <media/stagefright/foundation/ByteUtils.h>  // for FOURCC definition
28 #include <stdio.h>
29 #include <assert.h>
30 #include <limits.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <utils/threads.h>
34 #include "jni.h"
35 #include <nativehelper/JNIHelp.h>
36 #include "android_runtime/AndroidRuntime.h"
37 #include "android_runtime/android_view_Surface.h"
38 #include "android_runtime/Log.h"
39 #include "utils/Errors.h"  // for status_t
40 #include "utils/KeyedVector.h"
41 #include "utils/String8.h"
42 #include "android_media_BufferingParams.h"
43 #include "android_media_MediaDataSource.h"
44 #include "android_media_MediaMetricsJNI.h"
45 #include "android_media_PlaybackParams.h"
46 #include "android_media_SyncParams.h"
47 #include "android_media_VolumeShaper.h"
48 #include "android_media_Utils.h"
49 
50 #include "android_os_Parcel.h"
51 #include "android_util_Binder.h"
52 #include <binder/Parcel.h>
53 #include <gui/IGraphicBufferProducer.h>
54 #include <gui/Surface.h>
55 #include <binder/IPCThreadState.h>
56 #include <binder/IServiceManager.h>
57 
58 #include "android_util_Binder.h"
59 
60 // Modular DRM begin
61 #define FIND_CLASS(var, className) \
62 var = env->FindClass(className); \
63 LOG_FATAL_IF(! (var), "Unable to find class " className);
64 
65 #define GET_METHOD_ID(var, clazz, fieldName, fieldDescriptor) \
66 var = env->GetMethodID(clazz, fieldName, fieldDescriptor); \
67 LOG_FATAL_IF(! (var), "Unable to find method " fieldName);
68 
69 struct StateExceptionFields {
70     jmethodID init;
71     jclass classId;
72 };
73 
74 static StateExceptionFields gStateExceptionFields;
75 // Modular DRM end
76 
77 // ----------------------------------------------------------------------------
78 
79 using namespace android;
80 
81 using media::VolumeShaper;
82 
83 // ----------------------------------------------------------------------------
84 
85 struct fields_t {
86     jfieldID    context;
87     jfieldID    surface_texture;
88 
89     jmethodID   post_event;
90 
91     jmethodID   proxyConfigGetHost;
92     jmethodID   proxyConfigGetPort;
93     jmethodID   proxyConfigGetExclusionList;
94 };
95 static fields_t fields;
96 
97 static BufferingParams::fields_t gBufferingParamsFields;
98 static PlaybackParams::fields_t gPlaybackParamsFields;
99 static SyncParams::fields_t gSyncParamsFields;
100 static VolumeShaperHelper::fields_t gVolumeShaperFields;
101 
102 static Mutex sLock;
103 
104 // ----------------------------------------------------------------------------
105 // ref-counted object for callbacks
106 class JNIMediaPlayerListener: public MediaPlayerListener
107 {
108 public:
109     JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz);
110     ~JNIMediaPlayerListener();
111     virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
112 private:
113     JNIMediaPlayerListener();
114     jclass      mClass;     // Reference to MediaPlayer class
115     jobject     mObject;    // Weak ref to MediaPlayer Java object to call on
116 };
117 
JNIMediaPlayerListener(JNIEnv * env,jobject thiz,jobject weak_thiz)118 JNIMediaPlayerListener::JNIMediaPlayerListener(JNIEnv* env, jobject thiz, jobject weak_thiz)
119 {
120 
121     // Hold onto the MediaPlayer class for use in calling the static method
122     // that posts events to the application thread.
123     jclass clazz = env->GetObjectClass(thiz);
124     if (clazz == NULL) {
125         ALOGE("Can't find android/media/MediaPlayer");
126         jniThrowException(env, "java/lang/Exception", NULL);
127         return;
128     }
129     mClass = (jclass)env->NewGlobalRef(clazz);
130 
131     // We use a weak reference so the MediaPlayer object can be garbage collected.
132     // The reference is only used as a proxy for callbacks.
133     mObject  = env->NewGlobalRef(weak_thiz);
134 }
135 
~JNIMediaPlayerListener()136 JNIMediaPlayerListener::~JNIMediaPlayerListener()
137 {
138     // remove global references
139     JNIEnv *env = AndroidRuntime::getJNIEnv();
140     env->DeleteGlobalRef(mObject);
141     env->DeleteGlobalRef(mClass);
142 }
143 
notify(int msg,int ext1,int ext2,const Parcel * obj)144 void JNIMediaPlayerListener::notify(int msg, int ext1, int ext2, const Parcel *obj)
145 {
146     JNIEnv *env = AndroidRuntime::getJNIEnv();
147     if (obj && obj->dataSize() > 0) {
148         jobject jParcel = createJavaParcelObject(env);
149         if (jParcel != NULL) {
150             Parcel* nativeParcel = parcelForJavaObject(env, jParcel);
151             nativeParcel->setData(obj->data(), obj->dataSize());
152             env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
153                     msg, ext1, ext2, jParcel);
154             env->DeleteLocalRef(jParcel);
155         }
156     } else {
157         env->CallStaticVoidMethod(mClass, fields.post_event, mObject,
158                 msg, ext1, ext2, NULL);
159     }
160     if (env->ExceptionCheck()) {
161         ALOGW("An exception occurred while notifying an event.");
162         LOGW_EX(env);
163         env->ExceptionClear();
164     }
165 }
166 
167 // ----------------------------------------------------------------------------
168 
getMediaPlayer(JNIEnv * env,jobject thiz)169 static sp<MediaPlayer> getMediaPlayer(JNIEnv* env, jobject thiz)
170 {
171     Mutex::Autolock l(sLock);
172     MediaPlayer* const p = (MediaPlayer*)env->GetLongField(thiz, fields.context);
173     return sp<MediaPlayer>(p);
174 }
175 
setMediaPlayer(JNIEnv * env,jobject thiz,const sp<MediaPlayer> & player)176 static sp<MediaPlayer> setMediaPlayer(JNIEnv* env, jobject thiz, const sp<MediaPlayer>& player)
177 {
178     Mutex::Autolock l(sLock);
179     sp<MediaPlayer> old = (MediaPlayer*)env->GetLongField(thiz, fields.context);
180     if (player.get()) {
181         player->incStrong((void*)setMediaPlayer);
182     }
183     if (old != 0) {
184         old->decStrong((void*)setMediaPlayer);
185     }
186     env->SetLongField(thiz, fields.context, (jlong)player.get());
187     return old;
188 }
189 
190 // If exception is NULL and opStatus is not OK, this method sends an error
191 // event to the client application; otherwise, if exception is not NULL and
192 // opStatus is not OK, this method throws the given exception to the client
193 // application.
process_media_player_call(JNIEnv * env,jobject thiz,status_t opStatus,const char * exception,const char * message)194 static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStatus, const char* exception, const char *message)
195 {
196     if (exception == NULL) {  // Don't throw exception. Instead, send an event.
197         if (opStatus != (status_t) OK) {
198             sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
199             if (mp != 0) mp->notify(MEDIA_ERROR, opStatus, 0);
200         }
201     } else {  // Throw exception!
202         if ( opStatus == (status_t) INVALID_OPERATION ) {
203             jniThrowException(env, "java/lang/IllegalStateException", NULL);
204         } else if ( opStatus == (status_t) BAD_VALUE ) {
205             jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
206         } else if ( opStatus == (status_t) PERMISSION_DENIED ) {
207             jniThrowException(env, "java/lang/SecurityException", NULL);
208         } else if ( opStatus != (status_t) OK ) {
209             if (strlen(message) > 230) {
210                // if the message is too long, don't bother displaying the status code
211                jniThrowException( env, exception, message);
212             } else {
213                char msg[256];
214                 // append the status code to the message
215                sprintf(msg, "%s: status=0x%X", message, opStatus);
216                jniThrowException( env, exception, msg);
217             }
218         }
219     }
220 }
221 
222 static void
android_media_MediaPlayer_setDataSourceAndHeaders(JNIEnv * env,jobject thiz,jobject httpServiceBinderObj,jstring path,jobjectArray keys,jobjectArray values)223 android_media_MediaPlayer_setDataSourceAndHeaders(
224         JNIEnv *env, jobject thiz, jobject httpServiceBinderObj, jstring path,
225         jobjectArray keys, jobjectArray values) {
226 
227     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
228     if (mp == NULL ) {
229         jniThrowException(env, "java/lang/IllegalStateException", NULL);
230         return;
231     }
232 
233     if (path == NULL) {
234         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
235         return;
236     }
237 
238     const char *tmp = env->GetStringUTFChars(path, NULL);
239     if (tmp == NULL) {  // Out of memory
240         return;
241     }
242     ALOGV("setDataSource: path %s", tmp);
243 
244     String8 pathStr(tmp);
245     env->ReleaseStringUTFChars(path, tmp);
246     tmp = NULL;
247 
248     // We build a KeyedVector out of the key and val arrays
249     KeyedVector<String8, String8> headersVector;
250     if (!ConvertKeyValueArraysToKeyedVector(
251             env, keys, values, &headersVector)) {
252         return;
253     }
254 
255     sp<IMediaHTTPService> httpService;
256     if (httpServiceBinderObj != NULL) {
257         sp<IBinder> binder = ibinderForJavaObject(env, httpServiceBinderObj);
258         httpService = interface_cast<IMediaHTTPService>(binder);
259     }
260 
261     status_t opStatus =
262         mp->setDataSource(
263                 httpService,
264                 pathStr,
265                 headersVector.size() > 0? &headersVector : NULL);
266 
267     process_media_player_call(
268             env, thiz, opStatus, "java/io/IOException",
269             "setDataSource failed." );
270 }
271 
272 static void
android_media_MediaPlayer_setDataSourceFD(JNIEnv * env,jobject thiz,jobject fileDescriptor,jlong offset,jlong length)273 android_media_MediaPlayer_setDataSourceFD(JNIEnv *env, jobject thiz, jobject fileDescriptor, jlong offset, jlong length)
274 {
275     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
276     if (mp == NULL ) {
277         jniThrowException(env, "java/lang/IllegalStateException", NULL);
278         return;
279     }
280 
281     if (fileDescriptor == NULL) {
282         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
283         return;
284     }
285     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
286     ALOGV("setDataSourceFD: fd %d", fd);
287     process_media_player_call( env, thiz, mp->setDataSource(fd, offset, length), "java/io/IOException", "setDataSourceFD failed." );
288 }
289 
290 static void
android_media_MediaPlayer_setDataSourceCallback(JNIEnv * env,jobject thiz,jobject dataSource)291 android_media_MediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject dataSource)
292 {
293     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
294     if (mp == NULL ) {
295         jniThrowException(env, "java/lang/IllegalStateException", NULL);
296         return;
297     }
298 
299     if (dataSource == NULL) {
300         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
301         return;
302     }
303     sp<IDataSource> callbackDataSource = new JMediaDataSource(env, dataSource);
304     process_media_player_call(env, thiz, mp->setDataSource(callbackDataSource), "java/lang/RuntimeException", "setDataSourceCallback failed." );
305 }
306 
307 static sp<IGraphicBufferProducer>
getVideoSurfaceTexture(JNIEnv * env,jobject thiz)308 getVideoSurfaceTexture(JNIEnv* env, jobject thiz) {
309     IGraphicBufferProducer * const p = (IGraphicBufferProducer*)env->GetLongField(thiz, fields.surface_texture);
310     return sp<IGraphicBufferProducer>(p);
311 }
312 
313 static void
decVideoSurfaceRef(JNIEnv * env,jobject thiz)314 decVideoSurfaceRef(JNIEnv *env, jobject thiz)
315 {
316     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
317     if (mp == NULL) {
318         return;
319     }
320 
321     sp<IGraphicBufferProducer> old_st = getVideoSurfaceTexture(env, thiz);
322     if (old_st != NULL) {
323         old_st->decStrong((void*)decVideoSurfaceRef);
324     }
325 }
326 
327 static void
setVideoSurface(JNIEnv * env,jobject thiz,jobject jsurface,jboolean mediaPlayerMustBeAlive)328 setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
329 {
330     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
331     if (mp == NULL) {
332         if (mediaPlayerMustBeAlive) {
333             jniThrowException(env, "java/lang/IllegalStateException", NULL);
334         }
335         return;
336     }
337 
338     decVideoSurfaceRef(env, thiz);
339 
340     sp<IGraphicBufferProducer> new_st;
341     if (jsurface) {
342         sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
343         if (surface != NULL) {
344             new_st = surface->getIGraphicBufferProducer();
345             if (new_st == NULL) {
346                 jniThrowException(env, "java/lang/IllegalArgumentException",
347                     "The surface does not have a binding SurfaceTexture!");
348                 return;
349             }
350             new_st->incStrong((void*)decVideoSurfaceRef);
351         } else {
352             jniThrowException(env, "java/lang/IllegalArgumentException",
353                     "The surface has been released");
354             return;
355         }
356     }
357 
358     env->SetLongField(thiz, fields.surface_texture, (jlong)new_st.get());
359 
360     // This will fail if the media player has not been initialized yet. This
361     // can be the case if setDisplay() on MediaPlayer.java has been called
362     // before setDataSource(). The redundant call to setVideoSurfaceTexture()
363     // in prepare/prepareAsync covers for this case.
364     mp->setVideoSurfaceTexture(new_st);
365 }
366 
367 static void
android_media_MediaPlayer_setVideoSurface(JNIEnv * env,jobject thiz,jobject jsurface)368 android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
369 {
370     setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
371 }
372 
373 static jobject
android_media_MediaPlayer_getBufferingParams(JNIEnv * env,jobject thiz)374 android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
375 {
376     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
377     if (mp == NULL) {
378         jniThrowException(env, "java/lang/IllegalStateException", NULL);
379         return NULL;
380     }
381 
382     BufferingParams bp;
383     BufferingSettings &settings = bp.settings;
384     process_media_player_call(
385             env, thiz, mp->getBufferingSettings(&settings),
386             "java/lang/IllegalStateException", "unexpected error");
387     if (env->ExceptionCheck()) {
388         return nullptr;
389     }
390     ALOGV("getBufferingSettings:{%s}", settings.toString().string());
391 
392     return bp.asJobject(env, gBufferingParamsFields);
393 }
394 
395 static void
android_media_MediaPlayer_setBufferingParams(JNIEnv * env,jobject thiz,jobject params)396 android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
397 {
398     if (params == NULL) {
399         return;
400     }
401 
402     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
403     if (mp == NULL) {
404         jniThrowException(env, "java/lang/IllegalStateException", NULL);
405         return;
406     }
407 
408     BufferingParams bp;
409     bp.fillFromJobject(env, gBufferingParamsFields, params);
410     ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
411 
412     process_media_player_call(
413             env, thiz, mp->setBufferingSettings(bp.settings),
414             "java/lang/IllegalStateException", "unexpected error");
415 }
416 
417 static void
android_media_MediaPlayer_prepare(JNIEnv * env,jobject thiz)418 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
419 {
420     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
421     if (mp == NULL ) {
422         jniThrowException(env, "java/lang/IllegalStateException", NULL);
423         return;
424     }
425 
426     // Handle the case where the display surface was set before the mp was
427     // initialized. We try again to make it stick.
428     sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
429     mp->setVideoSurfaceTexture(st);
430 
431     process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
432 }
433 
434 static void
android_media_MediaPlayer_prepareAsync(JNIEnv * env,jobject thiz)435 android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
436 {
437     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
438     if (mp == NULL ) {
439         jniThrowException(env, "java/lang/IllegalStateException", NULL);
440         return;
441     }
442 
443     // Handle the case where the display surface was set before the mp was
444     // initialized. We try again to make it stick.
445     sp<IGraphicBufferProducer> st = getVideoSurfaceTexture(env, thiz);
446     mp->setVideoSurfaceTexture(st);
447 
448     process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
449 }
450 
451 static void
android_media_MediaPlayer_start(JNIEnv * env,jobject thiz)452 android_media_MediaPlayer_start(JNIEnv *env, jobject thiz)
453 {
454     ALOGV("start");
455     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
456     if (mp == NULL ) {
457         jniThrowException(env, "java/lang/IllegalStateException", NULL);
458         return;
459     }
460     process_media_player_call( env, thiz, mp->start(), NULL, NULL );
461 }
462 
463 static void
android_media_MediaPlayer_stop(JNIEnv * env,jobject thiz)464 android_media_MediaPlayer_stop(JNIEnv *env, jobject thiz)
465 {
466     ALOGV("stop");
467     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
468     if (mp == NULL ) {
469         jniThrowException(env, "java/lang/IllegalStateException", NULL);
470         return;
471     }
472     process_media_player_call( env, thiz, mp->stop(), NULL, NULL );
473 }
474 
475 static void
android_media_MediaPlayer_pause(JNIEnv * env,jobject thiz)476 android_media_MediaPlayer_pause(JNIEnv *env, jobject thiz)
477 {
478     ALOGV("pause");
479     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
480     if (mp == NULL ) {
481         jniThrowException(env, "java/lang/IllegalStateException", NULL);
482         return;
483     }
484     process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
485 }
486 
487 static jboolean
android_media_MediaPlayer_isPlaying(JNIEnv * env,jobject thiz)488 android_media_MediaPlayer_isPlaying(JNIEnv *env, jobject thiz)
489 {
490     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
491     if (mp == NULL ) {
492         jniThrowException(env, "java/lang/IllegalStateException", NULL);
493         return JNI_FALSE;
494     }
495     const jboolean is_playing = mp->isPlaying();
496 
497     ALOGV("isPlaying: %d", is_playing);
498     return is_playing;
499 }
500 
501 static void
android_media_MediaPlayer_setPlaybackParams(JNIEnv * env,jobject thiz,jobject params)502 android_media_MediaPlayer_setPlaybackParams(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     PlaybackParams pbp;
511     pbp.fillFromJobject(env, gPlaybackParamsFields, params);
512     ALOGV("setPlaybackParams: %d:%f %d:%f %d:%u %d:%u",
513             pbp.speedSet, pbp.audioRate.mSpeed,
514             pbp.pitchSet, pbp.audioRate.mPitch,
515             pbp.audioFallbackModeSet, pbp.audioRate.mFallbackMode,
516             pbp.audioStretchModeSet, pbp.audioRate.mStretchMode);
517 
518     AudioPlaybackRate rate;
519     status_t err = mp->getPlaybackSettings(&rate);
520     if (err == OK) {
521         bool updatedRate = false;
522         if (pbp.speedSet) {
523             rate.mSpeed = pbp.audioRate.mSpeed;
524             updatedRate = true;
525         }
526         if (pbp.pitchSet) {
527             rate.mPitch = pbp.audioRate.mPitch;
528             updatedRate = true;
529         }
530         if (pbp.audioFallbackModeSet) {
531             rate.mFallbackMode = pbp.audioRate.mFallbackMode;
532             updatedRate = true;
533         }
534         if (pbp.audioStretchModeSet) {
535             rate.mStretchMode = pbp.audioRate.mStretchMode;
536             updatedRate = true;
537         }
538         if (updatedRate) {
539             err = mp->setPlaybackSettings(rate);
540         }
541     }
542     process_media_player_call(
543             env, thiz, err,
544             "java/lang/IllegalStateException", "unexpected error");
545 }
546 
547 static jobject
android_media_MediaPlayer_getPlaybackParams(JNIEnv * env,jobject thiz)548 android_media_MediaPlayer_getPlaybackParams(JNIEnv *env, jobject thiz)
549 {
550     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
551     if (mp == NULL) {
552         jniThrowException(env, "java/lang/IllegalStateException", NULL);
553         return NULL;
554     }
555 
556     PlaybackParams pbp;
557     AudioPlaybackRate &audioRate = pbp.audioRate;
558     process_media_player_call(
559             env, thiz, mp->getPlaybackSettings(&audioRate),
560             "java/lang/IllegalStateException", "unexpected error");
561     if (env->ExceptionCheck()) {
562         return nullptr;
563     }
564     ALOGV("getPlaybackSettings: %f %f %d %d",
565             audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode);
566 
567     pbp.speedSet = true;
568     pbp.pitchSet = true;
569     pbp.audioFallbackModeSet = true;
570     pbp.audioStretchModeSet = true;
571 
572     return pbp.asJobject(env, gPlaybackParamsFields);
573 }
574 
575 static void
android_media_MediaPlayer_setSyncParams(JNIEnv * env,jobject thiz,jobject params)576 android_media_MediaPlayer_setSyncParams(JNIEnv *env, jobject thiz, jobject params)
577 {
578     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
579     if (mp == NULL) {
580         jniThrowException(env, "java/lang/IllegalStateException", NULL);
581         return;
582     }
583 
584     SyncParams scp;
585     scp.fillFromJobject(env, gSyncParamsFields, params);
586     ALOGV("setSyncParams: %d:%d %d:%d %d:%f %d:%f",
587           scp.syncSourceSet, scp.sync.mSource,
588           scp.audioAdjustModeSet, scp.sync.mAudioAdjustMode,
589           scp.toleranceSet, scp.sync.mTolerance,
590           scp.frameRateSet, scp.frameRate);
591 
592     AVSyncSettings avsync;
593     float videoFrameRate;
594     status_t err = mp->getSyncSettings(&avsync, &videoFrameRate);
595     if (err == OK) {
596         bool updatedSync = scp.frameRateSet;
597         if (scp.syncSourceSet) {
598             avsync.mSource = scp.sync.mSource;
599             updatedSync = true;
600         }
601         if (scp.audioAdjustModeSet) {
602             avsync.mAudioAdjustMode = scp.sync.mAudioAdjustMode;
603             updatedSync = true;
604         }
605         if (scp.toleranceSet) {
606             avsync.mTolerance = scp.sync.mTolerance;
607             updatedSync = true;
608         }
609         if (updatedSync) {
610             err = mp->setSyncSettings(avsync, scp.frameRateSet ? scp.frameRate : -1.f);
611         }
612     }
613     process_media_player_call(
614             env, thiz, err,
615             "java/lang/IllegalStateException", "unexpected error");
616 }
617 
618 static jobject
android_media_MediaPlayer_getSyncParams(JNIEnv * env,jobject thiz)619 android_media_MediaPlayer_getSyncParams(JNIEnv *env, jobject thiz)
620 {
621     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
622     if (mp == NULL) {
623         jniThrowException(env, "java/lang/IllegalStateException", NULL);
624         return NULL;
625     }
626 
627     SyncParams scp;
628     scp.frameRate = -1.f;
629     process_media_player_call(
630             env, thiz, mp->getSyncSettings(&scp.sync, &scp.frameRate),
631             "java/lang/IllegalStateException", "unexpected error");
632     if (env->ExceptionCheck()) {
633         return nullptr;
634     }
635 
636     ALOGV("getSyncSettings: %d %d %f %f",
637             scp.sync.mSource, scp.sync.mAudioAdjustMode, scp.sync.mTolerance, scp.frameRate);
638 
639     // sanity check params
640     if (scp.sync.mSource >= AVSYNC_SOURCE_MAX
641             || scp.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX
642             || scp.sync.mTolerance < 0.f
643             || scp.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) {
644         jniThrowException(env,  "java/lang/IllegalStateException", NULL);
645         return NULL;
646     }
647 
648     scp.syncSourceSet = true;
649     scp.audioAdjustModeSet = true;
650     scp.toleranceSet = true;
651     scp.frameRateSet = scp.frameRate >= 0.f;
652 
653     return scp.asJobject(env, gSyncParamsFields);
654 }
655 
656 static void
android_media_MediaPlayer_seekTo(JNIEnv * env,jobject thiz,jlong msec,jint mode)657 android_media_MediaPlayer_seekTo(JNIEnv *env, jobject thiz, jlong msec, jint mode)
658 {
659     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
660     if (mp == NULL ) {
661         jniThrowException(env, "java/lang/IllegalStateException", NULL);
662         return;
663     }
664     ALOGV("seekTo: %lld(msec), mode=%d", (long long)msec, mode);
665     process_media_player_call( env, thiz, mp->seekTo((int)msec, (MediaPlayerSeekMode)mode), NULL, NULL );
666 }
667 
668 static void
android_media_MediaPlayer_notifyAt(JNIEnv * env,jobject thiz,jlong mediaTimeUs)669 android_media_MediaPlayer_notifyAt(JNIEnv *env, jobject thiz, jlong mediaTimeUs)
670 {
671     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
672     if (mp == NULL) {
673         jniThrowException(env, "java/lang/IllegalStateException", NULL);
674         return;
675     }
676     ALOGV("notifyAt: %lld", (long long)mediaTimeUs);
677     process_media_player_call( env, thiz, mp->notifyAt((int64_t)mediaTimeUs), NULL, NULL );
678 }
679 
680 static jint
android_media_MediaPlayer_getVideoWidth(JNIEnv * env,jobject thiz)681 android_media_MediaPlayer_getVideoWidth(JNIEnv *env, jobject thiz)
682 {
683     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
684     if (mp == NULL ) {
685         jniThrowException(env, "java/lang/IllegalStateException", NULL);
686         return 0;
687     }
688     int w;
689     if (0 != mp->getVideoWidth(&w)) {
690         ALOGE("getVideoWidth failed");
691         w = 0;
692     }
693     ALOGV("getVideoWidth: %d", w);
694     return (jint) w;
695 }
696 
697 static jint
android_media_MediaPlayer_getVideoHeight(JNIEnv * env,jobject thiz)698 android_media_MediaPlayer_getVideoHeight(JNIEnv *env, jobject thiz)
699 {
700     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
701     if (mp == NULL ) {
702         jniThrowException(env, "java/lang/IllegalStateException", NULL);
703         return 0;
704     }
705     int h;
706     if (0 != mp->getVideoHeight(&h)) {
707         ALOGE("getVideoHeight failed");
708         h = 0;
709     }
710     ALOGV("getVideoHeight: %d", h);
711     return (jint) h;
712 }
713 
714 static jobject
android_media_MediaPlayer_native_getMetrics(JNIEnv * env,jobject thiz)715 android_media_MediaPlayer_native_getMetrics(JNIEnv *env, jobject thiz)
716 {
717     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
718     if (mp == NULL ) {
719         jniThrowException(env, "java/lang/IllegalStateException", NULL);
720         return 0;
721     }
722 
723     Parcel p;
724     int key = FOURCC('m','t','r','X');
725     status_t status = mp->getParameter(key, &p);
726     if (status != OK) {
727         ALOGD("getMetrics() failed: %d", status);
728         return (jobject) NULL;
729     }
730 
731     MediaAnalyticsItem *item = new MediaAnalyticsItem;
732     item->readFromParcel(p);
733     jobject mybundle = MediaMetricsJNI::writeMetricsToBundle(env, item, NULL);
734 
735     // housekeeping
736     delete item;
737     item = NULL;
738 
739     return mybundle;
740 }
741 
742 static jint
android_media_MediaPlayer_getCurrentPosition(JNIEnv * env,jobject thiz)743 android_media_MediaPlayer_getCurrentPosition(JNIEnv *env, jobject thiz)
744 {
745     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
746     if (mp == NULL ) {
747         jniThrowException(env, "java/lang/IllegalStateException", NULL);
748         return 0;
749     }
750     int msec;
751     process_media_player_call( env, thiz, mp->getCurrentPosition(&msec), NULL, NULL );
752     ALOGV("getCurrentPosition: %d (msec)", msec);
753     return (jint) msec;
754 }
755 
756 static jint
android_media_MediaPlayer_getDuration(JNIEnv * env,jobject thiz)757 android_media_MediaPlayer_getDuration(JNIEnv *env, jobject thiz)
758 {
759     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
760     if (mp == NULL ) {
761         jniThrowException(env, "java/lang/IllegalStateException", NULL);
762         return 0;
763     }
764     int msec;
765     process_media_player_call( env, thiz, mp->getDuration(&msec), NULL, NULL );
766     ALOGV("getDuration: %d (msec)", msec);
767     return (jint) msec;
768 }
769 
770 static void
android_media_MediaPlayer_reset(JNIEnv * env,jobject thiz)771 android_media_MediaPlayer_reset(JNIEnv *env, jobject thiz)
772 {
773     ALOGV("reset");
774     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
775     if (mp == NULL ) {
776         jniThrowException(env, "java/lang/IllegalStateException", NULL);
777         return;
778     }
779     process_media_player_call( env, thiz, mp->reset(), NULL, NULL );
780 }
781 
782 static void
android_media_MediaPlayer_setAudioStreamType(JNIEnv * env,jobject thiz,jint streamtype)783 android_media_MediaPlayer_setAudioStreamType(JNIEnv *env, jobject thiz, jint streamtype)
784 {
785     ALOGV("setAudioStreamType: %d", streamtype);
786     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
787     if (mp == NULL ) {
788         jniThrowException(env, "java/lang/IllegalStateException", NULL);
789         return;
790     }
791     process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
792 }
793 
794 static jint
android_media_MediaPlayer_getAudioStreamType(JNIEnv * env,jobject thiz)795 android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
796 {
797     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
798     if (mp == NULL ) {
799         jniThrowException(env, "java/lang/IllegalStateException", NULL);
800         return 0;
801     }
802     audio_stream_type_t streamtype;
803     process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
804     ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
805     return (jint) streamtype;
806 }
807 
808 static jboolean
android_media_MediaPlayer_setParameter(JNIEnv * env,jobject thiz,jint key,jobject java_request)809 android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
810 {
811     ALOGV("setParameter: key %d", key);
812     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
813     if (mp == NULL ) {
814         jniThrowException(env, "java/lang/IllegalStateException", NULL);
815         return false;
816     }
817 
818     Parcel *request = parcelForJavaObject(env, java_request);
819     status_t err = mp->setParameter(key, *request);
820     if (err == OK) {
821         return true;
822     } else {
823         return false;
824     }
825 }
826 
827 static void
android_media_MediaPlayer_setLooping(JNIEnv * env,jobject thiz,jboolean looping)828 android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
829 {
830     ALOGV("setLooping: %d", looping);
831     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
832     if (mp == NULL ) {
833         jniThrowException(env, "java/lang/IllegalStateException", NULL);
834         return;
835     }
836     process_media_player_call( env, thiz, mp->setLooping(looping), NULL, NULL );
837 }
838 
839 static jboolean
android_media_MediaPlayer_isLooping(JNIEnv * env,jobject thiz)840 android_media_MediaPlayer_isLooping(JNIEnv *env, jobject thiz)
841 {
842     ALOGV("isLooping");
843     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
844     if (mp == NULL ) {
845         jniThrowException(env, "java/lang/IllegalStateException", NULL);
846         return JNI_FALSE;
847     }
848     return mp->isLooping() ? JNI_TRUE : JNI_FALSE;
849 }
850 
851 static void
android_media_MediaPlayer_setVolume(JNIEnv * env,jobject thiz,jfloat leftVolume,jfloat rightVolume)852 android_media_MediaPlayer_setVolume(JNIEnv *env, jobject thiz, jfloat leftVolume, jfloat rightVolume)
853 {
854     ALOGV("setVolume: left %f  right %f", (float) leftVolume, (float) rightVolume);
855     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
856     if (mp == NULL ) {
857         jniThrowException(env, "java/lang/IllegalStateException", NULL);
858         return;
859     }
860     process_media_player_call( env, thiz, mp->setVolume((float) leftVolume, (float) rightVolume), NULL, NULL );
861 }
862 
863 // Sends the request and reply parcels to the media player via the
864 // binder interface.
865 static jint
android_media_MediaPlayer_invoke(JNIEnv * env,jobject thiz,jobject java_request,jobject java_reply)866 android_media_MediaPlayer_invoke(JNIEnv *env, jobject thiz,
867                                  jobject java_request, jobject java_reply)
868 {
869     sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
870     if (media_player == NULL ) {
871         jniThrowException(env, "java/lang/IllegalStateException", NULL);
872         return UNKNOWN_ERROR;
873     }
874 
875     Parcel *request = parcelForJavaObject(env, java_request);
876     Parcel *reply = parcelForJavaObject(env, java_reply);
877 
878     // Don't use process_media_player_call which use the async loop to
879     // report errors, instead returns the status.
880     return (jint) media_player->invoke(*request, reply);
881 }
882 
883 // Sends the new filter to the client.
884 static jint
android_media_MediaPlayer_setMetadataFilter(JNIEnv * env,jobject thiz,jobject request)885 android_media_MediaPlayer_setMetadataFilter(JNIEnv *env, jobject thiz, jobject request)
886 {
887     sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
888     if (media_player == NULL ) {
889         jniThrowException(env, "java/lang/IllegalStateException", NULL);
890         return UNKNOWN_ERROR;
891     }
892 
893     Parcel *filter = parcelForJavaObject(env, request);
894 
895     if (filter == NULL ) {
896         jniThrowException(env, "java/lang/RuntimeException", "Filter is null");
897         return UNKNOWN_ERROR;
898     }
899 
900     return (jint) media_player->setMetadataFilter(*filter);
901 }
902 
903 static jboolean
android_media_MediaPlayer_getMetadata(JNIEnv * env,jobject thiz,jboolean update_only,jboolean apply_filter,jobject reply)904 android_media_MediaPlayer_getMetadata(JNIEnv *env, jobject thiz, jboolean update_only,
905                                       jboolean apply_filter, jobject reply)
906 {
907     sp<MediaPlayer> media_player = getMediaPlayer(env, thiz);
908     if (media_player == NULL ) {
909         jniThrowException(env, "java/lang/IllegalStateException", NULL);
910         return JNI_FALSE;
911     }
912 
913     Parcel *metadata = parcelForJavaObject(env, reply);
914 
915     if (metadata == NULL ) {
916         jniThrowException(env, "java/lang/RuntimeException", "Reply parcel is null");
917         return JNI_FALSE;
918     }
919 
920     metadata->freeData();
921     // On return metadata is positioned at the beginning of the
922     // metadata. Note however that the parcel actually starts with the
923     // return code so you should not rewind the parcel using
924     // setDataPosition(0).
925     if (media_player->getMetadata(update_only, apply_filter, metadata) == OK) {
926         return JNI_TRUE;
927     } else {
928         return JNI_FALSE;
929     }
930 }
931 
932 // This function gets some field IDs, which in turn causes class initialization.
933 // It is called from a static block in MediaPlayer, which won't run until the
934 // first time an instance of this class is used.
935 static void
android_media_MediaPlayer_native_init(JNIEnv * env)936 android_media_MediaPlayer_native_init(JNIEnv *env)
937 {
938     jclass clazz;
939 
940     clazz = env->FindClass("android/media/MediaPlayer");
941     if (clazz == NULL) {
942         return;
943     }
944 
945     fields.context = env->GetFieldID(clazz, "mNativeContext", "J");
946     if (fields.context == NULL) {
947         return;
948     }
949 
950     fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative",
951                                                "(Ljava/lang/Object;IIILjava/lang/Object;)V");
952     if (fields.post_event == NULL) {
953         return;
954     }
955 
956     fields.surface_texture = env->GetFieldID(clazz, "mNativeSurfaceTexture", "J");
957     if (fields.surface_texture == NULL) {
958         return;
959     }
960 
961     env->DeleteLocalRef(clazz);
962 
963     clazz = env->FindClass("android/net/ProxyInfo");
964     if (clazz == NULL) {
965         return;
966     }
967 
968     fields.proxyConfigGetHost =
969         env->GetMethodID(clazz, "getHost", "()Ljava/lang/String;");
970 
971     fields.proxyConfigGetPort =
972         env->GetMethodID(clazz, "getPort", "()I");
973 
974     fields.proxyConfigGetExclusionList =
975         env->GetMethodID(clazz, "getExclusionListAsString", "()Ljava/lang/String;");
976 
977     env->DeleteLocalRef(clazz);
978 
979     gBufferingParamsFields.init(env);
980 
981     // Modular DRM
982     FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
983     if (clazz) {
984         GET_METHOD_ID(gStateExceptionFields.init, clazz, "<init>", "(ILjava/lang/String;)V");
985         gStateExceptionFields.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
986 
987         env->DeleteLocalRef(clazz);
988     } else {
989         ALOGE("JNI android_media_MediaPlayer_native_init couldn't "
990               "get clazz android/media/MediaDrm$MediaDrmStateException");
991     }
992 
993     gPlaybackParamsFields.init(env);
994     gSyncParamsFields.init(env);
995     gVolumeShaperFields.init(env);
996 }
997 
998 static void
android_media_MediaPlayer_native_setup(JNIEnv * env,jobject thiz,jobject weak_this)999 android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
1000 {
1001     ALOGV("native_setup");
1002     sp<MediaPlayer> mp = new MediaPlayer();
1003     if (mp == NULL) {
1004         jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
1005         return;
1006     }
1007 
1008     // create new listener and give it to MediaPlayer
1009     sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
1010     mp->setListener(listener);
1011 
1012     // Stow our new C++ MediaPlayer in an opaque field in the Java object.
1013     setMediaPlayer(env, thiz, mp);
1014 }
1015 
1016 static void
android_media_MediaPlayer_release(JNIEnv * env,jobject thiz)1017 android_media_MediaPlayer_release(JNIEnv *env, jobject thiz)
1018 {
1019     ALOGV("release");
1020     decVideoSurfaceRef(env, thiz);
1021     sp<MediaPlayer> mp = setMediaPlayer(env, thiz, 0);
1022     if (mp != NULL) {
1023         // this prevents native callbacks after the object is released
1024         mp->setListener(0);
1025         mp->disconnect();
1026     }
1027 }
1028 
1029 static void
android_media_MediaPlayer_native_finalize(JNIEnv * env,jobject thiz)1030 android_media_MediaPlayer_native_finalize(JNIEnv *env, jobject thiz)
1031 {
1032     ALOGV("native_finalize");
1033     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1034     if (mp != NULL) {
1035         ALOGW("MediaPlayer finalized without being released");
1036     }
1037     android_media_MediaPlayer_release(env, thiz);
1038 }
1039 
android_media_MediaPlayer_set_audio_session_id(JNIEnv * env,jobject thiz,jint sessionId)1040 static void android_media_MediaPlayer_set_audio_session_id(JNIEnv *env,  jobject thiz,
1041         jint sessionId) {
1042     ALOGV("set_session_id(): %d", sessionId);
1043     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1044     if (mp == NULL ) {
1045         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1046         return;
1047     }
1048     process_media_player_call( env, thiz, mp->setAudioSessionId((audio_session_t) sessionId), NULL,
1049             NULL);
1050 }
1051 
android_media_MediaPlayer_get_audio_session_id(JNIEnv * env,jobject thiz)1052 static jint android_media_MediaPlayer_get_audio_session_id(JNIEnv *env,  jobject thiz) {
1053     ALOGV("get_session_id()");
1054     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1055     if (mp == NULL ) {
1056         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1057         return 0;
1058     }
1059 
1060     return (jint) mp->getAudioSessionId();
1061 }
1062 
1063 static void
android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv * env,jobject thiz,jfloat level)1064 android_media_MediaPlayer_setAuxEffectSendLevel(JNIEnv *env, jobject thiz, jfloat level)
1065 {
1066     ALOGV("setAuxEffectSendLevel: level %f", level);
1067     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1068     if (mp == NULL ) {
1069         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1070         return;
1071     }
1072     process_media_player_call( env, thiz, mp->setAuxEffectSendLevel(level), NULL, NULL );
1073 }
1074 
android_media_MediaPlayer_attachAuxEffect(JNIEnv * env,jobject thiz,jint effectId)1075 static void android_media_MediaPlayer_attachAuxEffect(JNIEnv *env,  jobject thiz, jint effectId) {
1076     ALOGV("attachAuxEffect(): %d", effectId);
1077     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1078     if (mp == NULL ) {
1079         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1080         return;
1081     }
1082     process_media_player_call( env, thiz, mp->attachAuxEffect(effectId), NULL, NULL );
1083 }
1084 
1085 static jint
android_media_MediaPlayer_pullBatteryData(JNIEnv * env,jobject,jobject java_reply)1086 android_media_MediaPlayer_pullBatteryData(
1087         JNIEnv *env, jobject /* thiz */, jobject java_reply)
1088 {
1089     sp<IBinder> binder = defaultServiceManager()->getService(String16("media.player"));
1090     sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
1091     if (service.get() == NULL) {
1092         jniThrowException(env, "java/lang/RuntimeException", "cannot get MediaPlayerService");
1093         return UNKNOWN_ERROR;
1094     }
1095 
1096     Parcel *reply = parcelForJavaObject(env, java_reply);
1097 
1098     return (jint) service->pullBatteryData(reply);
1099 }
1100 
1101 static jint
android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv * env,jobject thiz,jstring addrString,jint port)1102 android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
1103                                                 jstring addrString, jint port) {
1104     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1105     if (mp == NULL ) {
1106         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1107         return INVALID_OPERATION;
1108     }
1109 
1110     const char *cAddrString = NULL;
1111 
1112     if (NULL != addrString) {
1113         cAddrString = env->GetStringUTFChars(addrString, NULL);
1114         if (cAddrString == NULL) {  // Out of memory
1115             return NO_MEMORY;
1116         }
1117     }
1118     ALOGV("setRetransmitEndpoint: %s:%d",
1119             cAddrString ? cAddrString : "(null)", port);
1120 
1121     status_t ret;
1122     if (cAddrString && (port > 0xFFFF)) {
1123         ret = BAD_VALUE;
1124     } else {
1125         ret = mp->setRetransmitEndpoint(cAddrString,
1126                 static_cast<uint16_t>(port));
1127     }
1128 
1129     if (NULL != addrString) {
1130         env->ReleaseStringUTFChars(addrString, cAddrString);
1131     }
1132 
1133     if (ret == INVALID_OPERATION ) {
1134         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1135     }
1136 
1137     return (jint) ret;
1138 }
1139 
1140 static void
android_media_MediaPlayer_setNextMediaPlayer(JNIEnv * env,jobject thiz,jobject java_player)1141 android_media_MediaPlayer_setNextMediaPlayer(JNIEnv *env, jobject thiz, jobject java_player)
1142 {
1143     ALOGV("setNextMediaPlayer");
1144     sp<MediaPlayer> thisplayer = getMediaPlayer(env, thiz);
1145     if (thisplayer == NULL) {
1146         jniThrowException(env, "java/lang/IllegalStateException", "This player not initialized");
1147         return;
1148     }
1149     sp<MediaPlayer> nextplayer = (java_player == NULL) ? NULL : getMediaPlayer(env, java_player);
1150     if (nextplayer == NULL && java_player != NULL) {
1151         jniThrowException(env, "java/lang/IllegalStateException", "That player not initialized");
1152         return;
1153     }
1154 
1155     if (nextplayer == thisplayer) {
1156         jniThrowException(env, "java/lang/IllegalArgumentException", "Next player can't be self");
1157         return;
1158     }
1159     // tie the two players together
1160     process_media_player_call(
1161             env, thiz, thisplayer->setNextMediaPlayer(nextplayer),
1162             "java/lang/IllegalArgumentException",
1163             "setNextMediaPlayer failed." );
1164     ;
1165 }
1166 
1167 // Pass through the arguments to the MediaServer player implementation.
android_media_MediaPlayer_applyVolumeShaper(JNIEnv * env,jobject thiz,jobject jconfig,jobject joperation)1168 static jint android_media_MediaPlayer_applyVolumeShaper(JNIEnv *env, jobject thiz,
1169         jobject jconfig, jobject joperation) {
1170     // NOTE: hard code here to prevent platform issues. Must match VolumeShaper.java
1171     const int VOLUME_SHAPER_INVALID_OPERATION = -38;
1172 
1173     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1174     if (mp == nullptr) {
1175         return (jint)VOLUME_SHAPER_INVALID_OPERATION;
1176     }
1177 
1178     sp<VolumeShaper::Configuration> configuration;
1179     sp<VolumeShaper::Operation> operation;
1180     if (jconfig != nullptr) {
1181         configuration = VolumeShaperHelper::convertJobjectToConfiguration(
1182                 env, gVolumeShaperFields, jconfig);
1183         ALOGV("applyVolumeShaper configuration: %s", configuration->toString().c_str());
1184     }
1185     if (joperation != nullptr) {
1186         operation = VolumeShaperHelper::convertJobjectToOperation(
1187                 env, gVolumeShaperFields, joperation);
1188         ALOGV("applyVolumeShaper operation: %s", operation->toString().c_str());
1189     }
1190     VolumeShaper::Status status = mp->applyVolumeShaper(configuration, operation);
1191     if (status == INVALID_OPERATION) {
1192         status = VOLUME_SHAPER_INVALID_OPERATION;
1193     }
1194     return (jint)status; // if status < 0 an error, else a VolumeShaper id
1195 }
1196 
1197 // Pass through the arguments to the MediaServer player implementation.
android_media_MediaPlayer_getVolumeShaperState(JNIEnv * env,jobject thiz,jint id)1198 static jobject android_media_MediaPlayer_getVolumeShaperState(JNIEnv *env, jobject thiz,
1199         jint id) {
1200     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1201     if (mp == nullptr) {
1202         return (jobject)nullptr;
1203     }
1204 
1205     sp<VolumeShaper::State> state = mp->getVolumeShaperState((int)id);
1206     if (state.get() == nullptr) {
1207         return (jobject)nullptr;
1208     }
1209     return VolumeShaperHelper::convertStateToJobject(env, gVolumeShaperFields, state);
1210 }
1211 
1212 /////////////////////////////////////////////////////////////////////////////////////
1213 // Modular DRM begin
1214 
1215 // TODO: investigate if these can be shared with their MediaDrm counterparts
throwDrmStateException(JNIEnv * env,const char * msg,status_t err)1216 static void throwDrmStateException(JNIEnv *env, const char *msg, status_t err)
1217 {
1218     ALOGE("Illegal DRM state exception: %s (%d)", msg, err);
1219 
1220     jobject exception = env->NewObject(gStateExceptionFields.classId,
1221             gStateExceptionFields.init, static_cast<int>(err),
1222             env->NewStringUTF(msg));
1223     env->Throw(static_cast<jthrowable>(exception));
1224 }
1225 
1226 // TODO: investigate if these can be shared with their MediaDrm counterparts
throwDrmExceptionAsNecessary(JNIEnv * env,status_t err,const char * msg=NULL)1227 static bool throwDrmExceptionAsNecessary(JNIEnv *env, status_t err, const char *msg = NULL)
1228 {
1229     const char *drmMessage = "Unknown DRM Msg";
1230 
1231     switch (err) {
1232     case ERROR_DRM_UNKNOWN:
1233         drmMessage = "General DRM error";
1234         break;
1235     case ERROR_DRM_NO_LICENSE:
1236         drmMessage = "No license";
1237         break;
1238     case ERROR_DRM_LICENSE_EXPIRED:
1239         drmMessage = "License expired";
1240         break;
1241     case ERROR_DRM_SESSION_NOT_OPENED:
1242         drmMessage = "Session not opened";
1243         break;
1244     case ERROR_DRM_DECRYPT_UNIT_NOT_INITIALIZED:
1245         drmMessage = "Not initialized";
1246         break;
1247     case ERROR_DRM_DECRYPT:
1248         drmMessage = "Decrypt error";
1249         break;
1250     case ERROR_DRM_CANNOT_HANDLE:
1251         drmMessage = "Unsupported scheme or data format";
1252         break;
1253     case ERROR_DRM_TAMPER_DETECTED:
1254         drmMessage = "Invalid state";
1255         break;
1256     default:
1257         break;
1258     }
1259 
1260     String8 vendorMessage;
1261     if (err >= ERROR_DRM_VENDOR_MIN && err <= ERROR_DRM_VENDOR_MAX) {
1262         vendorMessage = String8::format("DRM vendor-defined error: %d", err);
1263         drmMessage = vendorMessage.string();
1264     }
1265 
1266     if (err == BAD_VALUE) {
1267         jniThrowException(env, "java/lang/IllegalArgumentException", msg);
1268         return true;
1269     } else if (err == ERROR_DRM_NOT_PROVISIONED) {
1270         jniThrowException(env, "android/media/NotProvisionedException", msg);
1271         return true;
1272     } else if (err == ERROR_DRM_RESOURCE_BUSY) {
1273         jniThrowException(env, "android/media/ResourceBusyException", msg);
1274         return true;
1275     } else if (err == ERROR_DRM_DEVICE_REVOKED) {
1276         jniThrowException(env, "android/media/DeniedByServerException", msg);
1277         return true;
1278     } else if (err == DEAD_OBJECT) {
1279         jniThrowException(env, "android/media/MediaDrmResetException",
1280                           "mediaserver died");
1281         return true;
1282     } else if (err != OK) {
1283         String8 errbuf;
1284         if (drmMessage != NULL) {
1285             if (msg == NULL) {
1286                 msg = drmMessage;
1287             } else {
1288                 errbuf = String8::format("%s: %s", msg, drmMessage);
1289                 msg = errbuf.string();
1290             }
1291         }
1292         throwDrmStateException(env, msg, err);
1293         return true;
1294     }
1295     return false;
1296 }
1297 
JByteArrayToVector(JNIEnv * env,jbyteArray const & byteArray)1298 static Vector<uint8_t> JByteArrayToVector(JNIEnv *env, jbyteArray const &byteArray)
1299 {
1300     Vector<uint8_t> vector;
1301     size_t length = env->GetArrayLength(byteArray);
1302     vector.insertAt((size_t)0, length);
1303     env->GetByteArrayRegion(byteArray, 0, length, (jbyte *)vector.editArray());
1304     return vector;
1305 }
1306 
android_media_MediaPlayer_prepareDrm(JNIEnv * env,jobject thiz,jbyteArray uuidObj,jbyteArray drmSessionIdObj)1307 static void android_media_MediaPlayer_prepareDrm(JNIEnv *env, jobject thiz,
1308                     jbyteArray uuidObj, jbyteArray drmSessionIdObj)
1309 {
1310     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1311     if (mp == NULL) {
1312         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1313         return;
1314     }
1315 
1316     if (uuidObj == NULL) {
1317         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
1318         return;
1319     }
1320 
1321     Vector<uint8_t> uuid = JByteArrayToVector(env, uuidObj);
1322 
1323     if (uuid.size() != 16) {
1324         jniThrowException(
1325                           env,
1326                           "java/lang/IllegalArgumentException",
1327                           "invalid UUID size, expected 16 bytes");
1328         return;
1329     }
1330 
1331     Vector<uint8_t> drmSessionId = JByteArrayToVector(env, drmSessionIdObj);
1332 
1333     if (drmSessionId.size() == 0) {
1334         jniThrowException(
1335                           env,
1336                           "java/lang/IllegalArgumentException",
1337                           "empty drmSessionId");
1338         return;
1339     }
1340 
1341     status_t err = mp->prepareDrm(uuid.array(), drmSessionId);
1342     if (err != OK) {
1343         if (err == INVALID_OPERATION) {
1344             jniThrowException(
1345                               env,
1346                               "java/lang/IllegalStateException",
1347                               "The player must be in prepared state.");
1348         } else if (err == ERROR_DRM_CANNOT_HANDLE) {
1349             jniThrowException(
1350                               env,
1351                               "android/media/UnsupportedSchemeException",
1352                               "Failed to instantiate drm object.");
1353         } else {
1354             throwDrmExceptionAsNecessary(env, err, "Failed to prepare DRM scheme");
1355         }
1356     }
1357 }
1358 
android_media_MediaPlayer_releaseDrm(JNIEnv * env,jobject thiz)1359 static void android_media_MediaPlayer_releaseDrm(JNIEnv *env, jobject thiz)
1360 {
1361     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1362     if (mp == NULL ) {
1363         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1364         return;
1365     }
1366 
1367     status_t err = mp->releaseDrm();
1368     if (err != OK) {
1369         if (err == INVALID_OPERATION) {
1370             jniThrowException(
1371                               env,
1372                               "java/lang/IllegalStateException",
1373                               "Can not release DRM in an active player state.");
1374         }
1375     }
1376 }
1377 // Modular DRM end
1378 // ----------------------------------------------------------------------------
1379 
1380 /////////////////////////////////////////////////////////////////////////////////////
1381 // AudioRouting begin
android_media_MediaPlayer_setOutputDevice(JNIEnv * env,jobject thiz,jint device_id)1382 static jboolean android_media_MediaPlayer_setOutputDevice(JNIEnv *env, jobject thiz, jint device_id)
1383 {
1384     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1385     if (mp == NULL) {
1386         return false;
1387     }
1388     return mp->setOutputDevice(device_id) == NO_ERROR;
1389 }
1390 
android_media_MediaPlayer_getRoutedDeviceId(JNIEnv * env,jobject thiz)1391 static jint android_media_MediaPlayer_getRoutedDeviceId(JNIEnv *env, jobject thiz)
1392 {
1393     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1394     if (mp == NULL) {
1395         return AUDIO_PORT_HANDLE_NONE;
1396     }
1397     return mp->getRoutedDeviceId();
1398 }
1399 
android_media_MediaPlayer_enableDeviceCallback(JNIEnv * env,jobject thiz,jboolean enabled)1400 static void android_media_MediaPlayer_enableDeviceCallback(
1401         JNIEnv* env, jobject thiz, jboolean enabled)
1402 {
1403     sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
1404     if (mp == NULL) {
1405         return;
1406     }
1407 
1408     status_t status = mp->enableAudioDeviceCallback(enabled);
1409     if (status != NO_ERROR) {
1410         jniThrowException(env, "java/lang/IllegalStateException", NULL);
1411         ALOGE("enable device callback failed: %d", status);
1412     }
1413 }
1414 
1415 // AudioRouting end
1416 // ----------------------------------------------------------------------------
1417 
1418 static const JNINativeMethod gMethods[] = {
1419     {
1420         "nativeSetDataSource",
1421         "(Landroid/os/IBinder;Ljava/lang/String;[Ljava/lang/String;"
1422         "[Ljava/lang/String;)V",
1423         (void *)android_media_MediaPlayer_setDataSourceAndHeaders
1424     },
1425 
1426     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
1427     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
1428     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
1429     {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
1430     {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
1431     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
1432     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
1433     {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},
1434     {"_stop",               "()V",                              (void *)android_media_MediaPlayer_stop},
1435     {"getVideoWidth",       "()I",                              (void *)android_media_MediaPlayer_getVideoWidth},
1436     {"getVideoHeight",      "()I",                              (void *)android_media_MediaPlayer_getVideoHeight},
1437     {"native_getMetrics",   "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer_native_getMetrics},
1438     {"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
1439     {"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
1440     {"setSyncParams",     "(Landroid/media/SyncParams;)V",  (void *)android_media_MediaPlayer_setSyncParams},
1441     {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer_getSyncParams},
1442     {"_seekTo",             "(JI)V",                            (void *)android_media_MediaPlayer_seekTo},
1443     {"_notifyAt",           "(J)V",                             (void *)android_media_MediaPlayer_notifyAt},
1444     {"_pause",              "()V",                              (void *)android_media_MediaPlayer_pause},
1445     {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer_isPlaying},
1446     {"getCurrentPosition",  "()I",                              (void *)android_media_MediaPlayer_getCurrentPosition},
1447     {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
1448     {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
1449     {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
1450     {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
1451     {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},
1452     {"setParameter",        "(ILandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_setParameter},
1453     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
1454     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
1455     {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
1456     {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
1457     {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
1458     {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
1459     {"native_init",         "()V",                              (void *)android_media_MediaPlayer_native_init},
1460     {"native_setup",        "(Ljava/lang/Object;)V",            (void *)android_media_MediaPlayer_native_setup},
1461     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
1462     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
1463     {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
1464     {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
1465     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
1466     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
1467     {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
1468     {"setNextMediaPlayer",  "(Landroid/media/MediaPlayer;)V",   (void *)android_media_MediaPlayer_setNextMediaPlayer},
1469     {"native_applyVolumeShaper",
1470                             "(Landroid/media/VolumeShaper$Configuration;Landroid/media/VolumeShaper$Operation;)I",
1471                                                                 (void *)android_media_MediaPlayer_applyVolumeShaper},
1472     {"native_getVolumeShaperState",
1473                             "(I)Landroid/media/VolumeShaper$State;",
1474                                                                 (void *)android_media_MediaPlayer_getVolumeShaperState},
1475     // Modular DRM
1476     { "_prepareDrm", "([B[B)V",                                 (void *)android_media_MediaPlayer_prepareDrm },
1477     { "_releaseDrm", "()V",                                     (void *)android_media_MediaPlayer_releaseDrm },
1478 
1479     // AudioRouting
1480     {"native_setOutputDevice", "(I)Z",                          (void *)android_media_MediaPlayer_setOutputDevice},
1481     {"native_getRoutedDeviceId", "()I",                         (void *)android_media_MediaPlayer_getRoutedDeviceId},
1482     {"native_enableDeviceCallback", "(Z)V",                     (void *)android_media_MediaPlayer_enableDeviceCallback},
1483 };
1484 
1485 // This function only registers the native methods
register_android_media_MediaPlayer(JNIEnv * env)1486 static int register_android_media_MediaPlayer(JNIEnv *env)
1487 {
1488     return AndroidRuntime::registerNativeMethods(env,
1489                 "android/media/MediaPlayer", gMethods, NELEM(gMethods));
1490 }
1491 extern int register_android_media_ImageReader(JNIEnv *env);
1492 extern int register_android_media_ImageWriter(JNIEnv *env);
1493 extern int register_android_media_Crypto(JNIEnv *env);
1494 extern int register_android_media_Drm(JNIEnv *env);
1495 extern int register_android_media_Descrambler(JNIEnv *env);
1496 extern int register_android_media_MediaCodec(JNIEnv *env);
1497 extern int register_android_media_MediaExtractor(JNIEnv *env);
1498 extern int register_android_media_MediaCodecList(JNIEnv *env);
1499 extern int register_android_media_MediaHTTPConnection(JNIEnv *env);
1500 extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
1501 extern int register_android_media_MediaMuxer(JNIEnv *env);
1502 extern int register_android_media_MediaRecorder(JNIEnv *env);
1503 extern int register_android_media_MediaScanner(JNIEnv *env);
1504 extern int register_android_media_MediaSync(JNIEnv *env);
1505 extern int register_android_media_ResampleInputStream(JNIEnv *env);
1506 extern int register_android_media_MediaProfiles(JNIEnv *env);
1507 extern int register_android_mtp_MtpDatabase(JNIEnv *env);
1508 extern int register_android_mtp_MtpDevice(JNIEnv *env);
1509 extern int register_android_mtp_MtpServer(JNIEnv *env);
1510 
JNI_OnLoad(JavaVM * vm,void *)1511 jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
1512 {
1513     JNIEnv* env = NULL;
1514     jint result = -1;
1515 
1516     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
1517         ALOGE("ERROR: GetEnv failed\n");
1518         goto bail;
1519     }
1520     assert(env != NULL);
1521 
1522     if (register_android_media_ImageWriter(env) != JNI_OK) {
1523         ALOGE("ERROR: ImageWriter native registration failed");
1524         goto bail;
1525     }
1526 
1527     if (register_android_media_ImageReader(env) < 0) {
1528         ALOGE("ERROR: ImageReader native registration failed");
1529         goto bail;
1530     }
1531 
1532     if (register_android_media_MediaPlayer(env) < 0) {
1533         ALOGE("ERROR: MediaPlayer native registration failed\n");
1534         goto bail;
1535     }
1536 
1537     if (register_android_media_MediaRecorder(env) < 0) {
1538         ALOGE("ERROR: MediaRecorder native registration failed\n");
1539         goto bail;
1540     }
1541 
1542     if (register_android_media_MediaScanner(env) < 0) {
1543         ALOGE("ERROR: MediaScanner native registration failed\n");
1544         goto bail;
1545     }
1546 
1547     if (register_android_media_MediaMetadataRetriever(env) < 0) {
1548         ALOGE("ERROR: MediaMetadataRetriever native registration failed\n");
1549         goto bail;
1550     }
1551 
1552     if (register_android_media_ResampleInputStream(env) < 0) {
1553         ALOGE("ERROR: ResampleInputStream native registration failed\n");
1554         goto bail;
1555     }
1556 
1557     if (register_android_media_MediaProfiles(env) < 0) {
1558         ALOGE("ERROR: MediaProfiles native registration failed");
1559         goto bail;
1560     }
1561 
1562     if (register_android_mtp_MtpDatabase(env) < 0) {
1563         ALOGE("ERROR: MtpDatabase native registration failed");
1564         goto bail;
1565     }
1566 
1567     if (register_android_mtp_MtpDevice(env) < 0) {
1568         ALOGE("ERROR: MtpDevice native registration failed");
1569         goto bail;
1570     }
1571 
1572     if (register_android_mtp_MtpServer(env) < 0) {
1573         ALOGE("ERROR: MtpServer native registration failed");
1574         goto bail;
1575     }
1576 
1577     if (register_android_media_MediaCodec(env) < 0) {
1578         ALOGE("ERROR: MediaCodec native registration failed");
1579         goto bail;
1580     }
1581 
1582     if (register_android_media_MediaSync(env) < 0) {
1583         ALOGE("ERROR: MediaSync native registration failed");
1584         goto bail;
1585     }
1586 
1587     if (register_android_media_MediaExtractor(env) < 0) {
1588         ALOGE("ERROR: MediaCodec native registration failed");
1589         goto bail;
1590     }
1591 
1592     if (register_android_media_MediaMuxer(env) < 0) {
1593         ALOGE("ERROR: MediaMuxer native registration failed");
1594         goto bail;
1595     }
1596 
1597     if (register_android_media_MediaCodecList(env) < 0) {
1598         ALOGE("ERROR: MediaCodec native registration failed");
1599         goto bail;
1600     }
1601 
1602     if (register_android_media_Crypto(env) < 0) {
1603         ALOGE("ERROR: MediaCodec native registration failed");
1604         goto bail;
1605     }
1606 
1607     if (register_android_media_Drm(env) < 0) {
1608         ALOGE("ERROR: MediaDrm native registration failed");
1609         goto bail;
1610     }
1611 
1612     if (register_android_media_Descrambler(env) < 0) {
1613         ALOGE("ERROR: MediaDescrambler native registration failed");
1614         goto bail;
1615     }
1616 
1617     if (register_android_media_MediaHTTPConnection(env) < 0) {
1618         ALOGE("ERROR: MediaHTTPConnection native registration failed");
1619         goto bail;
1620     }
1621 
1622     /* success -- return valid version number */
1623     result = JNI_VERSION_1_4;
1624 
1625 bail:
1626     return result;
1627 }
1628 
1629 // KTHXBYE
1630