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