android_app_NativeActivity.cpp revision b87e22d300180ed34d0a4187c6538ddc32df566d
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/AndroidRuntime.h> 25#include <android_runtime/android_view_Surface.h> 26#include <android_runtime/android_app_NativeActivity.h> 27#include <android_runtime/android_util_AssetManager.h> 28#include <surfaceflinger/Surface.h> 29#include <ui/egl/android_natives.h> 30#include <ui/InputTransport.h> 31#include <utils/PollLoop.h> 32 33#include "JNIHelp.h" 34#include "android_os_MessageQueue.h" 35#include "android_view_InputChannel.h" 36#include "android_view_KeyEvent.h" 37 38//#define LOG_TRACE(...) 39#define LOG_TRACE(...) LOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__) 40 41namespace android 42{ 43 44static struct { 45 jclass clazz; 46 47 jmethodID dispatchUnhandledKeyEvent; 48 jmethodID setWindowFlags; 49 jmethodID setWindowFormat; 50 jmethodID showIme; 51 jmethodID hideIme; 52} gNativeActivityClassInfo; 53 54// ------------------------------------------------------------------------ 55 56struct ActivityWork { 57 int32_t cmd; 58 int32_t arg1; 59 int32_t arg2; 60}; 61 62enum { 63 CMD_DEF_KEY = 1, 64 CMD_SET_WINDOW_FORMAT, 65 CMD_SET_WINDOW_FLAGS, 66 CMD_SHOW_SOFT_INPUT, 67 CMD_HIDE_SOFT_INPUT, 68}; 69 70static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { 71 ActivityWork work; 72 work.cmd = cmd; 73 work.arg1 = arg1; 74 work.arg2 = arg2; 75 76 LOG_TRACE("write_work: cmd=%d", cmd); 77 78restart: 79 int res = write(fd, &work, sizeof(work)); 80 if (res < 0 && errno == EINTR) { 81 goto restart; 82 } 83 84 if (res == sizeof(work)) return; 85 86 if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno)); 87 else LOGW("Truncated writing to work fd: %d", res); 88} 89 90static bool read_work(int fd, ActivityWork* outWork) { 91 int res = read(fd, outWork, sizeof(ActivityWork)); 92 // no need to worry about EINTR, poll loop will just come back again. 93 if (res == sizeof(ActivityWork)) return true; 94 95 if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno)); 96 else LOGW("Truncated reading work fd: %d", res); 97 return false; 98} 99 100// ------------------------------------------------------------------------ 101 102} // namespace android 103 104using namespace android; 105 106AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) : 107 mWorkWrite(workWrite), mConsumer(channel) { 108 int msgpipe[2]; 109 if (pipe(msgpipe)) { 110 LOGW("could not create pipe: %s", strerror(errno)); 111 mDispatchKeyRead = mDispatchKeyWrite = -1; 112 } else { 113 mDispatchKeyRead = msgpipe[0]; 114 mDispatchKeyWrite = msgpipe[1]; 115 int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK); 116 SLOGW_IF(result != 0, "Could not make AInputQueue read pipe " 117 "non-blocking: %s", strerror(errno)); 118 result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK); 119 SLOGW_IF(result != 0, "Could not make AInputQueue write pipe " 120 "non-blocking: %s", strerror(errno)); 121 } 122} 123 124AInputQueue::~AInputQueue() { 125 close(mDispatchKeyRead); 126 close(mDispatchKeyWrite); 127} 128 129void AInputQueue::attachLooper(ALooper* looper, ALooper_callbackFunc* callback, void* data) { 130 mPollLoop = static_cast<android::PollLoop*>(looper); 131 mPollLoop->setLooperCallback(mConsumer.getChannel()->getReceivePipeFd(), 132 POLLIN, callback, data); 133 mPollLoop->setLooperCallback(mDispatchKeyRead, 134 POLLIN, callback, data); 135} 136 137void AInputQueue::detachLooper() { 138 mPollLoop->removeCallback(mConsumer.getChannel()->getReceivePipeFd()); 139 mPollLoop->removeCallback(mDispatchKeyRead); 140} 141 142int32_t AInputQueue::hasEvents() { 143 struct pollfd pfd[2]; 144 145 pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd(); 146 pfd[0].events = POLLIN; 147 pfd[0].revents = 0; 148 pfd[1].fd = mDispatchKeyRead; 149 pfd[0].events = POLLIN; 150 pfd[0].revents = 0; 151 152 int nfd = poll(pfd, 2, 0); 153 if (nfd <= 0) return 0; 154 return (pfd[0].revents == POLLIN || pfd[1].revents == POLLIN) ? 1 : -1; 155} 156 157int32_t AInputQueue::getEvent(AInputEvent** outEvent) { 158 *outEvent = NULL; 159 160 char byteread; 161 ssize_t nRead = read(mDispatchKeyRead, &byteread, 1); 162 if (nRead == 1) { 163 mLock.lock(); 164 if (mDispatchingKeys.size() > 0) { 165 KeyEvent* kevent = mDispatchingKeys[0]; 166 *outEvent = kevent; 167 mDispatchingKeys.removeAt(0); 168 mDeliveringKeys.add(kevent); 169 } 170 mLock.unlock(); 171 if (*outEvent != NULL) { 172 return 0; 173 } 174 } 175 176 int32_t res = mConsumer.receiveDispatchSignal(); 177 if (res != android::OK) { 178 LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", 179 mConsumer.getChannel()->getName().string(), res); 180 return -1; 181 } 182 183 InputEvent* myEvent = NULL; 184 res = mConsumer.consume(&mInputEventFactory, &myEvent); 185 if (res != android::OK) { 186 LOGW("channel '%s' ~ Failed to consume input event. status=%d", 187 mConsumer.getChannel()->getName().string(), res); 188 mConsumer.sendFinishedSignal(); 189 return -1; 190 } 191 192 *outEvent = myEvent; 193 return 0; 194} 195 196void AInputQueue::finishEvent(AInputEvent* event, bool handled) { 197 bool needFinished = true; 198 199 if (!handled && ((InputEvent*)event)->getType() == INPUT_EVENT_TYPE_KEY 200 && ((KeyEvent*)event)->hasDefaultAction()) { 201 // The app didn't handle this, but it may have a default action 202 // associated with it. We need to hand this back to Java to be 203 // executed. 204 doDefaultKey((KeyEvent*)event); 205 needFinished = false; 206 } 207 208 const size_t N = mDeliveringKeys.size(); 209 for (size_t i=0; i<N; i++) { 210 if (mDeliveringKeys[i] == event) { 211 delete event; 212 mDeliveringKeys.removeAt(i); 213 needFinished = false; 214 break; 215 } 216 } 217 218 if (needFinished) { 219 int32_t res = mConsumer.sendFinishedSignal(); 220 if (res != android::OK) { 221 LOGW("Failed to send finished signal on channel '%s'. status=%d", 222 mConsumer.getChannel()->getName().string(), res); 223 } 224 } 225} 226 227void AInputQueue::dispatchEvent(android::KeyEvent* event) { 228 mLock.lock(); 229 LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(), 230 mDispatchKeyWrite); 231 mDispatchingKeys.add(event); 232 mLock.unlock(); 233 234restart: 235 char dummy = 0; 236 int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy)); 237 if (res < 0 && errno == EINTR) { 238 goto restart; 239 } 240 241 if (res == sizeof(dummy)) return; 242 243 if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno)); 244 else LOGW("Truncated writing to dispatch fd: %d", res); 245} 246 247KeyEvent* AInputQueue::consumeUnhandledEvent() { 248 KeyEvent* event = NULL; 249 250 mLock.lock(); 251 if (mPendingKeys.size() > 0) { 252 event = mPendingKeys[0]; 253 mPendingKeys.removeAt(0); 254 } 255 mLock.unlock(); 256 257 LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event); 258 259 return event; 260} 261 262void AInputQueue::doDefaultKey(KeyEvent* keyEvent) { 263 mLock.lock(); 264 LOG_TRACE("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); 265 if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { 266 write_work(mWorkWrite, CMD_DEF_KEY); 267 } 268 mPendingKeys.add(keyEvent); 269 mLock.unlock(); 270} 271 272namespace android { 273 274// ------------------------------------------------------------------------ 275 276/* 277 * Native state for interacting with the NativeActivity class. 278 */ 279struct NativeCode : public ANativeActivity { 280 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { 281 memset((ANativeActivity*)this, 0, sizeof(ANativeActivity)); 282 memset(&callbacks, 0, sizeof(callbacks)); 283 dlhandle = _dlhandle; 284 createActivityFunc = _createFunc; 285 nativeWindow = NULL; 286 inputChannel = NULL; 287 nativeInputQueue = NULL; 288 mainWorkRead = mainWorkWrite = -1; 289 } 290 291 ~NativeCode() { 292 if (callbacks.onDestroy != NULL) { 293 callbacks.onDestroy(this); 294 } 295 if (env != NULL && clazz != NULL) { 296 env->DeleteGlobalRef(clazz); 297 } 298 if (pollLoop != NULL && mainWorkRead >= 0) { 299 pollLoop->removeCallback(mainWorkRead); 300 } 301 if (nativeInputQueue != NULL) { 302 nativeInputQueue->mWorkWrite = -1; 303 } 304 setSurface(NULL); 305 setInputChannel(NULL); 306 if (mainWorkRead >= 0) close(mainWorkRead); 307 if (mainWorkWrite >= 0) close(mainWorkWrite); 308 if (dlhandle != NULL) { 309 // for now don't unload... we probably should clean this 310 // up and only keep one open dlhandle per proc, since there 311 // is really no benefit to unloading the code. 312 //dlclose(dlhandle); 313 } 314 } 315 316 void setSurface(jobject _surface) { 317 if (_surface != NULL) { 318 nativeWindow = android_Surface_getNativeWindow(env, _surface); 319 } else { 320 nativeWindow = NULL; 321 } 322 } 323 324 status_t setInputChannel(jobject _channel) { 325 if (inputChannel != NULL) { 326 delete nativeInputQueue; 327 env->DeleteGlobalRef(inputChannel); 328 } 329 inputChannel = NULL; 330 nativeInputQueue = NULL; 331 if (_channel != NULL) { 332 inputChannel = env->NewGlobalRef(_channel); 333 sp<InputChannel> ic = 334 android_view_InputChannel_getInputChannel(env, _channel); 335 if (ic != NULL) { 336 nativeInputQueue = new AInputQueue(ic, mainWorkWrite); 337 if (nativeInputQueue->getConsumer().initialize() != android::OK) { 338 delete nativeInputQueue; 339 nativeInputQueue = NULL; 340 return UNKNOWN_ERROR; 341 } 342 } else { 343 return UNKNOWN_ERROR; 344 } 345 } 346 return OK; 347 } 348 349 ANativeActivityCallbacks callbacks; 350 351 void* dlhandle; 352 ANativeActivity_createFunc* createActivityFunc; 353 354 String8 internalDataPath; 355 String8 externalDataPath; 356 357 sp<ANativeWindow> nativeWindow; 358 int32_t lastWindowWidth; 359 int32_t lastWindowHeight; 360 361 jobject inputChannel; 362 struct AInputQueue* nativeInputQueue; 363 364 // These are used to wake up the main thread to process work. 365 int mainWorkRead; 366 int mainWorkWrite; 367 sp<PollLoop> pollLoop; 368}; 369 370void android_NativeActivity_setWindowFormat( 371 ANativeActivity* activity, int32_t format) { 372 NativeCode* code = static_cast<NativeCode*>(activity); 373 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format); 374} 375 376void android_NativeActivity_setWindowFlags( 377 ANativeActivity* activity, int32_t values, int32_t mask) { 378 NativeCode* code = static_cast<NativeCode*>(activity); 379 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); 380} 381 382void android_NativeActivity_showSoftInput( 383 ANativeActivity* activity, int32_t flags) { 384 NativeCode* code = static_cast<NativeCode*>(activity); 385 write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); 386} 387 388void android_NativeActivity_hideSoftInput( 389 ANativeActivity* activity, int32_t flags) { 390 NativeCode* code = static_cast<NativeCode*>(activity); 391 write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags); 392} 393 394// ------------------------------------------------------------------------ 395 396/* 397 * Callback for handling native events on the application's main thread. 398 */ 399static bool mainWorkCallback(int fd, int events, void* data) { 400 NativeCode* code = (NativeCode*)data; 401 if ((events & POLLIN) == 0) { 402 return true; 403 } 404 405 ActivityWork work; 406 if (!read_work(code->mainWorkRead, &work)) { 407 return true; 408 } 409 410 LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); 411 412 switch (work.cmd) { 413 case CMD_DEF_KEY: { 414 KeyEvent* keyEvent; 415 while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) { 416 jobject inputEventObj = android_view_KeyEvent_fromNative( 417 code->env, keyEvent); 418 code->env->CallVoidMethod(code->clazz, 419 gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); 420 int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal(); 421 if (res != OK) { 422 LOGW("Failed to send finished signal on channel '%s'. status=%d", 423 code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res); 424 } 425 } 426 } break; 427 case CMD_SET_WINDOW_FORMAT: { 428 code->env->CallVoidMethod(code->clazz, 429 gNativeActivityClassInfo.setWindowFormat, work.arg1); 430 } break; 431 case CMD_SET_WINDOW_FLAGS: { 432 code->env->CallVoidMethod(code->clazz, 433 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); 434 } break; 435 case CMD_SHOW_SOFT_INPUT: { 436 code->env->CallVoidMethod(code->clazz, 437 gNativeActivityClassInfo.showIme, work.arg1); 438 } break; 439 case CMD_HIDE_SOFT_INPUT: { 440 code->env->CallVoidMethod(code->clazz, 441 gNativeActivityClassInfo.hideIme, work.arg1); 442 } break; 443 default: 444 LOGW("Unknown work command: %d", work.cmd); 445 break; 446 } 447 448 return true; 449} 450 451// ------------------------------------------------------------------------ 452 453static jint 454loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue, 455 jstring internalDataDir, jstring externalDataDir, int sdkVersion, 456 jobject jAssetMgr) 457{ 458 LOG_TRACE("loadNativeCode_native"); 459 460 const char* pathStr = env->GetStringUTFChars(path, NULL); 461 NativeCode* code = NULL; 462 463 void* handle = dlopen(pathStr, RTLD_LAZY); 464 465 env->ReleaseStringUTFChars(path, pathStr); 466 467 if (handle != NULL) { 468 code = new NativeCode(handle, (ANativeActivity_createFunc*) 469 dlsym(handle, "ANativeActivity_onCreate")); 470 if (code->createActivityFunc == NULL) { 471 LOGW("ANativeActivity_onCreate not found"); 472 delete code; 473 return 0; 474 } 475 476 code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); 477 if (code->pollLoop == NULL) { 478 LOGW("Unable to retrieve MessageQueue's PollLoop"); 479 delete code; 480 return 0; 481 } 482 483 int msgpipe[2]; 484 if (pipe(msgpipe)) { 485 LOGW("could not create pipe: %s", strerror(errno)); 486 delete code; 487 return 0; 488 } 489 code->mainWorkRead = msgpipe[0]; 490 code->mainWorkWrite = msgpipe[1]; 491 int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK); 492 SLOGW_IF(result != 0, "Could not make main work read pipe " 493 "non-blocking: %s", strerror(errno)); 494 result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); 495 SLOGW_IF(result != 0, "Could not make main work write pipe " 496 "non-blocking: %s", strerror(errno)); 497 code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); 498 499 code->ANativeActivity::callbacks = &code->callbacks; 500 if (env->GetJavaVM(&code->vm) < 0) { 501 LOGW("NativeActivity GetJavaVM failed"); 502 delete code; 503 return 0; 504 } 505 code->env = env; 506 code->clazz = env->NewGlobalRef(clazz); 507 508 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); 509 code->internalDataPath = dirStr; 510 code->internalDataPath = code->internalDataPath.string(); 511 env->ReleaseStringUTFChars(path, dirStr); 512 513 dirStr = env->GetStringUTFChars(externalDataDir, NULL); 514 code->externalDataPath = dirStr; 515 code->externalDataPath = code->externalDataPath.string(); 516 env->ReleaseStringUTFChars(path, dirStr); 517 518 code->sdkVersion = sdkVersion; 519 520 code->assetManager = assetManagerForJavaObject(env, jAssetMgr); 521 522 code->createActivityFunc(code, NULL, 0); 523 } 524 525 return (jint)code; 526} 527 528static void 529unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) 530{ 531 LOG_TRACE("unloadNativeCode_native"); 532 if (handle != 0) { 533 NativeCode* code = (NativeCode*)handle; 534 delete code; 535 } 536} 537 538static void 539onStart_native(JNIEnv* env, jobject clazz, jint handle) 540{ 541 LOG_TRACE("onStart_native"); 542 if (handle != 0) { 543 NativeCode* code = (NativeCode*)handle; 544 if (code->callbacks.onStart != NULL) { 545 code->callbacks.onStart(code); 546 } 547 } 548} 549 550static void 551onResume_native(JNIEnv* env, jobject clazz, jint handle) 552{ 553 LOG_TRACE("onResume_native"); 554 if (handle != 0) { 555 NativeCode* code = (NativeCode*)handle; 556 if (code->callbacks.onResume != NULL) { 557 code->callbacks.onResume(code); 558 } 559 } 560} 561 562static void 563onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) 564{ 565 LOG_TRACE("onSaveInstanceState_native"); 566 if (handle != 0) { 567 NativeCode* code = (NativeCode*)handle; 568 if (code->callbacks.onSaveInstanceState != NULL) { 569 size_t len = 0; 570 code->callbacks.onSaveInstanceState(code, &len); 571 } 572 } 573} 574 575static void 576onPause_native(JNIEnv* env, jobject clazz, jint handle) 577{ 578 LOG_TRACE("onPause_native"); 579 if (handle != 0) { 580 NativeCode* code = (NativeCode*)handle; 581 if (code->callbacks.onPause != NULL) { 582 code->callbacks.onPause(code); 583 } 584 } 585} 586 587static void 588onStop_native(JNIEnv* env, jobject clazz, jint handle) 589{ 590 LOG_TRACE("onStop_native"); 591 if (handle != 0) { 592 NativeCode* code = (NativeCode*)handle; 593 if (code->callbacks.onStop != NULL) { 594 code->callbacks.onStop(code); 595 } 596 } 597} 598 599static void 600onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) 601{ 602 LOG_TRACE("onLowMemory_native"); 603 if (handle != 0) { 604 NativeCode* code = (NativeCode*)handle; 605 if (code->callbacks.onLowMemory != NULL) { 606 code->callbacks.onLowMemory(code); 607 } 608 } 609} 610 611static void 612onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) 613{ 614 LOG_TRACE("onWindowFocusChanged_native"); 615 if (handle != 0) { 616 NativeCode* code = (NativeCode*)handle; 617 if (code->callbacks.onWindowFocusChanged != NULL) { 618 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); 619 } 620 } 621} 622 623static void 624onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 625{ 626 LOG_TRACE("onSurfaceCreated_native"); 627 if (handle != 0) { 628 NativeCode* code = (NativeCode*)handle; 629 code->setSurface(surface); 630 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { 631 code->callbacks.onNativeWindowCreated(code, 632 code->nativeWindow.get()); 633 } 634 } 635} 636 637static int32_t getWindowProp(ANativeWindow* window, int what) { 638 int value; 639 int res = window->query(window, what, &value); 640 return res < 0 ? res : value; 641} 642 643static void 644onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, 645 jint format, jint width, jint height) 646{ 647 LOG_TRACE("onSurfaceChanged_native"); 648 if (handle != 0) { 649 NativeCode* code = (NativeCode*)handle; 650 sp<ANativeWindow> oldNativeWindow = code->nativeWindow; 651 code->setSurface(surface); 652 if (oldNativeWindow != code->nativeWindow) { 653 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 654 code->callbacks.onNativeWindowDestroyed(code, 655 oldNativeWindow.get()); 656 } 657 if (code->nativeWindow != NULL) { 658 if (code->callbacks.onNativeWindowCreated != NULL) { 659 code->callbacks.onNativeWindowCreated(code, 660 code->nativeWindow.get()); 661 } 662 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(), 663 NATIVE_WINDOW_WIDTH); 664 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(), 665 NATIVE_WINDOW_HEIGHT); 666 } 667 } else { 668 // Maybe it resized? 669 int32_t newWidth = getWindowProp(code->nativeWindow.get(), 670 NATIVE_WINDOW_WIDTH); 671 int32_t newHeight = getWindowProp(code->nativeWindow.get(), 672 NATIVE_WINDOW_HEIGHT); 673 if (newWidth != code->lastWindowWidth 674 || newHeight != code->lastWindowHeight) { 675 if (code->callbacks.onNativeWindowResized != NULL) { 676 code->callbacks.onNativeWindowResized(code, 677 code->nativeWindow.get()); 678 } 679 } 680 } 681 } 682} 683 684static void 685onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle) 686{ 687 LOG_TRACE("onSurfaceRedrawNeeded_native"); 688 if (handle != 0) { 689 NativeCode* code = (NativeCode*)handle; 690 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) { 691 code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get()); 692 } 693 } 694} 695 696static void 697onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 698{ 699 LOG_TRACE("onSurfaceDestroyed_native"); 700 if (handle != 0) { 701 NativeCode* code = (NativeCode*)handle; 702 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 703 code->callbacks.onNativeWindowDestroyed(code, 704 code->nativeWindow.get()); 705 } 706 code->setSurface(NULL); 707 } 708} 709 710static void 711onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 712{ 713 LOG_TRACE("onInputChannelCreated_native"); 714 if (handle != 0) { 715 NativeCode* code = (NativeCode*)handle; 716 status_t err = code->setInputChannel(channel); 717 if (err != OK) { 718 jniThrowException(env, "java/lang/IllegalStateException", 719 "Error setting input channel"); 720 return; 721 } 722 if (code->callbacks.onInputQueueCreated != NULL) { 723 code->callbacks.onInputQueueCreated(code, 724 code->nativeInputQueue); 725 } 726 } 727} 728 729static void 730onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 731{ 732 LOG_TRACE("onInputChannelDestroyed_native"); 733 if (handle != 0) { 734 NativeCode* code = (NativeCode*)handle; 735 if (code->nativeInputQueue != NULL 736 && code->callbacks.onInputQueueDestroyed != NULL) { 737 code->callbacks.onInputQueueDestroyed(code, 738 code->nativeInputQueue); 739 } 740 code->setInputChannel(NULL); 741 } 742} 743 744static void 745onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle, 746 jint x, jint y, jint w, jint h) 747{ 748 LOG_TRACE("onContentRectChanged_native"); 749 if (handle != 0) { 750 NativeCode* code = (NativeCode*)handle; 751 if (code->callbacks.onContentRectChanged != NULL) { 752 ARect rect; 753 rect.left = x; 754 rect.top = y; 755 rect.right = x+w; 756 rect.bottom = y+h; 757 code->callbacks.onContentRectChanged(code, &rect); 758 } 759 } 760} 761 762static void 763dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj) 764{ 765 LOG_TRACE("dispatchKeyEvent_native"); 766 if (handle != 0) { 767 NativeCode* code = (NativeCode*)handle; 768 if (code->nativeInputQueue != NULL) { 769 KeyEvent* event = new KeyEvent(); 770 android_view_KeyEvent_toNative(env, eventObj, INPUT_EVENT_NATURE_KEY, event); 771 code->nativeInputQueue->dispatchEvent(event); 772 } 773 } 774} 775 776static const JNINativeMethod g_methods[] = { 777 { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;)I", 778 (void*)loadNativeCode_native }, 779 { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, 780 { "onStartNative", "(I)V", (void*)onStart_native }, 781 { "onResumeNative", "(I)V", (void*)onResume_native }, 782 { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native }, 783 { "onPauseNative", "(I)V", (void*)onPause_native }, 784 { "onStopNative", "(I)V", (void*)onStop_native }, 785 { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, 786 { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, 787 { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, 788 { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, 789 { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native }, 790 { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, 791 { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, 792 { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, 793 { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native }, 794 { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native }, 795}; 796 797static const char* const kNativeActivityPathName = "android/app/NativeActivity"; 798 799#define FIND_CLASS(var, className) \ 800 var = env->FindClass(className); \ 801 LOG_FATAL_IF(! var, "Unable to find class " className); \ 802 var = jclass(env->NewGlobalRef(var)); 803 804#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 805 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 806 LOG_FATAL_IF(! var, "Unable to find method" methodName); 807 808int register_android_app_NativeActivity(JNIEnv* env) 809{ 810 //LOGD("register_android_app_NativeActivity"); 811 812 FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); 813 814 GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, 815 gNativeActivityClassInfo.clazz, 816 "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V"); 817 818 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, 819 gNativeActivityClassInfo.clazz, 820 "setWindowFlags", "(II)V"); 821 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, 822 gNativeActivityClassInfo.clazz, 823 "setWindowFormat", "(I)V"); 824 GET_METHOD_ID(gNativeActivityClassInfo.showIme, 825 gNativeActivityClassInfo.clazz, 826 "showIme", "(I)V"); 827 GET_METHOD_ID(gNativeActivityClassInfo.hideIme, 828 gNativeActivityClassInfo.clazz, 829 "hideIme", "(I)V"); 830 831 return AndroidRuntime::registerNativeMethods( 832 env, kNativeActivityPathName, 833 g_methods, NELEM(g_methods)); 834} 835 836} // namespace android 837