android_app_NativeActivity.cpp revision 289b9b62372ef52a06113b83dfb870e2c2fb325a
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 23#include <android_runtime/AndroidRuntime.h> 24#include <android_runtime/android_view_Surface.h> 25#include <android_runtime/android_app_NativeActivity.h> 26#include <surfaceflinger/Surface.h> 27#include <ui/egl/android_natives.h> 28#include <ui/InputTransport.h> 29#include <utils/PollLoop.h> 30 31#include "JNIHelp.h" 32#include "android_os_MessageQueue.h" 33#include "android_view_InputChannel.h" 34#include "android_view_KeyEvent.h" 35 36namespace android 37{ 38 39static struct { 40 jclass clazz; 41 42 jmethodID dispatchUnhandledKeyEvent; 43 jmethodID setWindowFlags; 44 jmethodID setWindowFormat; 45} gNativeActivityClassInfo; 46 47// ------------------------------------------------------------------------ 48 49struct ActivityWork { 50 int32_t cmd; 51 int32_t arg1; 52 int32_t arg2; 53}; 54 55enum { 56 CMD_DEF_KEY = 1, 57 CMD_SET_WINDOW_FORMAT, 58 CMD_SET_WINDOW_FLAGS, 59}; 60 61static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) { 62 ActivityWork work; 63 work.cmd = cmd; 64 work.arg1 = arg1; 65 work.arg2 = arg2; 66 67restart: 68 int res = write(fd, &work, sizeof(work)); 69 if (res < 0 && errno == EINTR) { 70 goto restart; 71 } 72 73 if (res == sizeof(work)) return; 74 75 if (res < 0) LOGW("Failed writing to work fd: %s", strerror(errno)); 76 else LOGW("Truncated writing to work fd: %d", res); 77} 78 79static bool read_work(int fd, ActivityWork* outWork) { 80 int res = read(fd, outWork, sizeof(ActivityWork)); 81 // no need to worry about EINTR, poll loop will just come back again. 82 if (res == sizeof(ActivityWork)) return true; 83 84 if (res < 0) LOGW("Failed reading work fd: %s", strerror(errno)); 85 else LOGW("Truncated reading work fd: %d", res); 86 return false; 87} 88 89// ------------------------------------------------------------------------ 90 91/* 92 * Specialized input queue that allows unhandled key events to be dispatched 93 * back to the native activity's Java framework code. 94 */ 95struct MyInputQueue : AInputQueue { 96 explicit MyInputQueue(const android::sp<android::InputChannel>& channel, int workWrite) 97 : AInputQueue(channel), mWorkWrite(workWrite) { 98 } 99 100 virtual void doDefaultKey(android::KeyEvent* keyEvent) { 101 mLock.lock(); 102 LOGI("Default key: pending=%d write=%d\n", mPendingKeys.size(), mWorkWrite); 103 if (mPendingKeys.size() <= 0 && mWorkWrite >= 0) { 104 write_work(mWorkWrite, CMD_DEF_KEY); 105 } 106 mPendingKeys.add(keyEvent); 107 mLock.unlock(); 108 } 109 110 KeyEvent* getNextEvent() { 111 KeyEvent* event = NULL; 112 113 mLock.lock(); 114 if (mPendingKeys.size() > 0) { 115 event = mPendingKeys[0]; 116 mPendingKeys.removeAt(0); 117 } 118 mLock.unlock(); 119 120 return event; 121 } 122 123 int mWorkWrite; 124 125 Mutex mLock; 126 Vector<KeyEvent*> mPendingKeys; 127}; 128 129// ------------------------------------------------------------------------ 130 131/* 132 * Native state for interacting with the NativeActivity class. 133 */ 134struct NativeCode : public ANativeActivity { 135 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) { 136 memset((ANativeActivity*)this, sizeof(ANativeActivity), 0); 137 memset(&callbacks, sizeof(callbacks), 0); 138 dlhandle = _dlhandle; 139 createActivityFunc = _createFunc; 140 nativeWindow = NULL; 141 inputChannel = NULL; 142 nativeInputQueue = NULL; 143 mainWorkRead = mainWorkWrite = -1; 144 } 145 146 ~NativeCode() { 147 if (callbacks.onDestroy != NULL) { 148 callbacks.onDestroy(this); 149 } 150 if (env != NULL && clazz != NULL) { 151 env->DeleteGlobalRef(clazz); 152 } 153 if (pollLoop != NULL && mainWorkRead >= 0) { 154 pollLoop->removeCallback(mainWorkRead); 155 } 156 if (nativeInputQueue != NULL) { 157 nativeInputQueue->mWorkWrite = -1; 158 } 159 setSurface(NULL); 160 setInputChannel(NULL); 161 if (mainWorkRead >= 0) close(mainWorkRead); 162 if (mainWorkWrite >= 0) close(mainWorkWrite); 163 if (dlhandle != NULL) { 164 // for now don't unload... we probably should clean this 165 // up and only keep one open dlhandle per proc, since there 166 // is really no benefit to unloading the code. 167 //dlclose(dlhandle); 168 } 169 } 170 171 void setSurface(jobject _surface) { 172 if (_surface != NULL) { 173 nativeWindow = android_Surface_getNativeWindow(env, _surface); 174 } else { 175 nativeWindow = NULL; 176 } 177 } 178 179 status_t setInputChannel(jobject _channel) { 180 if (inputChannel != NULL) { 181 delete nativeInputQueue; 182 env->DeleteGlobalRef(inputChannel); 183 } 184 inputChannel = NULL; 185 nativeInputQueue = NULL; 186 if (_channel != NULL) { 187 inputChannel = env->NewGlobalRef(_channel); 188 sp<InputChannel> ic = 189 android_view_InputChannel_getInputChannel(env, _channel); 190 if (ic != NULL) { 191 nativeInputQueue = new MyInputQueue(ic, mainWorkWrite); 192 if (nativeInputQueue->getConsumer().initialize() != android::OK) { 193 delete nativeInputQueue; 194 nativeInputQueue = NULL; 195 return UNKNOWN_ERROR; 196 } 197 } else { 198 return UNKNOWN_ERROR; 199 } 200 } 201 return OK; 202 } 203 204 ANativeActivityCallbacks callbacks; 205 206 void* dlhandle; 207 ANativeActivity_createFunc* createActivityFunc; 208 209 String8 internalDataPath; 210 String8 externalDataPath; 211 212 sp<ANativeWindow> nativeWindow; 213 jobject inputChannel; 214 struct MyInputQueue* nativeInputQueue; 215 216 // These are used to wake up the main thread to process work. 217 int mainWorkRead; 218 int mainWorkWrite; 219 sp<PollLoop> pollLoop; 220}; 221 222void android_NativeActivity_setWindowFormat( 223 ANativeActivity* activity, int32_t format) { 224 NativeCode* code = static_cast<NativeCode*>(activity); 225 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format); 226} 227 228void android_NativeActivity_setWindowFlags( 229 ANativeActivity* activity, int32_t values, int32_t mask) { 230 NativeCode* code = static_cast<NativeCode*>(activity); 231 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask); 232} 233 234// ------------------------------------------------------------------------ 235 236/* 237 * Callback for handling native events on the application's main thread. 238 */ 239static bool mainWorkCallback(int fd, int events, void* data) { 240 NativeCode* code = (NativeCode*)data; 241 if ((events & POLLIN) == 0) { 242 return true; 243 } 244 245 ActivityWork work; 246 if (!read_work(code->mainWorkRead, &work)) { 247 return true; 248 } 249 switch (work.cmd) { 250 case CMD_DEF_KEY: { 251 KeyEvent* keyEvent; 252 while ((keyEvent=code->nativeInputQueue->getNextEvent()) != NULL) { 253 jobject inputEventObj = android_view_KeyEvent_fromNative( 254 code->env, keyEvent); 255 code->env->CallVoidMethod(code->clazz, 256 gNativeActivityClassInfo.dispatchUnhandledKeyEvent, inputEventObj); 257 int32_t res = code->nativeInputQueue->getConsumer().sendFinishedSignal(); 258 if (res != OK) { 259 LOGW("Failed to send finished signal on channel '%s'. status=%d", 260 code->nativeInputQueue->getConsumer().getChannel()->getName().string(), res); 261 } 262 } 263 } break; 264 case CMD_SET_WINDOW_FORMAT: { 265 code->env->CallVoidMethod(code->clazz, 266 gNativeActivityClassInfo.setWindowFormat, work.arg1); 267 } break; 268 case CMD_SET_WINDOW_FLAGS: { 269 code->env->CallVoidMethod(code->clazz, 270 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2); 271 } break; 272 default: 273 LOGW("Unknown work command: %d", work.cmd); 274 break; 275 } 276 277 return true; 278} 279 280// ------------------------------------------------------------------------ 281 282static jint 283loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jobject messageQueue, 284 jstring internalDataDir, jstring externalDataDir, int sdkVersion) 285{ 286 const char* pathStr = env->GetStringUTFChars(path, NULL); 287 NativeCode* code = NULL; 288 289 void* handle = dlopen(pathStr, RTLD_LAZY); 290 291 env->ReleaseStringUTFChars(path, pathStr); 292 293 if (handle != NULL) { 294 code = new NativeCode(handle, (ANativeActivity_createFunc*) 295 dlsym(handle, "ANativeActivity_onCreate")); 296 if (code->createActivityFunc == NULL) { 297 LOGW("ANativeActivity_onCreate not found"); 298 delete code; 299 return 0; 300 } 301 302 code->pollLoop = android_os_MessageQueue_getPollLoop(env, messageQueue); 303 if (code->pollLoop == NULL) { 304 LOGW("Unable to retrieve MessageQueue's PollLoop"); 305 delete code; 306 return 0; 307 } 308 309 int msgpipe[2]; 310 if (pipe(msgpipe)) { 311 LOGW("could not create pipe: %s", strerror(errno)); 312 delete code; 313 return 0; 314 } 315 code->mainWorkRead = msgpipe[0]; 316 code->mainWorkWrite = msgpipe[1]; 317 code->pollLoop->setCallback(code->mainWorkRead, POLLIN, mainWorkCallback, code); 318 319 code->ANativeActivity::callbacks = &code->callbacks; 320 if (env->GetJavaVM(&code->vm) < 0) { 321 LOGW("NativeActivity GetJavaVM failed"); 322 delete code; 323 return 0; 324 } 325 code->env = env; 326 code->clazz = env->NewGlobalRef(clazz); 327 328 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL); 329 code->internalDataPath = dirStr; 330 code->internalDataPath = code->internalDataPath.string(); 331 env->ReleaseStringUTFChars(path, dirStr); 332 333 dirStr = env->GetStringUTFChars(externalDataDir, NULL); 334 code->externalDataPath = dirStr; 335 code->externalDataPath = code->externalDataPath.string(); 336 env->ReleaseStringUTFChars(path, dirStr); 337 338 code->sdkVersion = sdkVersion; 339 340 code->createActivityFunc(code, NULL, 0); 341 } 342 343 return (jint)code; 344} 345 346static void 347unloadNativeCode_native(JNIEnv* env, jobject clazz, jint handle) 348{ 349 if (handle != 0) { 350 NativeCode* code = (NativeCode*)handle; 351 delete code; 352 } 353} 354 355static void 356onStart_native(JNIEnv* env, jobject clazz, jint handle) 357{ 358 if (handle != 0) { 359 NativeCode* code = (NativeCode*)handle; 360 if (code->callbacks.onStart != NULL) { 361 code->callbacks.onStart(code); 362 } 363 } 364} 365 366static void 367onResume_native(JNIEnv* env, jobject clazz, jint handle) 368{ 369 if (handle != 0) { 370 NativeCode* code = (NativeCode*)handle; 371 if (code->callbacks.onResume != NULL) { 372 code->callbacks.onResume(code); 373 } 374 } 375} 376 377static void 378onSaveInstanceState_native(JNIEnv* env, jobject clazz, jint handle) 379{ 380 if (handle != 0) { 381 NativeCode* code = (NativeCode*)handle; 382 if (code->callbacks.onSaveInstanceState != NULL) { 383 size_t len = 0; 384 code->callbacks.onSaveInstanceState(code, &len); 385 } 386 } 387} 388 389static void 390onPause_native(JNIEnv* env, jobject clazz, jint handle) 391{ 392 if (handle != 0) { 393 NativeCode* code = (NativeCode*)handle; 394 if (code->callbacks.onPause != NULL) { 395 code->callbacks.onPause(code); 396 } 397 } 398} 399 400static void 401onStop_native(JNIEnv* env, jobject clazz, jint handle) 402{ 403 if (handle != 0) { 404 NativeCode* code = (NativeCode*)handle; 405 if (code->callbacks.onStop != NULL) { 406 code->callbacks.onStop(code); 407 } 408 } 409} 410 411static void 412onLowMemory_native(JNIEnv* env, jobject clazz, jint handle) 413{ 414 if (handle != 0) { 415 NativeCode* code = (NativeCode*)handle; 416 if (code->callbacks.onLowMemory != NULL) { 417 code->callbacks.onLowMemory(code); 418 } 419 } 420} 421 422static void 423onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jint handle, jboolean focused) 424{ 425 if (handle != 0) { 426 NativeCode* code = (NativeCode*)handle; 427 if (code->callbacks.onWindowFocusChanged != NULL) { 428 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0); 429 } 430 } 431} 432 433static void 434onSurfaceCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 435{ 436 if (handle != 0) { 437 NativeCode* code = (NativeCode*)handle; 438 code->setSurface(surface); 439 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { 440 code->callbacks.onNativeWindowCreated(code, 441 code->nativeWindow.get()); 442 } 443 } 444} 445 446static void 447onSurfaceChanged_native(JNIEnv* env, jobject clazz, jint handle, jobject surface, 448 jint format, jint width, jint height) 449{ 450 if (handle != 0) { 451 NativeCode* code = (NativeCode*)handle; 452 sp<ANativeWindow> oldNativeWindow = code->nativeWindow; 453 code->setSurface(surface); 454 if (oldNativeWindow != code->nativeWindow) { 455 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 456 code->callbacks.onNativeWindowDestroyed(code, 457 oldNativeWindow.get()); 458 } 459 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) { 460 code->callbacks.onNativeWindowCreated(code, 461 code->nativeWindow.get()); 462 } 463 } 464 } 465} 466 467static void 468onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject surface) 469{ 470 if (handle != 0) { 471 NativeCode* code = (NativeCode*)handle; 472 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) { 473 code->callbacks.onNativeWindowDestroyed(code, 474 code->nativeWindow.get()); 475 } 476 code->setSurface(NULL); 477 } 478} 479 480static void 481onInputChannelCreated_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 482{ 483 if (handle != 0) { 484 NativeCode* code = (NativeCode*)handle; 485 status_t err = code->setInputChannel(channel); 486 if (err != OK) { 487 jniThrowException(env, "java/lang/IllegalStateException", 488 "Error setting input channel"); 489 return; 490 } 491 if (code->callbacks.onInputQueueCreated != NULL) { 492 code->callbacks.onInputQueueCreated(code, 493 code->nativeInputQueue); 494 } 495 } 496} 497 498static void 499onInputChannelDestroyed_native(JNIEnv* env, jobject clazz, jint handle, jobject channel) 500{ 501 if (handle != 0) { 502 NativeCode* code = (NativeCode*)handle; 503 if (code->nativeInputQueue != NULL 504 && code->callbacks.onInputQueueDestroyed != NULL) { 505 code->callbacks.onInputQueueDestroyed(code, 506 code->nativeInputQueue); 507 } 508 code->setInputChannel(NULL); 509 } 510} 511 512static const JNINativeMethod g_methods[] = { 513 { "loadNativeCode", "(Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;I)I", 514 (void*)loadNativeCode_native }, 515 { "unloadNativeCode", "(I)V", (void*)unloadNativeCode_native }, 516 { "onStartNative", "(I)V", (void*)onStart_native }, 517 { "onResumeNative", "(I)V", (void*)onResume_native }, 518 { "onSaveInstanceStateNative", "(I)V", (void*)onSaveInstanceState_native }, 519 { "onPauseNative", "(I)V", (void*)onPause_native }, 520 { "onStopNative", "(I)V", (void*)onStop_native }, 521 { "onLowMemoryNative", "(I)V", (void*)onLowMemory_native }, 522 { "onWindowFocusChangedNative", "(IZ)V", (void*)onWindowFocusChanged_native }, 523 { "onSurfaceCreatedNative", "(ILandroid/view/Surface;)V", (void*)onSurfaceCreated_native }, 524 { "onSurfaceChangedNative", "(ILandroid/view/Surface;III)V", (void*)onSurfaceChanged_native }, 525 { "onSurfaceDestroyedNative", "(I)V", (void*)onSurfaceDestroyed_native }, 526 { "onInputChannelCreatedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelCreated_native }, 527 { "onInputChannelDestroyedNative", "(ILandroid/view/InputChannel;)V", (void*)onInputChannelDestroyed_native }, 528}; 529 530static const char* const kNativeActivityPathName = "android/app/NativeActivity"; 531 532#define FIND_CLASS(var, className) \ 533 var = env->FindClass(className); \ 534 LOG_FATAL_IF(! var, "Unable to find class " className); \ 535 var = jclass(env->NewGlobalRef(var)); 536 537#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 538 var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 539 LOG_FATAL_IF(! var, "Unable to find method" methodName); 540 541int register_android_app_NativeActivity(JNIEnv* env) 542{ 543 //LOGD("register_android_app_NativeActivity"); 544 545 FIND_CLASS(gNativeActivityClassInfo.clazz, kNativeActivityPathName); 546 547 GET_METHOD_ID(gNativeActivityClassInfo.dispatchUnhandledKeyEvent, 548 gNativeActivityClassInfo.clazz, 549 "dispatchUnhandledKeyEvent", "(Landroid/view/KeyEvent;)V"); 550 551 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFlags, 552 gNativeActivityClassInfo.clazz, 553 "setWindowFlags", "(II)V"); 554 GET_METHOD_ID(gNativeActivityClassInfo.setWindowFormat, 555 gNativeActivityClassInfo.clazz, 556 "setWindowFormat", "(I)V"); 557 558 return AndroidRuntime::registerNativeMethods( 559 env, kNativeActivityPathName, 560 g_methods, NELEM(g_methods)); 561} 562 563} // namespace android 564