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