1/* 2** 3** Copyright 2006, 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#include <nativehelper/JNIHelp.h> 19#include <android_runtime/AndroidRuntime.h> 20#include <android_runtime/android_view_Surface.h> 21#include <android_runtime/android_graphics_SurfaceTexture.h> 22#include <utils/misc.h> 23 24 25#include <EGL/egl.h> 26#include <GLES/gl.h> 27#include <private/EGL/display.h> 28 29#include <gui/Surface.h> 30#include <gui/GLConsumer.h> 31#include <gui/Surface.h> 32 33#include <GraphicsJNI.h> 34#include <SkBitmap.h> 35#include <SkPixelRef.h> 36 37#include <ui/ANativeObjectBase.h> 38 39namespace android { 40 41static jclass gConfig_class; 42 43static jmethodID gConfig_ctorID; 44 45static jfieldID gDisplay_EGLDisplayFieldID; 46static jfieldID gContext_EGLContextFieldID; 47static jfieldID gSurface_EGLSurfaceFieldID; 48static jfieldID gConfig_EGLConfigFieldID; 49 50static inline EGLDisplay getDisplay(JNIEnv* env, jobject o) { 51 if (!o) return EGL_NO_DISPLAY; 52 return (EGLDisplay)env->GetLongField(o, gDisplay_EGLDisplayFieldID); 53} 54static inline EGLSurface getSurface(JNIEnv* env, jobject o) { 55 if (!o) return EGL_NO_SURFACE; 56 return (EGLSurface)env->GetLongField(o, gSurface_EGLSurfaceFieldID); 57} 58static inline EGLContext getContext(JNIEnv* env, jobject o) { 59 if (!o) return EGL_NO_CONTEXT; 60 return (EGLContext)env->GetLongField(o, gContext_EGLContextFieldID); 61} 62static inline EGLConfig getConfig(JNIEnv* env, jobject o) { 63 if (!o) return 0; 64 return (EGLConfig)env->GetLongField(o, gConfig_EGLConfigFieldID); 65} 66 67static inline jboolean EglBoolToJBool(EGLBoolean eglBool) { 68 return eglBool == EGL_TRUE ? JNI_TRUE : JNI_FALSE; 69} 70 71static void nativeClassInit(JNIEnv *_env, jclass eglImplClass) 72{ 73 jclass config_class = _env->FindClass("com/google/android/gles_jni/EGLConfigImpl"); 74 gConfig_class = (jclass) _env->NewGlobalRef(config_class); 75 gConfig_ctorID = _env->GetMethodID(gConfig_class, "<init>", "(J)V"); 76 gConfig_EGLConfigFieldID = _env->GetFieldID(gConfig_class, "mEGLConfig", "J"); 77 78 jclass display_class = _env->FindClass("com/google/android/gles_jni/EGLDisplayImpl"); 79 gDisplay_EGLDisplayFieldID = _env->GetFieldID(display_class, "mEGLDisplay", "J"); 80 81 jclass context_class = _env->FindClass("com/google/android/gles_jni/EGLContextImpl"); 82 gContext_EGLContextFieldID = _env->GetFieldID(context_class, "mEGLContext", "J"); 83 84 jclass surface_class = _env->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"); 85 gSurface_EGLSurfaceFieldID = _env->GetFieldID(surface_class, "mEGLSurface", "J"); 86} 87 88static const jint gNull_attrib_base[] = {EGL_NONE}; 89 90static bool validAttribList(JNIEnv *_env, jintArray attrib_list) { 91 if (attrib_list == NULL) { 92 return true; 93 } 94 jsize len = _env->GetArrayLength(attrib_list); 95 if (len < 1) { 96 return false; 97 } 98 jint item = 0; 99 _env->GetIntArrayRegion(attrib_list, len-1, 1, &item); 100 return item == EGL_NONE; 101} 102 103static jint* beginNativeAttribList(JNIEnv *_env, jintArray attrib_list) { 104 if (attrib_list != NULL) { 105 return _env->GetIntArrayElements(attrib_list, (jboolean *)0); 106 } else { 107 return(jint*) gNull_attrib_base; 108 } 109} 110 111static void endNativeAttributeList(JNIEnv *_env, jintArray attrib_list, jint* attrib_base) { 112 if (attrib_list != NULL) { 113 _env->ReleaseIntArrayElements(attrib_list, attrib_base, 0); 114 } 115} 116 117static jboolean jni_eglInitialize(JNIEnv *_env, jobject _this, jobject display, 118 jintArray major_minor) { 119 if (display == NULL || (major_minor != NULL && 120 _env->GetArrayLength(major_minor) < 2)) { 121 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 122 return JNI_FALSE; 123 } 124 125 EGLDisplay dpy = getDisplay(_env, display); 126 EGLBoolean success = eglInitialize(dpy, NULL, NULL); 127 if (success && major_minor) { 128 int len = _env->GetArrayLength(major_minor); 129 if (len) { 130 // we're exposing only EGL 1.0 131 jint* base = (jint *)_env->GetPrimitiveArrayCritical(major_minor, (jboolean *)0); 132 if (len >= 1) base[0] = 1; 133 if (len >= 2) base[1] = 0; 134 _env->ReleasePrimitiveArrayCritical(major_minor, base, 0); 135 } 136 } 137 return EglBoolToJBool(success); 138} 139 140static jboolean jni_eglQueryContext(JNIEnv *_env, jobject _this, jobject display, 141 jobject context, jint attribute, jintArray value) { 142 if (display == NULL || context == NULL || value == NULL 143 || _env->GetArrayLength(value) < 1) { 144 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 145 return JNI_FALSE; 146 } 147 EGLDisplay dpy = getDisplay(_env, display); 148 EGLContext ctx = getContext(_env, context); 149 EGLBoolean success = EGL_FALSE; 150 int len = _env->GetArrayLength(value); 151 if (len) { 152 jint* base = _env->GetIntArrayElements(value, (jboolean *)0); 153 success = eglQueryContext(dpy, ctx, attribute, base); 154 _env->ReleaseIntArrayElements(value, base, 0); 155 } 156 return EglBoolToJBool(success); 157} 158 159static jboolean jni_eglQuerySurface(JNIEnv *_env, jobject _this, jobject display, 160 jobject surface, jint attribute, jintArray value) { 161 if (display == NULL || surface == NULL || value == NULL 162 || _env->GetArrayLength(value) < 1) { 163 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 164 return JNI_FALSE; 165 } 166 EGLDisplay dpy = getDisplay(_env, display); 167 EGLContext sur = getSurface(_env, surface); 168 169 EGLBoolean success = EGL_FALSE; 170 int len = _env->GetArrayLength(value); 171 if (len) { 172 jint* base = _env->GetIntArrayElements(value, (jboolean *)0); 173 success = eglQuerySurface(dpy, sur, attribute, base); 174 _env->ReleaseIntArrayElements(value, base, 0); 175 } 176 return EglBoolToJBool(success); 177} 178 179static jint jni_getInitCount(JNIEnv *_env, jobject _clazz, jobject display) { 180 EGLDisplay dpy = getDisplay(_env, display); 181 return android::egl_get_init_count(dpy); 182} 183 184static jboolean jni_eglReleaseThread(JNIEnv *_env, jobject _this) { 185 return EglBoolToJBool(eglReleaseThread()); 186} 187 188static jboolean jni_eglChooseConfig(JNIEnv *_env, jobject _this, jobject display, 189 jintArray attrib_list, jobjectArray configs, jint config_size, jintArray num_config) { 190 if (display == NULL 191 || !validAttribList(_env, attrib_list) 192 || (configs != NULL && _env->GetArrayLength(configs) < config_size) 193 || (num_config != NULL && _env->GetArrayLength(num_config) < 1)) { 194 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 195 return JNI_FALSE; 196 } 197 EGLDisplay dpy = getDisplay(_env, display); 198 EGLBoolean success = EGL_FALSE; 199 200 if (configs == NULL) { 201 config_size = 0; 202 } 203 EGLConfig nativeConfigs[config_size]; 204 205 int num = 0; 206 jint* attrib_base = beginNativeAttribList(_env, attrib_list); 207 success = eglChooseConfig(dpy, attrib_base, configs ? nativeConfigs : 0, config_size, &num); 208 endNativeAttributeList(_env, attrib_list, attrib_base); 209 210 if (num_config != NULL) { 211 _env->SetIntArrayRegion(num_config, 0, 1, (jint*) &num); 212 } 213 214 if (success && configs!=NULL) { 215 for (int i=0 ; i<num ; i++) { 216 jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, reinterpret_cast<jlong>(nativeConfigs[i])); 217 _env->SetObjectArrayElement(configs, i, obj); 218 } 219 } 220 return EglBoolToJBool(success); 221} 222 223static jlong jni_eglCreateContext(JNIEnv *_env, jobject _this, jobject display, 224 jobject config, jobject share_context, jintArray attrib_list) { 225 if (display == NULL || config == NULL || share_context == NULL 226 || !validAttribList(_env, attrib_list)) { 227 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 228 return JNI_FALSE; 229 } 230 EGLDisplay dpy = getDisplay(_env, display); 231 EGLConfig cnf = getConfig(_env, config); 232 EGLContext shr = getContext(_env, share_context); 233 jint* base = beginNativeAttribList(_env, attrib_list); 234 EGLContext ctx = eglCreateContext(dpy, cnf, shr, base); 235 endNativeAttributeList(_env, attrib_list, base); 236 return reinterpret_cast<jlong>(ctx); 237} 238 239static jlong jni_eglCreatePbufferSurface(JNIEnv *_env, jobject _this, jobject display, 240 jobject config, jintArray attrib_list) { 241 if (display == NULL || config == NULL 242 || !validAttribList(_env, attrib_list)) { 243 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 244 return JNI_FALSE; 245 } 246 EGLDisplay dpy = getDisplay(_env, display); 247 EGLConfig cnf = getConfig(_env, config); 248 jint* base = beginNativeAttribList(_env, attrib_list); 249 EGLSurface sur = eglCreatePbufferSurface(dpy, cnf, base); 250 endNativeAttributeList(_env, attrib_list, base); 251 return reinterpret_cast<jlong>(sur); 252} 253 254static void jni_eglCreatePixmapSurface(JNIEnv *_env, jobject _this, jobject out_sur, 255 jobject display, jobject config, jobject native_pixmap, 256 jintArray attrib_list) 257{ 258 jniThrowException(_env, "java/lang/UnsupportedOperationException", "eglCreatePixmapSurface"); 259} 260 261static jlong jni_eglCreateWindowSurface(JNIEnv *_env, jobject _this, jobject display, 262 jobject config, jobject native_window, jintArray attrib_list) { 263 if (display == NULL || config == NULL 264 || !validAttribList(_env, attrib_list)) { 265 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 266 return JNI_FALSE; 267 } 268 EGLDisplay dpy = getDisplay(_env, display); 269 EGLContext cnf = getConfig(_env, config); 270 sp<ANativeWindow> window; 271 if (native_window == NULL) { 272not_valid_surface: 273 jniThrowException(_env, "java/lang/IllegalArgumentException", 274 "Make sure the SurfaceView or associated SurfaceHolder has a valid Surface"); 275 return 0; 276 } 277 278 window = android_view_Surface_getNativeWindow(_env, native_window); 279 if (window == NULL) 280 goto not_valid_surface; 281 282 jint* base = beginNativeAttribList(_env, attrib_list); 283 EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); 284 endNativeAttributeList(_env, attrib_list, base); 285 return reinterpret_cast<jlong>(sur); 286} 287 288static jlong jni_eglCreateWindowSurfaceTexture(JNIEnv *_env, jobject _this, jobject display, 289 jobject config, jobject native_window, jintArray attrib_list) { 290 if (display == NULL || config == NULL 291 || !validAttribList(_env, attrib_list)) { 292 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 293 return 0; 294 } 295 EGLDisplay dpy = getDisplay(_env, display); 296 EGLContext cnf = getConfig(_env, config); 297 sp<ANativeWindow> window; 298 if (native_window == 0) { 299not_valid_surface: 300 jniThrowException(_env, "java/lang/IllegalArgumentException", 301 "Make sure the SurfaceTexture is valid"); 302 return 0; 303 } 304 305 sp<IGraphicBufferProducer> producer(SurfaceTexture_getProducer(_env, native_window)); 306 window = new Surface(producer, true); 307 if (window == NULL) 308 goto not_valid_surface; 309 310 jint* base = beginNativeAttribList(_env, attrib_list); 311 EGLSurface sur = eglCreateWindowSurface(dpy, cnf, window.get(), base); 312 endNativeAttributeList(_env, attrib_list, base); 313 return reinterpret_cast<jlong>(sur); 314} 315 316static jboolean jni_eglGetConfigAttrib(JNIEnv *_env, jobject _this, jobject display, 317 jobject config, jint attribute, jintArray value) { 318 if (display == NULL || config == NULL 319 || (value == NULL || _env->GetArrayLength(value) < 1)) { 320 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 321 return JNI_FALSE; 322 } 323 EGLDisplay dpy = getDisplay(_env, display); 324 EGLContext cnf = getConfig(_env, config); 325 EGLBoolean success = EGL_FALSE; 326 jint localValue; 327 success = eglGetConfigAttrib(dpy, cnf, attribute, &localValue); 328 if (success) { 329 _env->SetIntArrayRegion(value, 0, 1, &localValue); 330 } 331 return EglBoolToJBool(success); 332} 333 334static jboolean jni_eglGetConfigs(JNIEnv *_env, jobject _this, jobject display, 335 jobjectArray configs, jint config_size, jintArray num_config) { 336 if (display == NULL || (configs != NULL && _env->GetArrayLength(configs) < config_size) 337 || (num_config != NULL && _env->GetArrayLength(num_config) < 1)) { 338 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 339 return JNI_FALSE; 340 } 341 EGLDisplay dpy = getDisplay(_env, display); 342 EGLBoolean success = EGL_FALSE; 343 if (configs == NULL) { 344 config_size = 0; 345 } 346 EGLConfig nativeConfigs[config_size]; 347 int num; 348 success = eglGetConfigs(dpy, configs ? nativeConfigs : 0, config_size, &num); 349 if (num_config != NULL) { 350 _env->SetIntArrayRegion(num_config, 0, 1, (jint*) &num); 351 } 352 if (success && configs) { 353 for (int i=0 ; i<num ; i++) { 354 jobject obj = _env->NewObject(gConfig_class, gConfig_ctorID, reinterpret_cast<jlong>(nativeConfigs[i])); 355 _env->SetObjectArrayElement(configs, i, obj); 356 } 357 } 358 return EglBoolToJBool(success); 359} 360 361static jint jni_eglGetError(JNIEnv *_env, jobject _this) { 362 EGLint error = eglGetError(); 363 return error; 364} 365 366static jlong jni_eglGetCurrentContext(JNIEnv *_env, jobject _this) { 367 return reinterpret_cast<jlong>(eglGetCurrentContext()); 368} 369 370static jlong jni_eglGetCurrentDisplay(JNIEnv *_env, jobject _this) { 371 return reinterpret_cast<jlong>(eglGetCurrentDisplay()); 372} 373 374static jlong jni_eglGetCurrentSurface(JNIEnv *_env, jobject _this, jint readdraw) { 375 if ((readdraw != EGL_READ) && (readdraw != EGL_DRAW)) { 376 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 377 return 0; 378 } 379 return reinterpret_cast<jlong>(eglGetCurrentSurface(readdraw)); 380} 381 382static jboolean jni_eglDestroyContext(JNIEnv *_env, jobject _this, jobject display, jobject context) { 383 if (display == NULL || context == NULL) { 384 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 385 return JNI_FALSE; 386 } 387 EGLDisplay dpy = getDisplay(_env, display); 388 EGLContext ctx = getContext(_env, context); 389 return EglBoolToJBool(eglDestroyContext(dpy, ctx)); 390} 391 392static jboolean jni_eglDestroySurface(JNIEnv *_env, jobject _this, jobject display, jobject surface) { 393 if (display == NULL || surface == NULL) { 394 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 395 return JNI_FALSE; 396 } 397 EGLDisplay dpy = getDisplay(_env, display); 398 EGLSurface sur = getSurface(_env, surface); 399 return EglBoolToJBool(eglDestroySurface(dpy, sur)); 400} 401 402static jlong jni_eglGetDisplay(JNIEnv *_env, jobject _this, jobject native_display) { 403 return reinterpret_cast<jlong>(eglGetDisplay(EGL_DEFAULT_DISPLAY)); 404} 405 406static jboolean jni_eglMakeCurrent(JNIEnv *_env, jobject _this, jobject display, jobject draw, jobject read, jobject context) { 407 if (display == NULL || draw == NULL || read == NULL || context == NULL) { 408 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 409 return JNI_FALSE; 410 } 411 EGLDisplay dpy = getDisplay(_env, display); 412 EGLSurface sdr = getSurface(_env, draw); 413 EGLSurface srd = getSurface(_env, read); 414 EGLContext ctx = getContext(_env, context); 415 return EglBoolToJBool(eglMakeCurrent(dpy, sdr, srd, ctx)); 416} 417 418static jstring jni_eglQueryString(JNIEnv *_env, jobject _this, jobject display, jint name) { 419 if (display == NULL) { 420 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 421 return NULL; 422 } 423 EGLDisplay dpy = getDisplay(_env, display); 424 const char* chars = eglQueryString(dpy, name); 425 return _env->NewStringUTF(chars); 426} 427 428static jboolean jni_eglSwapBuffers(JNIEnv *_env, jobject _this, jobject display, jobject surface) { 429 if (display == NULL || surface == NULL) { 430 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 431 return JNI_FALSE; 432 } 433 EGLDisplay dpy = getDisplay(_env, display); 434 EGLSurface sur = getSurface(_env, surface); 435 return EglBoolToJBool(eglSwapBuffers(dpy, sur)); 436} 437 438static jboolean jni_eglTerminate(JNIEnv *_env, jobject _this, jobject display) { 439 if (display == NULL) { 440 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 441 return JNI_FALSE; 442 } 443 EGLDisplay dpy = getDisplay(_env, display); 444 return EglBoolToJBool(eglTerminate(dpy)); 445} 446 447static jboolean jni_eglCopyBuffers(JNIEnv *_env, jobject _this, jobject display, 448 jobject surface, jobject native_pixmap) { 449 if (display == NULL || surface == NULL || native_pixmap == NULL) { 450 jniThrowException(_env, "java/lang/IllegalArgumentException", NULL); 451 return JNI_FALSE; 452 } 453 // TODO: Implement this 454 return JNI_FALSE; 455} 456 457static jboolean jni_eglWaitGL(JNIEnv *_env, jobject _this) { 458 return EglBoolToJBool(eglWaitGL()); 459} 460 461static jboolean jni_eglWaitNative(JNIEnv *_env, jobject _this, jint engine, jobject bindTarget) { 462 return EglBoolToJBool(eglWaitNative(engine)); 463} 464 465 466static const char *classPathName = "com/google/android/gles_jni/EGLImpl"; 467 468#define DISPLAY "Ljavax/microedition/khronos/egl/EGLDisplay;" 469#define CONTEXT "Ljavax/microedition/khronos/egl/EGLContext;" 470#define CONFIG "Ljavax/microedition/khronos/egl/EGLConfig;" 471#define SURFACE "Ljavax/microedition/khronos/egl/EGLSurface;" 472#define OBJECT "Ljava/lang/Object;" 473#define STRING "Ljava/lang/String;" 474 475static const JNINativeMethod methods[] = { 476{"_nativeClassInit","()V", (void*)nativeClassInit }, 477{"eglWaitGL", "()Z", (void*)jni_eglWaitGL }, 478{"eglInitialize", "(" DISPLAY "[I)Z", (void*)jni_eglInitialize }, 479{"eglQueryContext", "(" DISPLAY CONTEXT "I[I)Z", (void*)jni_eglQueryContext }, 480{"eglQuerySurface", "(" DISPLAY SURFACE "I[I)Z", (void*)jni_eglQuerySurface }, 481{"eglReleaseThread","()Z", (void*)jni_eglReleaseThread }, 482{"getInitCount", "(" DISPLAY ")I", (void*)jni_getInitCount }, 483{"eglChooseConfig", "(" DISPLAY "[I[" CONFIG "I[I)Z", (void*)jni_eglChooseConfig }, 484{"_eglCreateContext","(" DISPLAY CONFIG CONTEXT "[I)J", (void*)jni_eglCreateContext }, 485{"eglGetConfigs", "(" DISPLAY "[" CONFIG "I[I)Z", (void*)jni_eglGetConfigs }, 486{"eglTerminate", "(" DISPLAY ")Z", (void*)jni_eglTerminate }, 487{"eglCopyBuffers", "(" DISPLAY SURFACE OBJECT ")Z", (void*)jni_eglCopyBuffers }, 488{"eglWaitNative", "(I" OBJECT ")Z", (void*)jni_eglWaitNative }, 489{"eglGetError", "()I", (void*)jni_eglGetError }, 490{"eglGetConfigAttrib", "(" DISPLAY CONFIG "I[I)Z", (void*)jni_eglGetConfigAttrib }, 491{"_eglGetDisplay", "(" OBJECT ")J", (void*)jni_eglGetDisplay }, 492{"_eglGetCurrentContext", "()J", (void*)jni_eglGetCurrentContext }, 493{"_eglGetCurrentDisplay", "()J", (void*)jni_eglGetCurrentDisplay }, 494{"_eglGetCurrentSurface", "(I)J", (void*)jni_eglGetCurrentSurface }, 495{"_eglCreatePbufferSurface","(" DISPLAY CONFIG "[I)J", (void*)jni_eglCreatePbufferSurface }, 496{"_eglCreatePixmapSurface", "(" SURFACE DISPLAY CONFIG OBJECT "[I)V", (void*)jni_eglCreatePixmapSurface }, 497{"_eglCreateWindowSurface", "(" DISPLAY CONFIG OBJECT "[I)J", (void*)jni_eglCreateWindowSurface }, 498{"_eglCreateWindowSurfaceTexture", "(" DISPLAY CONFIG OBJECT "[I)J", (void*)jni_eglCreateWindowSurfaceTexture }, 499{"eglDestroyContext", "(" DISPLAY CONTEXT ")Z", (void*)jni_eglDestroyContext }, 500{"eglDestroySurface", "(" DISPLAY SURFACE ")Z", (void*)jni_eglDestroySurface }, 501{"eglMakeCurrent", "(" DISPLAY SURFACE SURFACE CONTEXT")Z", (void*)jni_eglMakeCurrent }, 502{"eglQueryString", "(" DISPLAY "I)" STRING, (void*)jni_eglQueryString }, 503{"eglSwapBuffers", "(" DISPLAY SURFACE ")Z", (void*)jni_eglSwapBuffers }, 504}; 505 506} // namespace android 507 508int register_com_google_android_gles_jni_EGLImpl(JNIEnv *_env) 509{ 510 int err; 511 err = android::AndroidRuntime::registerNativeMethods(_env, 512 android::classPathName, android::methods, NELEM(android::methods)); 513 return err; 514} 515