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