android_app_NativeActivity.cpp revision 805fd7ee0e5dc2939e85c84f78d9890a51982bc0
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/Looper.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 preDispatchKeyEvent; 49 jmethodID finish; 50 jmethodID setWindowFlags; 51 jmethodID setWindowFormat; 52 jmethodID showIme; 53 jmethodID hideIme; 54} gNativeActivityClassInfo; 55 56// ------------------------------------------------------------------------ 57 58struct ActivityWork { 59 int32_t cmd; 60 int32_t arg1; 61 int32_t arg2; 62}; 63 64enum { 65 CMD_DEF_KEY = 1, 66 CMD_FINISH, 67 CMD_SET_WINDOW_FORMAT, 68 CMD_SET_WINDOW_FLAGS, 69 CMD_SHOW_SOFT_INPUT, 70 CMD_HIDE_SOFT_INPUT, 71}; 72 73static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { 74 ActivityWork work; 75 work.cmd = cmd; 76 work.arg1 = arg1; 77 work.arg2 = arg2; 78 79 LOG_TRACE("write_work: cmd=%d", cmd); 80 81restart: 82 int res = write(fd, &work, sizeof(work)); 83 if (res < 0 && errno == EINTR) { 84 goto restart; 85 } 86 87 if (res == sizeof(work)) return; 88 89 if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno)); 90 else LOGW("Truncated writing to work fd: %d", res); 91} 92 93static bool read_work(int fd, ActivityWork* outWork) { 94 int res = read(fd, outWork, sizeof(ActivityWork)); 95 // no need to worry about EINTR, poll loop will just come back again. 96 if (res == sizeof(ActivityWork)) return true; 97 98 if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno)); 99 else LOGW("Truncated reading work fd: %d", res); 100 return false; 101} 102 103// ------------------------------------------------------------------------ 104 105} // namespace android 106 107using namespace android; 108 109AInputQueue::AInputQueue(const sp<InputChannel>& channel, int workWrite) : 110 mWorkWrite(workWrite), mConsumer(channel), mSeq(0) { 111 int msgpipe[2]; 112 if (pipe(msgpipe)) { 113 LOGW("could not create pipe: %s", strerror(errno)); 114 mDispatchKeyRead = mDispatchKeyWrite = -1; 115 } else { 116 mDispatchKeyRead = msgpipe[0]; 117 mDispatchKeyWrite = msgpipe[1]; 118 int result = fcntl(mDispatchKeyRead, F_SETFL, O_NONBLOCK); 119 SLOGW_IF(result != 0, "Could not make AInputQueue read pipe " 120 "non-blocking: %s", strerror(errno)); 121 result = fcntl(mDispatchKeyWrite, F_SETFL, O_NONBLOCK); 122 SLOGW_IF(result != 0, "Could not make AInputQueue write pipe " 123 "non-blocking: %s", strerror(errno)); 124 } 125} 126 127AInputQueue::~AInputQueue() { 128 close(mDispatchKeyRead); 129 close(mDispatchKeyWrite); 130} 131 132void AInputQueue::attachLooper(ALooper* looper, int ident, 133 ALooper_callbackFunc callback, void* data) { 134 mLooper = static_cast<android::Looper*>(looper); 135 mLooper->addFd(mConsumer.getChannel()->getReceivePipeFd(), 136 ident, ALOOPER_EVENT_INPUT, callback, data); 137 mLooper->addFd(mDispatchKeyRead, 138 ident, ALOOPER_EVENT_INPUT, callback, data); 139} 140 141void AInputQueue::detachLooper() { 142 mLooper->removeFd(mConsumer.getChannel()->getReceivePipeFd()); 143 mLooper->removeFd(mDispatchKeyRead); 144} 145 146int32_t AInputQueue::hasEvents() { 147 struct pollfd pfd[2]; 148 149 pfd[0].fd = mConsumer.getChannel()->getReceivePipeFd(); 150 pfd[0].events = POLLIN; 151 pfd[0].revents = 0; 152 pfd[1].fd = mDispatchKeyRead; 153 pfd[0].events = POLLIN; 154 pfd[0].revents = 0; 155 156 int nfd = poll(pfd, 2, 0); 157 if (nfd <= 0) return 0; 158 return (pfd[0].revents == POLLIN || pfd[1].revents == POLLIN) ? 1 : -1; 159} 160 161int32_t AInputQueue::getEvent(AInputEvent** outEvent) { 162 *outEvent = NULL; 163 164 bool finishNow = false; 165 166 char byteread; 167 ssize_t nRead = read(mDispatchKeyRead, &byteread, 1); 168 if (nRead == 1) { 169 mLock.lock(); 170 if (mDispatchingKeys.size() > 0) { 171 KeyEvent* kevent = mDispatchingKeys[0]; 172 *outEvent = kevent; 173 mDispatchingKeys.removeAt(0); 174 in_flight_event inflight; 175 inflight.event = kevent; 176 inflight.seq = -1; 177 inflight.doFinish = false; 178 mInFlightEvents.push(inflight); 179 } 180 if (mFinishPreDispatches.size() > 0) { 181 finish_pre_dispatch finish(mFinishPreDispatches[0]); 182 mFinishPreDispatches.removeAt(0); 183 const size_t N = mInFlightEvents.size(); 184 for (size_t i=0; i<N; i++) { 185 const in_flight_event& inflight(mInFlightEvents[i]); 186 if (inflight.seq == finish.seq) { 187 *outEvent = inflight.event; 188 finishNow = finish.handled; 189 } 190 } 191 if (*outEvent == NULL) { 192 LOGW("getEvent couldn't find inflight for seq %d", finish.seq); 193 } 194 } 195 mLock.unlock(); 196 197 if (finishNow) { 198 finishEvent(*outEvent, true, false); 199 *outEvent = NULL; 200 return -1; 201 } else if (*outEvent != NULL) { 202 return 0; 203 } 204 } 205 206 int32_t res = mConsumer.receiveDispatchSignal(); 207 if (res != android::OK) { 208 LOGE("channel '%s' ~ Failed to receive dispatch signal. status=%d", 209 mConsumer.getChannel()->getName().string(), res); 210 return -1; 211 } 212 213 InputEvent* myEvent = NULL; 214 res = mConsumer.consume(this, &myEvent); 215 if (res != android::OK) { 216 LOGW("channel '%s' ~ Failed to consume input event. status=%d", 217 mConsumer.getChannel()->getName().string(), res); 218 mConsumer.sendFinishedSignal(false); 219 return -1; 220 } 221 222 in_flight_event inflight; 223 inflight.event = myEvent; 224 inflight.seq = -1; 225 inflight.doFinish = true; 226 mInFlightEvents.push(inflight); 227 228 *outEvent = myEvent; 229 return 0; 230} 231 232bool AInputQueue::preDispatchEvent(AInputEvent* event) { 233 if (((InputEvent*)event)->getType() != AINPUT_EVENT_TYPE_KEY) { 234 // The IME only cares about key events. 235 return false; 236 } 237 238 // For now we only send system keys to the IME... this avoids having 239 // critical keys like DPAD go through this path. We really need to have 240 // the IME report which keys it wants. 241 if (!((KeyEvent*)event)->isSystemKey()) { 242 return false; 243 } 244 245 return preDispatchKey((KeyEvent*)event); 246} 247 248void AInputQueue::finishEvent(AInputEvent* event, bool handled, bool didDefaultHandling) { 249 LOG_TRACE("finishEvent: %p handled=%d, didDefaultHandling=%d", event, 250 handled ? 1 : 0, didDefaultHandling ? 1 : 0); 251 252 if (!handled && !didDefaultHandling 253 && ((InputEvent*)event)->getType() == AINPUT_EVENT_TYPE_KEY 254 && ((KeyEvent*)event)->hasDefaultAction()) { 255 // The app didn't handle this, but it may have a default action 256 // associated with it. We need to hand this back to Java to be 257 // executed. 258 doUnhandledKey((KeyEvent*)event); 259 return; 260 } 261 262 mLock.lock(); 263 const size_t N = mInFlightEvents.size(); 264 for (size_t i=0; i<N; i++) { 265 const in_flight_event& inflight(mInFlightEvents[i]); 266 if (inflight.event == event) { 267 if (inflight.doFinish) { 268 int32_t res = mConsumer.sendFinishedSignal(handled); 269 if (res != android::OK) { 270 LOGW("Failed to send finished signal on channel '%s'. status=%d", 271 mConsumer.getChannel()->getName().string(), res); 272 } 273 } 274 if (static_cast<InputEvent*>(event)->getType() == AINPUT_EVENT_TYPE_KEY) { 275 mAvailKeyEvents.push(static_cast<KeyEvent*>(event)); 276 } else { 277 mAvailMotionEvents.push(static_cast<MotionEvent*>(event)); 278 } 279 mInFlightEvents.removeAt(i); 280 mLock.unlock(); 281 return; 282 } 283 } 284 mLock.unlock(); 285 286 LOGW("finishEvent called for unknown event: %p", event); 287} 288 289void AInputQueue::dispatchEvent(android::KeyEvent* event) { 290 mLock.lock(); 291 LOG_TRACE("dispatchEvent: dispatching=%d write=%d\n", mDispatchingKeys.size(), 292 mDispatchKeyWrite); 293 mDispatchingKeys.add(event); 294 wakeupDispatch(); 295 mLock.unlock(); 296} 297 298void AInputQueue::finishPreDispatch(int seq, bool handled) { 299 mLock.lock(); 300 LOG_TRACE("finishPreDispatch: seq=%d handled=%d\n", seq, handled ? 1 : 0); 301 finish_pre_dispatch finish; 302 finish.seq = seq; 303 finish.handled = handled; 304 mFinishPreDispatches.add(finish); 305 wakeupDispatch(); 306 mLock.unlock(); 307} 308 309KeyEvent* AInputQueue::consumeUnhandledEvent() { 310 KeyEvent* event = NULL; 311 312 mLock.lock(); 313 if (mUnhandledKeys.size() > 0) { 314 event = mUnhandledKeys[0]; 315 mUnhandledKeys.removeAt(0); 316 } 317 mLock.unlock(); 318 319 LOG_TRACE("consumeUnhandledEvent: KeyEvent=%p", event); 320 321 return event; 322} 323 324KeyEvent* AInputQueue::consumePreDispatchingEvent(int* outSeq) { 325 KeyEvent* event = NULL; 326 327 mLock.lock(); 328 if (mPreDispatchingKeys.size() > 0) { 329 const in_flight_event& inflight(mPreDispatchingKeys[0]); 330 event = static_cast<KeyEvent*>(inflight.event); 331 *outSeq = inflight.seq; 332 mPreDispatchingKeys.removeAt(0); 333 } 334 mLock.unlock(); 335 336 LOG_TRACE("consumePreDispatchingEvent: KeyEvent=%p", event); 337 338 return event; 339} 340 341KeyEvent* AInputQueue::createKeyEvent() { 342 mLock.lock(); 343 KeyEvent* event; 344 if (mAvailKeyEvents.size() <= 0) { 345 event = new KeyEvent(); 346 } else { 347 event = mAvailKeyEvents.top(); 348 mAvailKeyEvents.pop(); 349 } 350 mLock.unlock(); 351 return event; 352} 353 354MotionEvent* AInputQueue::createMotionEvent() { 355 mLock.lock(); 356 MotionEvent* event; 357 if (mAvailMotionEvents.size() <= 0) { 358 event = new MotionEvent(); 359 } else { 360 event = mAvailMotionEvents.top(); 361 mAvailMotionEvents.pop(); 362 } 363 mLock.unlock(); 364 return event; 365} 366 367void AInputQueue::doUnhandledKey(KeyEvent* keyEvent) { 368 mLock.lock(); 369 LOG_TRACE("Unhandled key: pending=%d write=%d\n", mUnhandledKeys.size(), mWorkWrite); 370 if (mUnhandledKeys.size() <= 0 && mWorkWrite >= 0) { 371 write_work(mWorkWrite, CMD_DEF_KEY); 372 } 373 mUnhandledKeys.add(keyEvent); 374 mLock.unlock(); 375} 376 377bool AInputQueue::preDispatchKey(KeyEvent* keyEvent) { 378 mLock.lock(); 379 LOG_TRACE("preDispatch key: pending=%d write=%d\n", mPreDispatchingKeys.size(), mWorkWrite); 380 const size_t N = mInFlightEvents.size(); 381 for (size_t i=0; i<N; i++) { 382 in_flight_event& inflight(mInFlightEvents.editItemAt(i)); 383 if (inflight.event == keyEvent) { 384 if (inflight.seq >= 0) { 385 // This event has already been pre-dispatched! 386 LOG_TRACE("Event already pre-dispatched!"); 387 mLock.unlock(); 388 return false; 389 } 390 mSeq++; 391 if (mSeq < 0) mSeq = 1; 392 inflight.seq = mSeq; 393 394 if (mPreDispatchingKeys.size() <= 0 && mWorkWrite >= 0) { 395 write_work(mWorkWrite, CMD_DEF_KEY); 396 } 397 mPreDispatchingKeys.add(inflight); 398 mLock.unlock(); 399 return true; 400 } 401 } 402 403 LOGW("preDispatchKey called for unknown event: %p", keyEvent); 404 return false; 405} 406 407void AInputQueue::wakeupDispatch() { 408restart: 409 char dummy = 0; 410 int res = write(mDispatchKeyWrite, &dummy, sizeof(dummy)); 411 if (res < 0 && errno == EINTR) { 412 goto restart; 413 } 414 415 if (res == sizeof(dummy)) return; 416 417 if (res < 0) LOGW("Failed writing to dispatch fd: %s", strerror(errno)); 418 else LOGW("Truncated writing to dispatch fd: %d", res); 419} 420 421namespace android { 422 423// ------------------------------------------------------------------------ 424 425/* 426 * Native state for interacting with the NativeActivity class. 427 */ 428struct NativeCode : public ANativeActivity { 429 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { 430 memset((ANativeActivity*)this, 0, sizeof(ANativeActivity)); 431 memset(&callbacks, 0, sizeof(callbacks)); 432 dlhandle = _dlhandle; 433 createActivityFunc = _createFunc; 434 nativeWindow = NULL; 435 inputChannel = NULL; 436 nativeInputQueue = NULL; 437 mainWorkRead = mainWorkWrite = -1; 438 } 439 440 ~NativeCode() { 441 if (callbacks.onDestroy != NULL) { 442 callbacks.onDestroy(this); 443 } 444 if (env != NULL && clazz != NULL) { 445 env->DeleteGlobalRef(clazz); 446 } 447 if (looper != NULL && mainWorkRead >= 0) { 448 looper->removeFd(mainWorkRead); 449 } 450 if (nativeInputQueue != NULL) { 451 nativeInputQueue->mWorkWrite = -1; 452 } 453 setSurface(NULL); 454 setInputChannel(NULL); 455 if (mainWorkRead >= 0) close(mainWorkRead); 456 if (mainWorkWrite >= 0) close(mainWorkWrite); 457 if (dlhandle != NULL) { 458 // for now don't unload... we probably should clean this 459 // up and only keep one open dlhandle per proc, since there 460 // is really no benefit to unloading the code. 461 //dlclose(dlhandle); 462 } 463 } 464 465 void setSurface(jobject _surface) { 466 if (_surface != NULL) { 467 nativeWindow = android_Surface_getNativeWindow(env, _surface); 468 } else { 469 nativeWindow = NULL; 470 } 471 } 472 473 status_t setInputChannel(jobject _channel) { 474 if (inputChannel != NULL) { 475 delete nativeInputQueue; 476 env->DeleteGlobalRef(inputChannel); 477 } 478 inputChannel = NULL; 479 nativeInputQueue = NULL; 480 if (_channel != NULL) { 481 inputChannel = env->NewGlobalRef(_channel); 482 sp<InputChannel> ic = 483 android_view_InputChannel_getInputChannel(env, _channel); 484 if (ic != NULL) { 485 nativeInputQueue = new AInputQueue(ic, mainWorkWrite); 486 if (nativeInputQueue->getConsumer().initialize() != android::OK) { 487 delete nativeInputQueue; 488 nativeInputQueue = NULL; 489 return UNKNOWN_ERROR; 490 } 491 } else { 492 return UNKNOWN_ERROR; 493 } 494 } 495 return OK; 496 } 497 498 ANativeActivityCallbacks callbacks; 499 500 void* dlhandle; 501 ANativeActivity_createFunc* createActivityFunc; 502 503 String8 internalDataPathObj; 504 String8 externalDataPathObj; 505 String8 obbPathObj; 506 507 sp<ANativeWindow> nativeWindow; 508 int32_t lastWindowWidth; 509 int32_t lastWindowHeight; 510 511 jobject inputChannel; 512 struct AInputQueue* nativeInputQueue; 513 514 // These are used to wake up the main thread to process work. 515 int mainWorkRead; 516 int mainWorkWrite; 517 sp<Looper> looper; 518}; 519 520void android_NativeActivity_finish(ANativeActivity* activity) { 521 NativeCode* code = static_cast<NativeCode*>(activity); 522 write_work(code->mainWorkWrite, CMD_FINISH, 0); 523} 524 525void android_NativeActivity_setWindowFormat( 526 ANativeActivity* activity, int32_t format) { 527 NativeCode* code = static_cast<NativeCode*>(activity); 528 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format); 529} 530 531void android_NativeActivity_setWindowFlags( 532 ANativeActivity* activity, int32_t values, int32_t mask) { 533 NativeCode* code = static_cast<NativeCode*>(activity); 534 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); 535} 536 537void android_NativeActivity_showSoftInput( 538 ANativeActivity* activity, int32_t flags) { 539 NativeCode* code = static_cast<NativeCode*>(activity); 540 write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags); 541} 542 543void android_NativeActivity_hideSoftInput( 544 ANativeActivity* activity, int32_t flags) { 545 NativeCode* code = static_cast<NativeCode*>(activity); 546 write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags); 547} 548 549// ------------------------------------------------------------------------ 550 551static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { 552 if (env->ExceptionCheck()) { 553 LOGE("An exception was thrown by callback '%s'.", methodName); 554 LOGE_EX(env); 555 env->ExceptionClear(); 556 return true; 557 } 558 return false; 559} 560 561/* 562 * Callback for handling native events on the application's main thread. 563 */ 564static int mainWorkCallback(int fd, int events, void* data) { 565 NativeCode* code = (NativeCode*)data; 566 if ((events & POLLIN) == 0) { 567 return 1; 568 } 569 570 ActivityWork work; 571 if (!read_work(code->mainWorkRead, &work)) { 572 return 1; 573 } 574 575 LOG_TRACE("mainWorkCallback: cmd=%d", work.cmd); 576 577 switch (work.cmd) { 578 case CMD_DEF_KEY: { 579 KeyEvent* keyEvent; 580 while ((keyEvent=code->nativeInputQueue->consumeUnhandledEvent()) != NULL) { 581 jobject inputEventObj = android_view_KeyEvent_fromNative( 582 code->env, keyEvent); 583 jboolean handled; 584 if (inputEventObj) { 585 handled = code->env->CallBooleanMethod(code->clazz, 586 gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); 587 checkAndClearExceptionFromCallback(code->env, "dispatchUnhandledKeyEvent"); 588 code->env->DeleteLocalRef(inputEventObj); 589 } else { 590 LOGE("Failed to obtain key event for dispatchUnhandledKeyEvent."); 591 handled = false; 592 } 593 code->nativeInputQueue->finishEvent(keyEvent, handled, true); 594 } 595 int seq; 596 while ((keyEvent=code->nativeInputQueue->consumePreDispatchingEvent(&seq)) != NULL) { 597 jobject inputEventObj = android_view_KeyEvent_fromNative( 598 code->env, keyEvent); 599 if (inputEventObj) { 600 code->env->CallVoidMethod(code->clazz, 601 gNativeActivityClassInfo.preDispatchKeyEvent, inputEventObj, seq); 602 checkAndClearExceptionFromCallback(code->env, "preDispatchKeyEvent"); 603 code->env->DeleteLocalRef(inputEventObj); 604 } else { 605 LOGE("Failed to obtain key event for preDispatchKeyEvent."); 606 } 607 } 608 } break; 609 case CMD_FINISH: { 610 code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish); 611 checkAndClearExceptionFromCallback(code->env, "finish"); 612 } break; 613 case CMD_SET_WINDOW_FORMAT: { 614 code->env->CallVoidMethod(code->clazz, 615 gNativeActivityClassInfo.setWindowFormat, work.arg1); 616 checkAndClearExceptionFromCallback(code->env, "setWindowFormat"); 617 } break; 618 case CMD_SET_WINDOW_FLAGS: { 619 code->env->CallVoidMethod(code->clazz, 620 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); 621 checkAndClearExceptionFromCallback(code->env, "setWindowFlags"); 622 } break; 623 case CMD_SHOW_SOFT_INPUT: { 624 code->env->CallVoidMethod(code->clazz, 625 gNativeActivityClassInfo.showIme, work.arg1); 626 checkAndClearExceptionFromCallback(code->env, "showIme"); 627 } break; 628 case CMD_HIDE_SOFT_INPUT: { 629 code->env->CallVoidMethod(code->clazz, 630 gNativeActivityClassInfo.hideIme, work.arg1); 631 checkAndClearExceptionFromCallback(code->env, "hideIme"); 632 } break; 633 default: 634 LOGW("Unknown work command: %d", work.cmd); 635 break; 636 } 637 638 return 1; 639} 640 641// ------------------------------------------------------------------------ 642 643static jint 644loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName, 645 jobject messageQueue, jstring internalDataDir, jstring obbDir, 646 jstring externalDataDir, int sdkVersion, 647 jobject jAssetMgr, jbyteArray savedState) 648{ 649 LOG_TRACE("loadNativeCode_native"); 650 651 const char* pathStr = env->GetStringUTFChars(path, NULL); 652 NativeCode* code = NULL; 653 654 void* handle = dlopen(pathStr, RTLD_LAZY); 655 656 env->ReleaseStringUTFChars(path, pathStr); 657 658 if (handle != NULL) { 659 const char* funcStr = env->GetStringUTFChars(funcName, NULL); 660 code = new NativeCode(handle, (ANativeActivity_createFunc*) 661 dlsym(handle, funcStr)); 662 env->ReleaseStringUTFChars(funcName, funcStr); 663 664 if (code->createActivityFunc == NULL) { 665 LOGW("ANativeActivity_onCreate not found"); 666 delete code; 667 return 0; 668 } 669 670 code->looper = android_os_MessageQueue_getLooper(env, messageQueue); 671 if (code->looper == NULL) { 672 LOGW("Unable to retrieve MessageQueue's Looper"); 673 delete code; 674 return 0; 675 } 676 677 int msgpipe[2]; 678 if (pipe(msgpipe)) { 679 LOGW("could not create pipe: %s", strerror(errno)); 680 delete code; 681 return 0; 682 } 683 code->mainWorkRead = msgpipe[0]; 684 code->mainWorkWrite = msgpipe[1]; 685 int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK); 686 SLOGW_IF(result != 0, "Could not make main work read pipe " 687 "non-blocking: %s", strerror(errno)); 688 result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK); 689 SLOGW_IF(result != 0, "Could not make main work write pipe " 690 "non-blocking: %s", strerror(errno)); 691 code->looper->addFd(code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code); 692 693 code->ANativeActivity::callbacks = &code->callbacks; 694 if (env->GetJavaVM(&code->vm) < 0) { 695 LOGW("NativeActivity GetJavaVM failed"); 696 delete code; 697 return 0; 698 } 699 code->env = env; 700 code->clazz = env->NewGlobalRef(clazz); 701 702 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); 703 code->internalDataPathObj = dirStr; 704 code->internalDataPath = code->internalDataPathObj.string(); 705 env->ReleaseStringUTFChars(internalDataDir, dirStr); 706 707 dirStr = env->GetStringUTFChars(externalDataDir, NULL); 708 code->externalDataPathObj = dirStr; 709 code->externalDataPath = code->externalDataPathObj.string(); 710 env->ReleaseStringUTFChars(externalDataDir, dirStr); 711 712 code->sdkVersion = sdkVersion; 713 714 code->assetManager = assetManagerForJavaObject(env, jAssetMgr); 715 716 dirStr = env->GetStringUTFChars(obbDir, NULL); 717 code->obbPathObj = dirStr; 718 code->obbPath = code->obbPathObj.string(); 719 env->ReleaseStringUTFChars(obbDir, dirStr); 720 721 jbyte* rawSavedState = NULL; 722 jsize rawSavedSize = 0; 723 if (savedState != NULL) { 724 rawSavedState = env->GetByteArrayElements(savedState, NULL); 725 rawSavedSize = env->GetArrayLength(savedState); 726 } 727 728 code->createActivityFunc(code, rawSavedState, rawSavedSize); 729 730 if (rawSavedState != NULL) { 731 env->ReleaseByteArrayElements(savedState, rawSavedState, 0); 732 } 733 } 734 735 return (jint)code; 736} 737 738static void 739unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) 740{ 741 LOG_TRACE("unloadNativeCode_native"); 742 if (handle != 0) { 743 NativeCode* code = (NativeCode*)handle; 744 delete code; 745 } 746} 747 748static void 749onStart_native(JNIEnv* env, jobject clazz, jint handle) 750{ 751 LOG_TRACE("onStart_native"); 752 if (handle != 0) { 753 NativeCode* code = (NativeCode*)handle; 754 if (code->callbacks.onStart != NULL) { 755 code->callbacks.onStart(code); 756 } 757 } 758} 759 760static void 761onResume_native(JNIEnv* env, jobject clazz, jint handle) 762{ 763 LOG_TRACE("onResume_native"); 764 if (handle != 0) { 765 NativeCode* code = (NativeCode*)handle; 766 if (code->callbacks.onResume != NULL) { 767 code->callbacks.onResume(code); 768 } 769 } 770} 771 772static jbyteArray 773onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) 774{ 775 LOG_TRACE("onSaveInstanceState_native"); 776 777 jbyteArray array = NULL; 778 779 if (handle != 0) { 780 NativeCode* code = (NativeCode*)handle; 781 if (code->callbacks.onSaveInstanceState != NULL) { 782 size_t len = 0; 783 jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len); 784 if (len > 0) { 785 array = env->NewByteArray(len); 786 if (array != NULL) { 787 env->SetByteArrayRegion(array, 0, len, state); 788 } 789 } 790 if (state != NULL) { 791 free(state); 792 } 793 } 794 } 795 796 return array; 797} 798 799static void 800onPause_native(JNIEnv* env, jobject clazz, jint handle) 801{ 802 LOG_TRACE("onPause_native"); 803 if (handle != 0) { 804 NativeCode* code = (NativeCode*)handle; 805 if (code->callbacks.onPause != NULL) { 806 code->callbacks.onPause(code); 807 } 808 } 809} 810 811static void 812onStop_native(JNIEnv* env, jobject clazz, jint handle) 813{ 814 LOG_TRACE("onStop_native"); 815 if (handle != 0) { 816 NativeCode* code = (NativeCode*)handle; 817 if (code->callbacks.onStop != NULL) { 818 code->callbacks.onStop(code); 819 } 820 } 821} 822 823static void 824onConfigurationChanged_native(JNIEnv* env, jobject clazz, jint handle) 825{ 826 LOG_TRACE("onConfigurationChanged_native"); 827 if (handle != 0) { 828 NativeCode* code = (NativeCode*)handle; 829 if (code->callbacks.onConfigurationChanged != NULL) { 830 code->callbacks.onConfigurationChanged(code); 831 } 832 } 833} 834 835static void 836onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) 837{ 838 LOG_TRACE("onLowMemory_native"); 839 if (handle != 0) { 840 NativeCode* code = (NativeCode*)handle; 841 if (code->callbacks.onLowMemory != NULL) { 842 code->callbacks.onLowMemory(code); 843 } 844 } 845} 846 847static void 848onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) 849{ 850 LOG_TRACE("onWindowFocusChanged_native"); 851 if (handle != 0) { 852 NativeCode* code = (NativeCode*)handle; 853 if (code->callbacks.onWindowFocusChanged != NULL) { 854 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); 855 } 856 } 857} 858 859static void 860onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 861{ 862 LOG_TRACE("onSurfaceCreated_native"); 863 if (handle != 0) { 864 NativeCode* code = (NativeCode*)handle; 865 code->setSurface(surface); 866 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { 867 code->callbacks.onNativeWindowCreated(code, 868 code->nativeWindow.get()); 869 } 870 } 871} 872 873static int32_t getWindowProp(ANativeWindow* window, int what) { 874 int value; 875 int res = window->query(window, what, &value); 876 return res < 0 ? res : value; 877} 878 879static void 880onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, 881 jint format, jint width, jint height) 882{ 883 LOG_TRACE("onSurfaceChanged_native"); 884 if (handle != 0) { 885 NativeCode* code = (NativeCode*)handle; 886 sp<ANativeWindow> oldNativeWindow = code->nativeWindow; 887 code->setSurface(surface); 888 if (oldNativeWindow != code->nativeWindow) { 889 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 890 code->callbacks.onNativeWindowDestroyed(code, 891 oldNativeWindow.get()); 892 } 893 if (code->nativeWindow != NULL) { 894 if (code->callbacks.onNativeWindowCreated != NULL) { 895 code->callbacks.onNativeWindowCreated(code, 896 code->nativeWindow.get()); 897 } 898 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(), 899 NATIVE_WINDOW_WIDTH); 900 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(), 901 NATIVE_WINDOW_HEIGHT); 902 } 903 } else { 904 // Maybe it resized? 905 int32_t newWidth = getWindowProp(code->nativeWindow.get(), 906 NATIVE_WINDOW_WIDTH); 907 int32_t newHeight = getWindowProp(code->nativeWindow.get(), 908 NATIVE_WINDOW_HEIGHT); 909 if (newWidth != code->lastWindowWidth 910 || newHeight != code->lastWindowHeight) { 911 if (code->callbacks.onNativeWindowResized != NULL) { 912 code->callbacks.onNativeWindowResized(code, 913 code->nativeWindow.get()); 914 } 915 } 916 } 917 } 918} 919 920static void 921onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jint handle) 922{ 923 LOG_TRACE("onSurfaceRedrawNeeded_native"); 924 if (handle != 0) { 925 NativeCode* code = (NativeCode*)handle; 926 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) { 927 code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get()); 928 } 929 } 930} 931 932static void 933onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 934{ 935 LOG_TRACE("onSurfaceDestroyed_native"); 936 if (handle != 0) { 937 NativeCode* code = (NativeCode*)handle; 938 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 939 code->callbacks.onNativeWindowDestroyed(code, 940 code->nativeWindow.get()); 941 } 942 code->setSurface(NULL); 943 } 944} 945 946static void 947onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 948{ 949 LOG_TRACE("onInputChannelCreated_native"); 950 if (handle != 0) { 951 NativeCode* code = (NativeCode*)handle; 952 status_t err = code->setInputChannel(channel); 953 if (err != OK) { 954 jniThrowException(env, "java/lang/IllegalStateException", 955 "Error setting input channel"); 956 return; 957 } 958 if (code->callbacks.onInputQueueCreated != NULL) { 959 code->callbacks.onInputQueueCreated(code, 960 code->nativeInputQueue); 961 } 962 } 963} 964 965static void 966onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 967{ 968 LOG_TRACE("onInputChannelDestroyed_native"); 969 if (handle != 0) { 970 NativeCode* code = (NativeCode*)handle; 971 if (code->nativeInputQueue != NULL 972 && code->callbacks.onInputQueueDestroyed != NULL) { 973 code->callbacks.onInputQueueDestroyed(code, 974 code->nativeInputQueue); 975 } 976 code->setInputChannel(NULL); 977 } 978} 979 980static void 981onContentRectChanged_native(JNIEnv* env, jobject clazz, jint handle, 982 jint x, jint y, jint w, jint h) 983{ 984 LOG_TRACE("onContentRectChanged_native"); 985 if (handle != 0) { 986 NativeCode* code = (NativeCode*)handle; 987 if (code->callbacks.onContentRectChanged != NULL) { 988 ARect rect; 989 rect.left = x; 990 rect.top = y; 991 rect.right = x+w; 992 rect.bottom = y+h; 993 code->callbacks.onContentRectChanged(code, &rect); 994 } 995 } 996} 997 998static void 999dispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, jobject eventObj) 1000{ 1001 LOG_TRACE("dispatchKeyEvent_native"); 1002 if (handle != 0) { 1003 NativeCode* code = (NativeCode*)handle; 1004 if (code->nativeInputQueue != NULL) { 1005 KeyEvent* event = code->nativeInputQueue->createKeyEvent(); 1006 status_t status = android_view_KeyEvent_toNative(env, eventObj, event); 1007 if (status) { 1008 delete event; 1009 jniThrowRuntimeException(env, "Could not read contents of KeyEvent object."); 1010 return; 1011 } 1012 code->nativeInputQueue->dispatchEvent(event); 1013 } 1014 } 1015} 1016 1017static void 1018finishPreDispatchKeyEvent_native(JNIEnv* env, jobject clazz, jint handle, 1019 jint seq, jboolean handled) 1020{ 1021 LOG_TRACE("finishPreDispatchKeyEvent_native"); 1022 if (handle != 0) { 1023 NativeCode* code = (NativeCode*)handle; 1024 if (code->nativeInputQueue != NULL) { 1025 code->nativeInputQueue->finishPreDispatch(seq, handled ? true : false); 1026 } 1027 } 1028} 1029 1030static const JNINativeMethod g_methods[] = { 1031 { "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", 1032 (void*)loadNativeCode_native }, 1033 { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, 1034 { "onStartNative", "(I)V", (void*)onStart_native }, 1035 { "onResumeNative", "(I)V", (void*)onResume_native }, 1036 { "onSaveInstanceStateNative", "(I)[B", (void*)onSaveInstanceState_native }, 1037 { "onPauseNative", "(I)V", (void*)onPause_native }, 1038 { "onStopNative", "(I)V", (void*)onStop_native }, 1039 { "onConfigurationChangedNative", "(I)V", (void*)onConfigurationChanged_native }, 1040 { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, 1041 { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, 1042 { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, 1043 { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, 1044 { "onSurfaceRedrawNeededNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native }, 1045 { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, 1046 { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, 1047 { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, 1048 { "onContentRectChangedNative", "(IIIII)V", (void*)onContentRectChanged_native }, 1049 { "dispatchKeyEventNative", "(ILandroid/view/KeyEvent;)V", (void*)dispatchKeyEvent_native }, 1050 { "finishPreDispatchKeyEventNative", "(IIZ)V", (void*)finishPreDispatchKeyEvent_native }, 1051}; 1052 1053static const char* const kNativeActivityPathName = "android/app/NativeActivity"; 1054 1055#define FIND_CLASS(var, className) \ 1056 var = env->FindClass(className); \ 1057 LOG_FATAL_IF(! var, "Unable to find class %s", className); \ 1058 var = jclass(env->NewGlobalRef(var)); 1059 1060#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 1061 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 1062 LOG_FATAL_IF(! var, "Unable to find method" methodName); 1063 1064int register_android_app_NativeActivity(JNIEnv* env) 1065{ 1066 //LOGD("register_android_app_NativeActivity"); 1067 1068 FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); 1069 1070 GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, 1071 gNativeActivityClassInfo.clazz, 1072 "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)Z"); 1073 GET_METHOD_ID(gNativeActivityClassInfo.preDispatchKeyEvent, 1074 gNativeActivityClassInfo.clazz, 1075 "preDispatchKeyEvent", "(Landroid/view/KeyEvent;I)V"); 1076 1077 GET_METHOD_ID(gNativeActivityClassInfo.finish, 1078 gNativeActivityClassInfo.clazz, 1079 "finish", "()V"); 1080 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, 1081 gNativeActivityClassInfo.clazz, 1082 "setWindowFlags", "(II)V"); 1083 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, 1084 gNativeActivityClassInfo.clazz, 1085 "setWindowFormat", "(I)V"); 1086 GET_METHOD_ID(gNativeActivityClassInfo.showIme, 1087 gNativeActivityClassInfo.clazz, 1088 "showIme", "(I)V"); 1089 GET_METHOD_ID(gNativeActivityClassInfo.hideIme, 1090 gNativeActivityClassInfo.clazz, 1091 "hideIme", "(I)V"); 1092 1093 return AndroidRuntime::registerNativeMethods( 1094 env, kNativeActivityPathName, 1095 g_methods, NELEM(g_methods)); 1096} 1097 1098} // namespace android 1099