1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "JET_JNI"
19
20
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24
25 #include <jni.h>
26 #include <nativehelper/JNIPlatformHelp.h>
27 #include "core_jni_helpers.h"
28
29 #include <utils/Log.h>
30 #include "JetPlayer.h"
31
32
33 using namespace android;
34
35 // ----------------------------------------------------------------------------
36 static const char* const kClassPathName = "android/media/JetPlayer";
37
38 // ----------------------------------------------------------------------------
39 struct fields_t {
40 // these fields provide access from C++ to the...
41 jclass jetClass; // JetPlayer java class global ref
42 jmethodID postNativeEventInJava; // java method to post events to the Java thread from native
43 jfieldID nativePlayerInJavaObj; // stores in Java the native JetPlayer object
44 };
45
46 static fields_t javaJetPlayerFields {};
47
48 #define JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME "mNativePlayerInJavaObj"
49 #define JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME "postEventFromNative"
50
initializeJavaIDs(JNIEnv * env)51 static void initializeJavaIDs(JNIEnv* env) {
52 static std::once_flag sJniInitialized;
53
54 std::call_once(sJniInitialized, [](JNIEnv* env) {
55 // Get the JetPlayer java class
56 jclass jetPlayerClass = FindClassOrDie(env, kClassPathName);
57 javaJetPlayerFields.jetClass = MakeGlobalRefOrDie(env, jetPlayerClass);
58
59 // Get the mNativePlayerInJavaObj variable field
60 javaJetPlayerFields.nativePlayerInJavaObj =
61 GetFieldIDOrDie(env, jetPlayerClass, JAVA_NATIVEJETPLAYERINJAVAOBJ_FIELD_NAME, "J");
62
63 // Get the callback to post events from this native code to Java
64 javaJetPlayerFields.postNativeEventInJava = GetStaticMethodIDOrDie(env,
65 javaJetPlayerFields.jetClass, JAVA_NATIVEJETPOSTEVENT_CALLBACK_NAME,
66 "(Ljava/lang/Object;III)V");
67 }, env);
68 }
69
70 // ----------------------------------------------------------------------------
71 // ----------------------------------------------------------------------------
72
73
74 /*
75 * This function is called from JetPlayer instance's render thread
76 */
77 static void
jetPlayerEventCallback(int what,int arg1=0,int arg2=0,void * javaTarget=NULL)78 jetPlayerEventCallback(int what, int arg1=0, int arg2=0, void* javaTarget = NULL)
79 {
80 JNIEnv *env = AndroidRuntime::getJNIEnv();
81 if (env) {
82 env->CallStaticVoidMethod(
83 javaJetPlayerFields.jetClass, javaJetPlayerFields.postNativeEventInJava,
84 javaTarget,
85 what, arg1, arg2);
86 if (env->ExceptionCheck()) {
87 env->ExceptionDescribe();
88 env->ExceptionClear();
89 }
90 } else {
91 ALOGE("JET jetPlayerEventCallback(): No JNI env for JET event callback, can't post event.");
92 return;
93 }
94 }
95
96
97 // ----------------------------------------------------------------------------
98 // ----------------------------------------------------------------------------
99
100 static jboolean
android_media_JetPlayer_setup(JNIEnv * env,jobject thiz,jobject weak_this,jint maxTracks,jint trackBufferSize)101 android_media_JetPlayer_setup(JNIEnv *env, jobject thiz, jobject weak_this,
102 jint maxTracks, jint trackBufferSize)
103 {
104 initializeJavaIDs(env);
105
106 //ALOGV("android_media_JetPlayer_setup(): entering.");
107 JetPlayer* lpJet = new JetPlayer(env->NewGlobalRef(weak_this), maxTracks, trackBufferSize);
108
109 EAS_RESULT result = lpJet->init();
110
111 if (result==EAS_SUCCESS) {
112 // save our newly created C++ JetPlayer in the "nativePlayerInJavaObj" field
113 // of the Java object (in mNativePlayerInJavaObj)
114 env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, (jlong)lpJet);
115 return JNI_TRUE;
116 } else {
117 ALOGE("android_media_JetPlayer_setup(): initialization failed with EAS error code %d", (int)result);
118 delete lpJet;
119 env->SetLongField(weak_this, javaJetPlayerFields.nativePlayerInJavaObj, 0);
120 return JNI_FALSE;
121 }
122 }
123
124
125 // ----------------------------------------------------------------------------
126 static void
android_media_JetPlayer_finalize(JNIEnv * env,jobject thiz)127 android_media_JetPlayer_finalize(JNIEnv *env, jobject thiz)
128 {
129 ALOGV("android_media_JetPlayer_finalize(): entering.");
130 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
131 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
132 if (lpJet != NULL) {
133 lpJet->release();
134 delete lpJet;
135 }
136
137 ALOGV("android_media_JetPlayer_finalize(): exiting.");
138 }
139
140
141 // ----------------------------------------------------------------------------
142 static void
android_media_JetPlayer_release(JNIEnv * env,jobject thiz)143 android_media_JetPlayer_release(JNIEnv *env, jobject thiz)
144 {
145 android_media_JetPlayer_finalize(env, thiz);
146 env->SetLongField(thiz, javaJetPlayerFields.nativePlayerInJavaObj, 0);
147 ALOGV("android_media_JetPlayer_release() done");
148 }
149
150
151 // ----------------------------------------------------------------------------
152 static jboolean
android_media_JetPlayer_loadFromFile(JNIEnv * env,jobject thiz,jstring path)153 android_media_JetPlayer_loadFromFile(JNIEnv *env, jobject thiz, jstring path)
154 {
155 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
156 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
157 if (lpJet == NULL) {
158 jniThrowException(env, "java/lang/IllegalStateException",
159 "Unable to retrieve JetPlayer pointer for openFile()");
160 return JNI_FALSE;
161 }
162
163 // set up event callback function
164 lpJet->setEventCallback(jetPlayerEventCallback);
165
166 const char *pathStr = env->GetStringUTFChars(path, NULL);
167 if (pathStr == NULL) { // Out of memory
168 ALOGE("android_media_JetPlayer_openFile(): aborting, out of memory");
169 return JNI_FALSE;
170 }
171
172 ALOGV("android_media_JetPlayer_openFile(): trying to open %s", pathStr );
173 EAS_RESULT result = lpJet->loadFromFile(pathStr);
174 env->ReleaseStringUTFChars(path, pathStr);
175
176 if (result==EAS_SUCCESS) {
177 //ALOGV("android_media_JetPlayer_openFile(): file successfully opened");
178 return JNI_TRUE;
179 } else {
180 ALOGE("android_media_JetPlayer_openFile(): failed to open file with EAS error %d",
181 (int)result);
182 return JNI_FALSE;
183 }
184 }
185
186
187 // ----------------------------------------------------------------------------
188 static jboolean
android_media_JetPlayer_loadFromFileD(JNIEnv * env,jobject thiz,jobject fileDescriptor,jlong offset,jlong length)189 android_media_JetPlayer_loadFromFileD(JNIEnv *env, jobject thiz,
190 jobject fileDescriptor, jlong offset, jlong length)
191 {
192 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
193 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
194 if (lpJet == NULL) {
195 jniThrowException(env, "java/lang/IllegalStateException",
196 "Unable to retrieve JetPlayer pointer for openFile()");
197 return JNI_FALSE;
198 }
199
200 // set up event callback function
201 lpJet->setEventCallback(jetPlayerEventCallback);
202
203 ALOGV("android_media_JetPlayer_openFileDescr(): trying to load JET file through its fd" );
204 EAS_RESULT result = lpJet->loadFromFD(jniGetFDFromFileDescriptor(env, fileDescriptor),
205 (long long)offset, (long long)length); // cast params to types used by EAS_FILE
206
207 if (result==EAS_SUCCESS) {
208 ALOGV("android_media_JetPlayer_openFileDescr(): file successfully opened");
209 return JNI_TRUE;
210 } else {
211 ALOGE("android_media_JetPlayer_openFileDescr(): failed to open file with EAS error %d",
212 (int)result);
213 return JNI_FALSE;
214 }
215 }
216
217
218 // ----------------------------------------------------------------------------
219 static jboolean
android_media_JetPlayer_closeFile(JNIEnv * env,jobject thiz)220 android_media_JetPlayer_closeFile(JNIEnv *env, jobject thiz)
221 {
222 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
223 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
224 if (lpJet == NULL) {
225 jniThrowException(env, "java/lang/IllegalStateException",
226 "Unable to retrieve JetPlayer pointer for closeFile()");
227 return JNI_FALSE;
228 }
229
230 if (lpJet->closeFile()==EAS_SUCCESS) {
231 //ALOGV("android_media_JetPlayer_closeFile(): file successfully closed");
232 return JNI_TRUE;
233 } else {
234 ALOGE("android_media_JetPlayer_closeFile(): failed to close file");
235 return JNI_FALSE;
236 }
237 }
238
239
240 // ----------------------------------------------------------------------------
241 static jboolean
android_media_JetPlayer_play(JNIEnv * env,jobject thiz)242 android_media_JetPlayer_play(JNIEnv *env, jobject thiz)
243 {
244 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
245 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
246 if (lpJet == NULL) {
247 jniThrowException(env, "java/lang/IllegalStateException",
248 "Unable to retrieve JetPlayer pointer for play()");
249 return JNI_FALSE;
250 }
251
252 EAS_RESULT result = lpJet->play();
253 if (result==EAS_SUCCESS) {
254 //ALOGV("android_media_JetPlayer_play(): play successful");
255 return JNI_TRUE;
256 } else {
257 ALOGE("android_media_JetPlayer_play(): failed to play with EAS error code %ld",
258 result);
259 return JNI_FALSE;
260 }
261 }
262
263
264 // ----------------------------------------------------------------------------
265 static jboolean
android_media_JetPlayer_pause(JNIEnv * env,jobject thiz)266 android_media_JetPlayer_pause(JNIEnv *env, jobject thiz)
267 {
268 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
269 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
270 if (lpJet == NULL) {
271 jniThrowException(env, "java/lang/IllegalStateException",
272 "Unable to retrieve JetPlayer pointer for pause()");
273 return JNI_FALSE;
274 }
275
276 EAS_RESULT result = lpJet->pause();
277 if (result==EAS_SUCCESS) {
278 //ALOGV("android_media_JetPlayer_pause(): pause successful");
279 return JNI_TRUE;
280 } else {
281 if (result==EAS_ERROR_QUEUE_IS_EMPTY) {
282 ALOGV("android_media_JetPlayer_pause(): paused with an empty queue");
283 return JNI_TRUE;
284 } else
285 ALOGE("android_media_JetPlayer_pause(): failed to pause with EAS error code %ld",
286 result);
287 return JNI_FALSE;
288 }
289 }
290
291
292 // ----------------------------------------------------------------------------
293 static jboolean
android_media_JetPlayer_queueSegment(JNIEnv * env,jobject thiz,jint segmentNum,jint libNum,jint repeatCount,jint transpose,jint muteFlags,jbyte userID)294 android_media_JetPlayer_queueSegment(JNIEnv *env, jobject thiz,
295 jint segmentNum, jint libNum, jint repeatCount, jint transpose, jint muteFlags,
296 jbyte userID)
297 {
298 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
299 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
300 if (lpJet == NULL) {
301 jniThrowException(env, "java/lang/IllegalStateException",
302 "Unable to retrieve JetPlayer pointer for queueSegment()");
303 return JNI_FALSE;
304 }
305
306 EAS_RESULT result
307 = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteFlags, userID);
308 if (result==EAS_SUCCESS) {
309 //ALOGV("android_media_JetPlayer_queueSegment(): segment successfully queued");
310 return JNI_TRUE;
311 } else {
312 ALOGE("android_media_JetPlayer_queueSegment(): failed with EAS error code %ld",
313 result);
314 return JNI_FALSE;
315 }
316 }
317
318
319 // ----------------------------------------------------------------------------
320 static jboolean
android_media_JetPlayer_queueSegmentMuteArray(JNIEnv * env,jobject thiz,jint segmentNum,jint libNum,jint repeatCount,jint transpose,jbooleanArray muteArray,jbyte userID)321 android_media_JetPlayer_queueSegmentMuteArray(JNIEnv *env, jobject thiz,
322 jint segmentNum, jint libNum, jint repeatCount, jint transpose, jbooleanArray muteArray,
323 jbyte userID)
324 {
325 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
326 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
327 if (lpJet == NULL) {
328 jniThrowException(env, "java/lang/IllegalStateException",
329 "Unable to retrieve JetPlayer pointer for queueSegmentMuteArray()");
330 return JNI_FALSE;
331 }
332
333 EAS_RESULT result=EAS_FAILURE;
334
335 jboolean *muteTracks = NULL;
336 muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
337 if (muteTracks == NULL) {
338 ALOGE("android_media_JetPlayer_queueSegment(): failed to read track mute mask.");
339 return JNI_FALSE;
340 }
341
342 EAS_U32 muteMask=0;
343 int maxTracks = lpJet->getMaxTracks();
344 for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
345 if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
346 muteMask = (muteMask << 1) | 0x00000001;
347 else
348 muteMask = muteMask << 1;
349 }
350 //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): FINAL mute mask =0x%08lX", mask);
351
352 result = lpJet->queueSegment(segmentNum, libNum, repeatCount, transpose, muteMask, userID);
353
354 env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
355 if (result==EAS_SUCCESS) {
356 //ALOGV("android_media_JetPlayer_queueSegmentMuteArray(): segment successfully queued");
357 return JNI_TRUE;
358 } else {
359 ALOGE("android_media_JetPlayer_queueSegmentMuteArray(): failed with EAS error code %ld",
360 result);
361 return JNI_FALSE;
362 }
363 }
364
365
366 // ----------------------------------------------------------------------------
367 static jboolean
android_media_JetPlayer_setMuteFlags(JNIEnv * env,jobject thiz,jint muteFlags,jboolean bSync)368 android_media_JetPlayer_setMuteFlags(JNIEnv *env, jobject thiz,
369 jint muteFlags /*unsigned?*/, jboolean bSync)
370 {
371 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
372 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
373 if (lpJet == NULL) {
374 jniThrowException(env, "java/lang/IllegalStateException",
375 "Unable to retrieve JetPlayer pointer for setMuteFlags()");
376 return JNI_FALSE;
377 }
378
379 EAS_RESULT result;
380 result = lpJet->setMuteFlags(muteFlags, bSync==JNI_TRUE ? true : false);
381 if (result==EAS_SUCCESS) {
382 //ALOGV("android_media_JetPlayer_setMuteFlags(): mute flags successfully updated");
383 return JNI_TRUE;
384 } else {
385 ALOGE("android_media_JetPlayer_setMuteFlags(): failed with EAS error code %ld", result);
386 return JNI_FALSE;
387 }
388 }
389
390
391 // ----------------------------------------------------------------------------
392 static jboolean
android_media_JetPlayer_setMuteArray(JNIEnv * env,jobject thiz,jbooleanArray muteArray,jboolean bSync)393 android_media_JetPlayer_setMuteArray(JNIEnv *env, jobject thiz,
394 jbooleanArray muteArray, jboolean bSync)
395 {
396 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
397 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
398 if (lpJet == NULL) {
399 jniThrowException(env, "java/lang/IllegalStateException",
400 "Unable to retrieve JetPlayer pointer for setMuteArray()");
401 return JNI_FALSE;
402 }
403
404 EAS_RESULT result=EAS_FAILURE;
405
406 jboolean *muteTracks = NULL;
407 muteTracks = env->GetBooleanArrayElements(muteArray, NULL);
408 if (muteTracks == NULL) {
409 ALOGE("android_media_JetPlayer_setMuteArray(): failed to read track mute mask.");
410 return JNI_FALSE;
411 }
412
413 EAS_U32 muteMask=0;
414 int maxTracks = lpJet->getMaxTracks();
415 for (jint trackIndex=0; trackIndex<maxTracks; trackIndex++) {
416 if (muteTracks[maxTracks-1-trackIndex]==JNI_TRUE)
417 muteMask = (muteMask << 1) | 0x00000001;
418 else
419 muteMask = muteMask << 1;
420 }
421 //ALOGV("android_media_JetPlayer_setMuteArray(): FINAL mute mask =0x%08lX", muteMask);
422
423 result = lpJet->setMuteFlags(muteMask, bSync==JNI_TRUE ? true : false);
424
425 env->ReleaseBooleanArrayElements(muteArray, muteTracks, 0);
426 if (result==EAS_SUCCESS) {
427 //ALOGV("android_media_JetPlayer_setMuteArray(): mute flags successfully updated");
428 return JNI_TRUE;
429 } else {
430 ALOGE("android_media_JetPlayer_setMuteArray(): \
431 failed to update mute flags with EAS error code %ld", result);
432 return JNI_FALSE;
433 }
434 }
435
436
437 // ----------------------------------------------------------------------------
438 static jboolean
android_media_JetPlayer_setMuteFlag(JNIEnv * env,jobject thiz,jint trackId,jboolean muteFlag,jboolean bSync)439 android_media_JetPlayer_setMuteFlag(JNIEnv *env, jobject thiz,
440 jint trackId, jboolean muteFlag, jboolean bSync)
441 {
442 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
443 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
444 if (lpJet == NULL) {
445 jniThrowException(env, "java/lang/IllegalStateException",
446 "Unable to retrieve JetPlayer pointer for setMuteFlag()");
447 return JNI_FALSE;
448 }
449
450 EAS_RESULT result;
451 result = lpJet->setMuteFlag(trackId,
452 muteFlag==JNI_TRUE ? true : false, bSync==JNI_TRUE ? true : false);
453 if (result==EAS_SUCCESS) {
454 //ALOGV("android_media_JetPlayer_setMuteFlag(): mute flag successfully updated for track %d", trackId);
455 return JNI_TRUE;
456 } else {
457 ALOGE("android_media_JetPlayer_setMuteFlag(): failed to update mute flag for track %d with EAS error code %ld",
458 trackId, result);
459 return JNI_FALSE;
460 }
461 }
462
463
464 // ----------------------------------------------------------------------------
465 static jboolean
android_media_JetPlayer_triggerClip(JNIEnv * env,jobject thiz,jint clipId)466 android_media_JetPlayer_triggerClip(JNIEnv *env, jobject thiz, jint clipId)
467 {
468 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
469 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
470 if (lpJet == NULL) {
471 jniThrowException(env, "java/lang/IllegalStateException",
472 "Unable to retrieve JetPlayer pointer for triggerClip()");
473 return JNI_FALSE;
474 }
475
476 EAS_RESULT result;
477 result = lpJet->triggerClip(clipId);
478 if (result==EAS_SUCCESS) {
479 //ALOGV("android_media_JetPlayer_triggerClip(): triggerClip successful for clip %d", clipId);
480 return JNI_TRUE;
481 } else {
482 ALOGE("android_media_JetPlayer_triggerClip(): triggerClip for clip %d failed with EAS error code %ld",
483 clipId, result);
484 return JNI_FALSE;
485 }
486 }
487
488
489 // ----------------------------------------------------------------------------
490 static jboolean
android_media_JetPlayer_clearQueue(JNIEnv * env,jobject thiz)491 android_media_JetPlayer_clearQueue(JNIEnv *env, jobject thiz)
492 {
493 JetPlayer *lpJet = (JetPlayer *)env->GetLongField(
494 thiz, javaJetPlayerFields.nativePlayerInJavaObj);
495 if (lpJet == NULL) {
496 jniThrowException(env, "java/lang/IllegalStateException",
497 "Unable to retrieve JetPlayer pointer for clearQueue()");
498 return JNI_FALSE;
499 }
500
501 EAS_RESULT result = lpJet->clearQueue();
502 if (result==EAS_SUCCESS) {
503 //ALOGV("android_media_JetPlayer_clearQueue(): clearQueue successful");
504 return JNI_TRUE;
505 } else {
506 ALOGE("android_media_JetPlayer_clearQueue(): clearQueue failed with EAS error code %ld",
507 result);
508 return JNI_FALSE;
509 }
510 }
511
512
513 // ----------------------------------------------------------------------------
514 // ----------------------------------------------------------------------------
515 static const JNINativeMethod gMethods[] = {
516 // name, signature, funcPtr
517 {"native_setup", "(Ljava/lang/Object;II)Z", (void *)android_media_JetPlayer_setup},
518 {"native_finalize", "()V", (void *)android_media_JetPlayer_finalize},
519 {"native_release", "()V", (void *)android_media_JetPlayer_release},
520 {"native_loadJetFromFile",
521 "(Ljava/lang/String;)Z", (void *)android_media_JetPlayer_loadFromFile},
522 {"native_loadJetFromFileD", "(Ljava/io/FileDescriptor;JJ)Z",
523 (void *)android_media_JetPlayer_loadFromFileD},
524 {"native_closeJetFile","()Z", (void *)android_media_JetPlayer_closeFile},
525 {"native_playJet", "()Z", (void *)android_media_JetPlayer_play},
526 {"native_pauseJet", "()Z", (void *)android_media_JetPlayer_pause},
527 {"native_queueJetSegment",
528 "(IIIIIB)Z", (void *)android_media_JetPlayer_queueSegment},
529 {"native_queueJetSegmentMuteArray",
530 "(IIII[ZB)Z", (void *)android_media_JetPlayer_queueSegmentMuteArray},
531 {"native_setMuteFlags","(IZ)Z", (void *)android_media_JetPlayer_setMuteFlags},
532 {"native_setMuteArray","([ZZ)Z", (void *)android_media_JetPlayer_setMuteArray},
533 {"native_setMuteFlag", "(IZZ)Z", (void *)android_media_JetPlayer_setMuteFlag},
534 {"native_triggerClip", "(I)Z", (void *)android_media_JetPlayer_triggerClip},
535 {"native_clearQueue", "()Z", (void *)android_media_JetPlayer_clearQueue},
536 };
537
538
539
register_android_media_JetPlayer(JNIEnv * env)540 int register_android_media_JetPlayer(JNIEnv *env)
541 {
542 return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
543 }
544