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