1/* 2 * Copyright (C) 2010 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_TAG "NativeActivity" 18#include <utils/Log.h> 19 20#include <poll.h> 21#include <dlfcn.h> 22#include <fcntl.h> 23 24#include <android_runtime/android_app_NativeActivity.h> 25#include <android_runtime/android_util_AssetManager.h> 26#include <android_runtime/android_view_Surface.h> 27#include <android_runtime/AndroidRuntime.h> 28#include <input/InputTransport.h> 29 30#include <gui/Surface.h> 31 32#include <system/window.h> 33 34#include <utils/Looper.h> 35 36#include "JNIHelp.h" 37#include "android_os_MessageQueue.h" 38#include "android_view_InputChannel.h" 39#include "android_view_KeyEvent.h" 40 41#define LOG_TRACE(...) 42//#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) 43 44namespace android 45{ 46 47static struct { 48 jmethodID finish; 49 jmethodID setWindowFlags; 50 jmethodID setWindowFormat; 51 jmethodID showIme; 52 jmethodID hideIme; 53} gNativeActivityClassInfo; 54 55// ------------------------------------------------------------------------ 56 57struct ActivityWork { 58 int32_t cmd; 59 int32_t arg1; 60 int32_t arg2; 61}; 62 63enum { 64 CMD_FINISH = 1, 65 CMD_SET_WINDOW_FORMAT, 66 CMD_SET_WINDOW_FLAGS, 67 CMD_SHOW_SOFT_INPUT, 68 CMD_HIDE_SOFT_INPUT, 69}; 70 71static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { 72 ActivityWork work; 73 work.cmd = cmd; 74 work.arg1 = arg1; 75 work.arg2 = arg2; 76 77 LOG_TRACE("write_work: cmd=%d", cmd); 78 79restart: 80 int res = write(fd, &work, sizeof(work)); 81 if (res < 0 && errno == EINTR) { 82 goto restart; 83 } 84 85 if (res == sizeof(work)) return; 86 87 if (res < 0) ALOGW("Failed writing to work fd: %s", strerror(errno)); 88 else ALOGW("Truncated writing to work fd: %d", res); 89} 90 91static bool read_work(int fd, ActivityWork* outWork) { 92 int res = read(fd, outWork, sizeof(ActivityWork)); 93 // no need to worry about EINTR, poll loop will just come back again. 94 if (res == sizeof(ActivityWork)) return true; 95 96 if (res < 0) ALOGW("Failed reading work fd: %s", strerror(errno)); 97 else ALOGW("Truncated reading work fd: %d", res); 98 return false; 99} 100 101/* 102 * Native state for interacting with the NativeActivity class. 103 */ 104struct NativeCode : public ANativeActivity { 105 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { 106 memset((ANativeActivity*)this, 0, sizeof(ANativeActivity)); 107 memset(&callbacks, 0, sizeof(callbacks)); 108 dlhandle = _dlhandle; 109 createActivityFunc = _createFunc; 110 nativeWindow = NULL; 111 mainWorkRead = mainWorkWrite = -1; 112 } 113 114 ~NativeCode() { 115 if (callbacks.onDestroy != NULL) { 116 callbacks.onDestroy(this); 117 } 118 if (env != NULL && clazz != NULL) { 119 env->DeleteGlobalRef(clazz); 120 } 121 if (messageQueue != NULL && mainWorkRead >= 0) { 122 messageQueue->getLooper()->removeFd(mainWorkRead); 123 } 124 setSurface(NULL); 125 if (mainWorkRead >= 0) close(mainWorkRead); 126 if (mainWorkWrite >= 0) close(mainWorkWrite); 127 if (dlhandle != NULL) { 128 // for now don't unload... we probably should clean this 129 // up and only keep one open dlhandle per proc, since there 130 // is really no benefit to unloading the code. 131 //dlclose(dlhandle); 132 } 133 } 134 135 void setSurface(jobject _surface) { 136 if (_surface != NULL) { 137 nativeWindow = android_view_Surface_getNativeWindow(env, _surface); 138 } else { 139 nativeWindow = NULL; 140 } 141 } 142 143 ANativeActivityCallbacks callbacks; 144 145 void* dlhandle; 146 ANativeActivity_createFunc* createActivityFunc; 147 148 String8 internalDataPathObj; 149 String8 externalDataPathObj; 150 String8 obbPathObj; 151 152 sp<ANativeWindow> nativeWindow; 153 int32_t lastWindowWidth; 154 int32_t lastWindowHeight; 155 156 // These are used to wake up the main thread to process work. 157 int mainWorkRead; 158 int mainWorkWrite; 159 sp<MessageQueue> messageQueue; 160}; 161 162void android_NativeActivity_finish(ANativeActivity* activity) { 163 NativeCode* code = static_cast<NativeCode*>(activity); 164 write_work(code->mainWorkWrite, CMD_FINISH, 0); 165} 166 167void android_NativeActivity_setWindowFormat( 168 ANativeActivity* activity, int32_t format) { 169 NativeCode* code = static_cast<NativeCode*>(activity); 170 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format); 171} 172 173void android_NativeActivity_setWindowFlags( 174 ANativeActivity* activity, int32_t values, int32_t mask) { 175 NativeCode* code = static_cast<NativeCode*>(activity); 176 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); 177} 178 179void android_NativeActivity_showSoftInput( 180 ANativeActivity* activity, int32_t flags) { 181 NativeCode* code = static_cast<NativeCode*>(activity); 182 write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); 183} 184 185void android_NativeActivity_hideSoftInput( 186 ANativeActivity* activity, int32_t flags) { 187 NativeCode* code = static_cast<NativeCode*>(activity); 188 write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags); 189} 190 191// ------------------------------------------------------------------------ 192 193/* 194 * Callback for handling native events on the application's main thread. 195 */ 196static int mainWorkCallback(int fd, int events, void* data) { 197 NativeCode* code = (NativeCode*)data; 198 if ((events & POLLIN) == 0) { 199 return 1; 200 } 201 202 ActivityWork work; 203 if (!read_work(code->mainWorkRead, &work)) { 204 return 1; 205 } 206 207 LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); 208 209 switch (work.cmd) { 210 case CMD_FINISH: { 211 code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish); 212 code->messageQueue->raiseAndClearException(code->env, "finish"); 213 } break; 214 case CMD_SET_WINDOW_FORMAT: { 215 code->env->CallVoidMethod(code->clazz, 216 gNativeActivityClassInfo.setWindowFormat, work.arg1); 217 code->messageQueue->raiseAndClearException(code->env, "setWindowFormat"); 218 } break; 219 case CMD_SET_WINDOW_FLAGS: { 220 code->env->CallVoidMethod(code->clazz, 221 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); 222 code->messageQueue->raiseAndClearException(code->env, "setWindowFlags"); 223 } break; 224 case CMD_SHOW_SOFT_INPUT: { 225 code->env->CallVoidMethod(code->clazz, 226 gNativeActivityClassInfo.showIme, work.arg1); 227 code->messageQueue->raiseAndClearException(code->env, "showIme"); 228 } break; 229 case CMD_HIDE_SOFT_INPUT: { 230 code->env->CallVoidMethod(code->clazz, 231 gNativeActivityClassInfo.hideIme, work.arg1); 232 code->messageQueue->raiseAndClearException(code->env, "hideIme"); 233 } break; 234 default: 235 ALOGW("Unknown work command: %d", work.cmd); 236 break; 237 } 238 239 return 1; 240} 241 242// ------------------------------------------------------------------------ 243 244static jint 245loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName, 246 jobject messageQueue, jstring internalDataDir, jstring obbDir, 247 jstring externalDataDir, int sdkVersion, 248 jobject jAssetMgr, jbyteArray savedState) 249{ 250 LOG_TRACE("loadNativeCode_native"); 251 252 const char* pathStr = env->GetStringUTFChars(path, NULL); 253 NativeCode* code = NULL; 254 255 void* handle = dlopen(pathStr, RTLD_LAZY); 256 257 env->ReleaseStringUTFChars(path, pathStr); 258 259 if (handle != NULL) { 260 const char* funcStr = env->GetStringUTFChars(funcName, NULL); 261 code = new NativeCode(handle, (ANativeActivity_createFunc*) 262 dlsym(handle, funcStr)); 263 env->ReleaseStringUTFChars(funcName, funcStr); 264 265 if (code->createActivityFunc == NULL) { 266 ALOGW("ANativeActivity_onCreate not found"); 267 delete code; 268 return 0; 269 } 270 271 code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue); 272 if (code->messageQueue == NULL) { 273 ALOGW("Unable to retrieve native MessageQueue"); 274 delete code; 275 return 0; 276 } 277 278 int msgpipe[2]; 279 if (pipe(msgpipe)) { 280 ALOGW("could not create pipe: %s", strerror(errno)); 281 delete code; 282 return 0; 283 } 284 code->mainWorkRead = msgpipe[0]; 285 code->mainWorkWrite = msgpipe[1]; 286 int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK); 287 SLOGW_IF(result != 0, "Could not make main work read pipe " 288 "non-blocking: %s", strerror(errno)); 289 result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); 290 SLOGW_IF(result != 0, "Could not make main work write pipe " 291 "non-blocking: %s", strerror(errno)); 292 code->messageQueue->getLooper()->addFd( 293 code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code); 294 295 code->ANativeActivity::callbacks = &code->callbacks; 296 if (env->GetJavaVM(&code->vm) < 0) { 297 ALOGW("NativeActivity GetJavaVM failed"); 298 delete code; 299 return 0; 300 } 301 code->env = env; 302 code->clazz = env->NewGlobalRef(clazz); 303 304 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); 305 code->internalDataPathObj = dirStr; 306 code->internalDataPath = code->internalDataPathObj.string(); 307 env->ReleaseStringUTFChars(internalDataDir, dirStr); 308 309 if (externalDataDir != NULL) { 310 dirStr = env->GetStringUTFChars(externalDataDir, NULL); 311 code->externalDataPathObj = dirStr; 312 env->ReleaseStringUTFChars(externalDataDir, dirStr); 313 } 314 code->externalDataPath = code->externalDataPathObj.string(); 315 316 code->sdkVersion = sdkVersion; 317 318 code->assetManager = assetManagerForJavaObject(env, jAssetMgr); 319 320 if (obbDir != NULL) { 321 dirStr = env->GetStringUTFChars(obbDir, NULL); 322 code->obbPathObj = dirStr; 323 env->ReleaseStringUTFChars(obbDir, dirStr); 324 } 325 code->obbPath = code->obbPathObj.string(); 326 327 jbyte* rawSavedState = NULL; 328 jsize rawSavedSize = 0; 329 if (savedState != NULL) { 330 rawSavedState = env->GetByteArrayElements(savedState, NULL); 331 rawSavedSize = env->GetArrayLength(savedState); 332 } 333 334 code->createActivityFunc(code, rawSavedState, rawSavedSize); 335 336 if (rawSavedState != NULL) { 337 env->ReleaseByteArrayElements(savedState, rawSavedState, 0); 338 } 339 } 340 341 return (jint)code; 342} 343 344static void 345unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) 346{ 347 LOG_TRACE("unloadNativeCode_native"); 348 if (handle != 0) { 349 NativeCode* code = (NativeCode*)handle; 350 delete code; 351 } 352} 353 354static void 355onStart_native(JNIEnv* env, jobject clazz, jint handle) 356{ 357 LOG_TRACE("onStart_native"); 358 if (handle != 0) { 359 NativeCode* code = (NativeCode*)handle; 360 if (code->callbacks.onStart != NULL) { 361 code->callbacks.onStart(code); 362 } 363 } 364} 365 366static void 367onResume_native(JNIEnv* env, jobject clazz, jint handle) 368{ 369 LOG_TRACE("onResume_native"); 370 if (handle != 0) { 371 NativeCode* code = (NativeCode*)handle; 372 if (code->callbacks.onResume != NULL) { 373 code->callbacks.onResume(code); 374 } 375 } 376} 377 378static jbyteArray 379onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) 380{ 381 LOG_TRACE("onSaveInstanceState_native"); 382 383 jbyteArray array = NULL; 384 385 if (handle != 0) { 386 NativeCode* code = (NativeCode*)handle; 387 if (code->callbacks.onSaveInstanceState != NULL) { 388 size_t len = 0; 389 jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len); 390 if (len > 0) { 391 array = env->NewByteArray(len); 392 if (array != NULL) { 393 env->SetByteArrayRegion(array, 0, len, state); 394 } 395 } 396 if (state != NULL) { 397 free(state); 398 } 399 } 400 } 401 402 return array; 403} 404 405static void 406onPause_native(JNIEnv* env, jobject clazz, jint handle) 407{ 408 LOG_TRACE("onPause_native"); 409 if (handle != 0) { 410 NativeCode* code = (NativeCode*)handle; 411 if (code->callbacks.onPause != NULL) { 412 code->callbacks.onPause(code); 413 } 414 } 415} 416 417static void 418onStop_native(JNIEnv* env, jobject clazz, jint handle) 419{ 420 LOG_TRACE("onStop_native"); 421 if (handle != 0) { 422 NativeCode* code = (NativeCode*)handle; 423 if (code->callbacks.onStop != NULL) { 424 code->callbacks.onStop(code); 425 } 426 } 427} 428 429static void 430onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle) 431{ 432 LOG_TRACE("onConfigurationChanged_native"); 433 if (handle != 0) { 434 NativeCode* code = (NativeCode*)handle; 435 if (code->callbacks.onConfigurationChanged != NULL) { 436 code->callbacks.onConfigurationChanged(code); 437 } 438 } 439} 440 441static void 442onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) 443{ 444 LOG_TRACE("onLowMemory_native"); 445 if (handle != 0) { 446 NativeCode* code = (NativeCode*)handle; 447 if (code->callbacks.onLowMemory != NULL) { 448 code->callbacks.onLowMemory(code); 449 } 450 } 451} 452 453static void 454onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) 455{ 456 LOG_TRACE("onWindowFocusChanged_native"); 457 if (handle != 0) { 458 NativeCode* code = (NativeCode*)handle; 459 if (code->callbacks.onWindowFocusChanged != NULL) { 460 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); 461 } 462 } 463} 464 465static void 466onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 467{ 468 LOG_TRACE("onSurfaceCreated_native"); 469 if (handle != 0) { 470 NativeCode* code = (NativeCode*)handle; 471 code->setSurface(surface); 472 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { 473 code->callbacks.onNativeWindowCreated(code, 474 code->nativeWindow.get()); 475 } 476 } 477} 478 479static int32_t getWindowProp(ANativeWindow* window, int what) { 480 int value; 481 int res = window->query(window, what, &value); 482 return res < 0 ? res : value; 483} 484 485static void 486onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, 487 jint format, jint width, jint height) 488{ 489 LOG_TRACE("onSurfaceChanged_native"); 490 if (handle != 0) { 491 NativeCode* code = (NativeCode*)handle; 492 sp<ANativeWindow> oldNativeWindow = code->nativeWindow; 493 code->setSurface(surface); 494 if (oldNativeWindow != code->nativeWindow) { 495 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 496 code->callbacks.onNativeWindowDestroyed(code, 497 oldNativeWindow.get()); 498 } 499 if (code->nativeWindow != NULL) { 500 if (code->callbacks.onNativeWindowCreated != NULL) { 501 code->callbacks.onNativeWindowCreated(code, 502 code->nativeWindow.get()); 503 } 504 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(), 505 NATIVE_WINDOW_WIDTH); 506 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(), 507 NATIVE_WINDOW_HEIGHT); 508 } 509 } else { 510 // Maybe it resized? 511 int32_t newWidth = getWindowProp(code->nativeWindow.get(), 512 NATIVE_WINDOW_WIDTH); 513 int32_t newHeight = getWindowProp(code->nativeWindow.get(), 514 NATIVE_WINDOW_HEIGHT); 515 if (newWidth != code->lastWindowWidth 516 || newHeight != code->lastWindowHeight) { 517 if (code->callbacks.onNativeWindowResized != NULL) { 518 code->callbacks.onNativeWindowResized(code, 519 code->nativeWindow.get()); 520 } 521 } 522 } 523 } 524} 525 526static void 527onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle) 528{ 529 LOG_TRACE("onSurfaceRedrawNeeded_native"); 530 if (handle != 0) { 531 NativeCode* code = (NativeCode*)handle; 532 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) { 533 code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get()); 534 } 535 } 536} 537 538static void 539onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 540{ 541 LOG_TRACE("onSurfaceDestroyed_native"); 542 if (handle != 0) { 543 NativeCode* code = (NativeCode*)handle; 544 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 545 code->callbacks.onNativeWindowDestroyed(code, 546 code->nativeWindow.get()); 547 } 548 code->setSurface(NULL); 549 } 550} 551 552static void 553onInputQueueCreated_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr) 554{ 555 LOG_TRACE("onInputChannelCreated_native"); 556 if (handle != 0) { 557 NativeCode* code = (NativeCode*)handle; 558 if (code->callbacks.onInputQueueCreated != NULL) { 559 AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr); 560 code->callbacks.onInputQueueCreated(code, queue); 561 } 562 } 563} 564 565static void 566onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jint queuePtr) 567{ 568 LOG_TRACE("onInputChannelDestroyed_native"); 569 if (handle != 0) { 570 NativeCode* code = (NativeCode*)handle; 571 if (code->callbacks.onInputQueueDestroyed != NULL) { 572 AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr); 573 code->callbacks.onInputQueueDestroyed(code, queue); 574 } 575 } 576} 577 578static void 579onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle, 580 jint x, jint y, jint w, jint h) 581{ 582 LOG_TRACE("onContentRectChanged_native"); 583 if (handle != 0) { 584 NativeCode* code = (NativeCode*)handle; 585 if (code->callbacks.onContentRectChanged != NULL) { 586 ARect rect; 587 rect.left = x; 588 rect.top = y; 589 rect.right = x+w; 590 rect.bottom = y+h; 591 code->callbacks.onContentRectChanged(code, &rect); 592 } 593 } 594} 595 596static const JNINativeMethod g_methods[] = { 597 { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)I", 598 (void*)loadNativeCode_native }, 599 { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, 600 { "onStartNative", "(I)V", (void*)onStart_native }, 601 { "onResumeNative", "(I)V", (void*)onResume_native }, 602 { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native }, 603 { "onPauseNative", "(I)V", (void*)onPause_native }, 604 { "onStopNative", "(I)V", (void*)onStop_native }, 605 { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native }, 606 { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, 607 { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, 608 { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, 609 { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, 610 { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native }, 611 { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, 612 { "onInputQueueCreatedNative", "(II)V", 613 (void*)onInputQueueCreated_native }, 614 { "onInputQueueDestroyedNative", "(II)V", 615 (void*)onInputQueueDestroyed_native }, 616 { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native }, 617}; 618 619static const char* const kNativeActivityPathName = "android/app/NativeActivity"; 620 621#define FIND_CLASS(var, className) \ 622 var = env->FindClass(className); \ 623 LOG_FATAL_IF(! var, "Unable to find class %s", className); 624 625#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 626 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 627 LOG_FATAL_IF(! var, "Unable to find method" methodName); 628 629int register_android_app_NativeActivity(JNIEnv* env) 630{ 631 //ALOGD("register_android_app_NativeActivity"); 632 jclass clazz; 633 FIND_CLASS(clazz, kNativeActivityPathName); 634 635 GET_METHOD_ID(gNativeActivityClassInfo.finish, 636 clazz, 637 "finish", "()V"); 638 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, 639 clazz, 640 "setWindowFlags", "(II)V"); 641 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, 642 clazz, 643 "setWindowFormat", "(I)V"); 644 GET_METHOD_ID(gNativeActivityClassInfo.showIme, 645 clazz, 646 "showIme", "(I)V"); 647 GET_METHOD_ID(gNativeActivityClassInfo.hideIme, 648 clazz, 649 "hideIme", "(I)V"); 650 651 return AndroidRuntime::registerNativeMethods( 652 env, kNativeActivityPathName, 653 g_methods, NELEM(g_methods)); 654} 655 656} // namespace android 657