2 * Copyright (C) 2010 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #define LOG_TAG "NativeActivity"
18 #include <utils/Log.h>
24 #include <android_runtime/android_app_NativeActivity.h>
25 #include <android_runtime/android_util_AssetManager.h>
26 #include <android_runtime/android_view_Surface.h>
27 #include <android_runtime/AndroidRuntime.h>
28 #include <input/InputTransport.h>
30 #include <gui/Surface.h>
32 #include <system/window.h>
34 #include <utils/Looper.h>
37 #include "android_os_MessageQueue.h"
38 #include "android_view_InputChannel.h"
39 #include "android_view_KeyEvent.h"
41 #include "nativebridge/native_bridge.h"
43 #include "core_jni_helpers.h"
46 #define LOG_TRACE(...)
47 //#define LOG_TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
52 static const bool kLogTrace = false;
56 jmethodID setWindowFlags;
57 jmethodID setWindowFormat;
60 } gNativeActivityClassInfo;
62 // ------------------------------------------------------------------------
72 CMD_SET_WINDOW_FORMAT,
78 static void write_work(int fd, int32_t cmd, int32_t arg1=0, int32_t arg2=0) {
85 ALOGD("write_work: cmd=%d", cmd);
89 int res = write(fd, &work, sizeof(work));
90 if (res < 0 && errno == EINTR) {
94 if (res == sizeof(work)) return;
96 if (res < 0) ALOGW("Failed writing to work fd: %s", strerror(errno));
97 else ALOGW("Truncated writing to work fd: %d", res);
100 static bool read_work(int fd, ActivityWork* outWork) {
101 int res = read(fd, outWork, sizeof(ActivityWork));
102 // no need to worry about EINTR, poll loop will just come back again.
103 if (res == sizeof(ActivityWork)) return true;
105 if (res < 0) ALOGW("Failed reading work fd: %s", strerror(errno));
106 else ALOGW("Truncated reading work fd: %d", res);
111 * Native state for interacting with the NativeActivity class.
113 struct NativeCode : public ANativeActivity {
114 NativeCode(void* _dlhandle, ANativeActivity_createFunc* _createFunc) {
115 memset((ANativeActivity*)this, 0, sizeof(ANativeActivity));
116 memset(&callbacks, 0, sizeof(callbacks));
117 dlhandle = _dlhandle;
118 createActivityFunc = _createFunc;
120 mainWorkRead = mainWorkWrite = -1;
124 if (callbacks.onDestroy != NULL) {
125 callbacks.onDestroy(this);
127 if (env != NULL && clazz != NULL) {
128 env->DeleteGlobalRef(clazz);
130 if (messageQueue != NULL && mainWorkRead >= 0) {
131 messageQueue->getLooper()->removeFd(mainWorkRead);
134 if (mainWorkRead >= 0) close(mainWorkRead);
135 if (mainWorkWrite >= 0) close(mainWorkWrite);
136 if (dlhandle != NULL) {
137 // for now don't unload... we probably should clean this
138 // up and only keep one open dlhandle per proc, since there
139 // is really no benefit to unloading the code.
144 void setSurface(jobject _surface) {
145 if (_surface != NULL) {
146 nativeWindow = android_view_Surface_getNativeWindow(env, _surface);
152 ANativeActivityCallbacks callbacks;
155 ANativeActivity_createFunc* createActivityFunc;
157 String8 internalDataPathObj;
158 String8 externalDataPathObj;
161 sp<ANativeWindow> nativeWindow;
162 int32_t lastWindowWidth;
163 int32_t lastWindowHeight;
165 // These are used to wake up the main thread to process work.
168 sp<MessageQueue> messageQueue;
171 void android_NativeActivity_finish(ANativeActivity* activity) {
172 NativeCode* code = static_cast<NativeCode*>(activity);
173 write_work(code->mainWorkWrite, CMD_FINISH, 0);
176 void android_NativeActivity_setWindowFormat(
177 ANativeActivity* activity, int32_t format) {
178 NativeCode* code = static_cast<NativeCode*>(activity);
179 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FORMAT, format);
182 void android_NativeActivity_setWindowFlags(
183 ANativeActivity* activity, int32_t values, int32_t mask) {
184 NativeCode* code = static_cast<NativeCode*>(activity);
185 write_work(code->mainWorkWrite, CMD_SET_WINDOW_FLAGS, values, mask);
188 void android_NativeActivity_showSoftInput(
189 ANativeActivity* activity, int32_t flags) {
190 NativeCode* code = static_cast<NativeCode*>(activity);
191 write_work(code->mainWorkWrite, CMD_SHOW_SOFT_INPUT, flags);
194 void android_NativeActivity_hideSoftInput(
195 ANativeActivity* activity, int32_t flags) {
196 NativeCode* code = static_cast<NativeCode*>(activity);
197 write_work(code->mainWorkWrite, CMD_HIDE_SOFT_INPUT, flags);
200 // ------------------------------------------------------------------------
203 * Callback for handling native events on the application's main thread.
205 static int mainWorkCallback(int fd, int events, void* data) {
206 NativeCode* code = (NativeCode*)data;
207 if ((events & POLLIN) == 0) {
212 if (!read_work(code->mainWorkRead, &work)) {
217 ALOGD("mainWorkCallback: cmd=%d", work.cmd);
222 code->env->CallVoidMethod(code->clazz, gNativeActivityClassInfo.finish);
223 code->messageQueue->raiseAndClearException(code->env, "finish");
225 case CMD_SET_WINDOW_FORMAT: {
226 code->env->CallVoidMethod(code->clazz,
227 gNativeActivityClassInfo.setWindowFormat, work.arg1);
228 code->messageQueue->raiseAndClearException(code->env, "setWindowFormat");
230 case CMD_SET_WINDOW_FLAGS: {
231 code->env->CallVoidMethod(code->clazz,
232 gNativeActivityClassInfo.setWindowFlags, work.arg1, work.arg2);
233 code->messageQueue->raiseAndClearException(code->env, "setWindowFlags");
235 case CMD_SHOW_SOFT_INPUT: {
236 code->env->CallVoidMethod(code->clazz,
237 gNativeActivityClassInfo.showIme, work.arg1);
238 code->messageQueue->raiseAndClearException(code->env, "showIme");
240 case CMD_HIDE_SOFT_INPUT: {
241 code->env->CallVoidMethod(code->clazz,
242 gNativeActivityClassInfo.hideIme, work.arg1);
243 code->messageQueue->raiseAndClearException(code->env, "hideIme");
246 ALOGW("Unknown work command: %d", work.cmd);
253 // ------------------------------------------------------------------------
256 loadNativeCode_native(JNIEnv* env, jobject clazz, jstring path, jstring funcName,
257 jobject messageQueue, jstring internalDataDir, jstring obbDir,
258 jstring externalDataDir, jint sdkVersion,
259 jobject jAssetMgr, jbyteArray savedState)
262 ALOGD("loadNativeCode_native");
265 const char* pathStr = env->GetStringUTFChars(path, NULL);
266 NativeCode* code = NULL;
267 bool needNativeBridge = false;
269 void* handle = dlopen(pathStr, RTLD_LAZY);
270 if (handle == NULL) {
271 if (NativeBridgeIsSupported(pathStr)) {
272 handle = NativeBridgeLoadLibrary(pathStr, RTLD_LAZY);
273 needNativeBridge = true;
276 env->ReleaseStringUTFChars(path, pathStr);
278 if (handle != NULL) {
279 void* funcPtr = NULL;
280 const char* funcStr = env->GetStringUTFChars(funcName, NULL);
281 if (needNativeBridge) {
282 funcPtr = NativeBridgeGetTrampoline(handle, funcStr, NULL, 0);
284 funcPtr = dlsym(handle, funcStr);
287 code = new NativeCode(handle, (ANativeActivity_createFunc*)funcPtr);
288 env->ReleaseStringUTFChars(funcName, funcStr);
290 if (code->createActivityFunc == NULL) {
291 ALOGW("ANativeActivity_onCreate not found");
296 code->messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueue);
297 if (code->messageQueue == NULL) {
298 ALOGW("Unable to retrieve native MessageQueue");
305 ALOGW("could not create pipe: %s", strerror(errno));
309 code->mainWorkRead = msgpipe[0];
310 code->mainWorkWrite = msgpipe[1];
311 int result = fcntl(code->mainWorkRead, F_SETFL, O_NONBLOCK);
312 SLOGW_IF(result != 0, "Could not make main work read pipe "
313 "non-blocking: %s", strerror(errno));
314 result = fcntl(code->mainWorkWrite, F_SETFL, O_NONBLOCK);
315 SLOGW_IF(result != 0, "Could not make main work write pipe "
316 "non-blocking: %s", strerror(errno));
317 code->messageQueue->getLooper()->addFd(
318 code->mainWorkRead, 0, ALOOPER_EVENT_INPUT, mainWorkCallback, code);
320 code->ANativeActivity::callbacks = &code->callbacks;
321 if (env->GetJavaVM(&code->vm) < 0) {
322 ALOGW("NativeActivity GetJavaVM failed");
327 code->clazz = env->NewGlobalRef(clazz);
329 const char* dirStr = env->GetStringUTFChars(internalDataDir, NULL);
330 code->internalDataPathObj = dirStr;
331 code->internalDataPath = code->internalDataPathObj.string();
332 env->ReleaseStringUTFChars(internalDataDir, dirStr);
334 if (externalDataDir != NULL) {
335 dirStr = env->GetStringUTFChars(externalDataDir, NULL);
336 code->externalDataPathObj = dirStr;
337 env->ReleaseStringUTFChars(externalDataDir, dirStr);
339 code->externalDataPath = code->externalDataPathObj.string();
341 code->sdkVersion = sdkVersion;
343 code->assetManager = assetManagerForJavaObject(env, jAssetMgr);
345 if (obbDir != NULL) {
346 dirStr = env->GetStringUTFChars(obbDir, NULL);
347 code->obbPathObj = dirStr;
348 env->ReleaseStringUTFChars(obbDir, dirStr);
350 code->obbPath = code->obbPathObj.string();
352 jbyte* rawSavedState = NULL;
353 jsize rawSavedSize = 0;
354 if (savedState != NULL) {
355 rawSavedState = env->GetByteArrayElements(savedState, NULL);
356 rawSavedSize = env->GetArrayLength(savedState);
359 code->createActivityFunc(code, rawSavedState, rawSavedSize);
361 if (rawSavedState != NULL) {
362 env->ReleaseByteArrayElements(savedState, rawSavedState, 0);
370 unloadNativeCode_native(JNIEnv* env, jobject clazz, jlong handle)
373 ALOGD("unloadNativeCode_native");
376 NativeCode* code = (NativeCode*)handle;
382 onStart_native(JNIEnv* env, jobject clazz, jlong handle)
385 ALOGD("onStart_native");
388 NativeCode* code = (NativeCode*)handle;
389 if (code->callbacks.onStart != NULL) {
390 code->callbacks.onStart(code);
396 onResume_native(JNIEnv* env, jobject clazz, jlong handle)
399 ALOGD("onResume_native");
402 NativeCode* code = (NativeCode*)handle;
403 if (code->callbacks.onResume != NULL) {
404 code->callbacks.onResume(code);
410 onSaveInstanceState_native(JNIEnv* env, jobject clazz, jlong handle)
413 ALOGD("onSaveInstanceState_native");
416 jbyteArray array = NULL;
419 NativeCode* code = (NativeCode*)handle;
420 if (code->callbacks.onSaveInstanceState != NULL) {
422 jbyte* state = (jbyte*)code->callbacks.onSaveInstanceState(code, &len);
424 array = env->NewByteArray(len);
426 env->SetByteArrayRegion(array, 0, len, state);
439 onPause_native(JNIEnv* env, jobject clazz, jlong handle)
442 ALOGD("onPause_native");
445 NativeCode* code = (NativeCode*)handle;
446 if (code->callbacks.onPause != NULL) {
447 code->callbacks.onPause(code);
453 onStop_native(JNIEnv* env, jobject clazz, jlong handle)
456 ALOGD("onStop_native");
459 NativeCode* code = (NativeCode*)handle;
460 if (code->callbacks.onStop != NULL) {
461 code->callbacks.onStop(code);
467 onConfigurationChanged_native(JNIEnv* env, jobject clazz, jlong handle)
470 ALOGD("onConfigurationChanged_native");
473 NativeCode* code = (NativeCode*)handle;
474 if (code->callbacks.onConfigurationChanged != NULL) {
475 code->callbacks.onConfigurationChanged(code);
481 onLowMemory_native(JNIEnv* env, jobject clazz, jlong handle)
484 ALOGD("onLowMemory_native");
487 NativeCode* code = (NativeCode*)handle;
488 if (code->callbacks.onLowMemory != NULL) {
489 code->callbacks.onLowMemory(code);
495 onWindowFocusChanged_native(JNIEnv* env, jobject clazz, jlong handle, jboolean focused)
498 ALOGD("onWindowFocusChanged_native");
501 NativeCode* code = (NativeCode*)handle;
502 if (code->callbacks.onWindowFocusChanged != NULL) {
503 code->callbacks.onWindowFocusChanged(code, focused ? 1 : 0);
509 onSurfaceCreated_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface)
512 ALOGD("onSurfaceCreated_native");
515 NativeCode* code = (NativeCode*)handle;
516 code->setSurface(surface);
517 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowCreated != NULL) {
518 code->callbacks.onNativeWindowCreated(code,
519 code->nativeWindow.get());
524 static int32_t getWindowProp(ANativeWindow* window, int what) {
526 int res = window->query(window, what, &value);
527 return res < 0 ? res : value;
531 onSurfaceChanged_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface,
532 jint format, jint width, jint height)
535 ALOGD("onSurfaceChanged_native");
538 NativeCode* code = (NativeCode*)handle;
539 sp<ANativeWindow> oldNativeWindow = code->nativeWindow;
540 code->setSurface(surface);
541 if (oldNativeWindow != code->nativeWindow) {
542 if (oldNativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
543 code->callbacks.onNativeWindowDestroyed(code,
544 oldNativeWindow.get());
546 if (code->nativeWindow != NULL) {
547 if (code->callbacks.onNativeWindowCreated != NULL) {
548 code->callbacks.onNativeWindowCreated(code,
549 code->nativeWindow.get());
551 code->lastWindowWidth = getWindowProp(code->nativeWindow.get(),
552 NATIVE_WINDOW_WIDTH);
553 code->lastWindowHeight = getWindowProp(code->nativeWindow.get(),
554 NATIVE_WINDOW_HEIGHT);
558 int32_t newWidth = getWindowProp(code->nativeWindow.get(),
559 NATIVE_WINDOW_WIDTH);
560 int32_t newHeight = getWindowProp(code->nativeWindow.get(),
561 NATIVE_WINDOW_HEIGHT);
562 if (newWidth != code->lastWindowWidth
563 || newHeight != code->lastWindowHeight) {
564 if (code->callbacks.onNativeWindowResized != NULL) {
565 code->callbacks.onNativeWindowResized(code,
566 code->nativeWindow.get());
574 onSurfaceRedrawNeeded_native(JNIEnv* env, jobject clazz, jlong handle)
577 ALOGD("onSurfaceRedrawNeeded_native");
580 NativeCode* code = (NativeCode*)handle;
581 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowRedrawNeeded != NULL) {
582 code->callbacks.onNativeWindowRedrawNeeded(code, code->nativeWindow.get());
588 onSurfaceDestroyed_native(JNIEnv* env, jobject clazz, jlong handle, jobject surface)
591 ALOGD("onSurfaceDestroyed_native");
594 NativeCode* code = (NativeCode*)handle;
595 if (code->nativeWindow != NULL && code->callbacks.onNativeWindowDestroyed != NULL) {
596 code->callbacks.onNativeWindowDestroyed(code,
597 code->nativeWindow.get());
599 code->setSurface(NULL);
604 onInputQueueCreated_native(JNIEnv* env, jobject clazz, jlong handle, jlong queuePtr)
607 ALOGD("onInputChannelCreated_native");
610 NativeCode* code = (NativeCode*)handle;
611 if (code->callbacks.onInputQueueCreated != NULL) {
612 AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
613 code->callbacks.onInputQueueCreated(code, queue);
619 onInputQueueDestroyed_native(JNIEnv* env, jobject clazz, jlong handle, jlong queuePtr)
622 ALOGD("onInputChannelDestroyed_native");
625 NativeCode* code = (NativeCode*)handle;
626 if (code->callbacks.onInputQueueDestroyed != NULL) {
627 AInputQueue* queue = reinterpret_cast<AInputQueue*>(queuePtr);
628 code->callbacks.onInputQueueDestroyed(code, queue);
634 onContentRectChanged_native(JNIEnv* env, jobject clazz, jlong handle,
635 jint x, jint y, jint w, jint h)
638 ALOGD("onContentRectChanged_native");
641 NativeCode* code = (NativeCode*)handle;
642 if (code->callbacks.onContentRectChanged != NULL) {
648 code->callbacks.onContentRectChanged(code, &rect);
653 static const JNINativeMethod g_methods[] = {
654 { "loadNativeCode", "(Ljava/lang/String;Ljava/lang/String;Landroid/os/MessageQueue;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILandroid/content/res/AssetManager;[B)J",
655 (void*)loadNativeCode_native },
656 { "unloadNativeCode", "(J)V", (void*)unloadNativeCode_native },
657 { "onStartNative", "(J)V", (void*)onStart_native },
658 { "onResumeNative", "(J)V", (void*)onResume_native },
659 { "onSaveInstanceStateNative", "(J)[B", (void*)onSaveInstanceState_native },
660 { "onPauseNative", "(J)V", (void*)onPause_native },
661 { "onStopNative", "(J)V", (void*)onStop_native },
662 { "onConfigurationChangedNative", "(J)V", (void*)onConfigurationChanged_native },
663 { "onLowMemoryNative", "(J)V", (void*)onLowMemory_native },
664 { "onWindowFocusChangedNative", "(JZ)V", (void*)onWindowFocusChanged_native },
665 { "onSurfaceCreatedNative", "(JLandroid/view/Surface;)V", (void*)onSurfaceCreated_native },
666 { "onSurfaceChangedNative", "(JLandroid/view/Surface;III)V", (void*)onSurfaceChanged_native },
667 { "onSurfaceRedrawNeededNative", "(JLandroid/view/Surface;)V", (void*)onSurfaceRedrawNeeded_native },
668 { "onSurfaceDestroyedNative", "(J)V", (void*)onSurfaceDestroyed_native },
669 { "onInputQueueCreatedNative", "(JJ)V",
670 (void*)onInputQueueCreated_native },
671 { "onInputQueueDestroyedNative", "(JJ)V",
672 (void*)onInputQueueDestroyed_native },
673 { "onContentRectChangedNative", "(JIIII)V", (void*)onContentRectChanged_native },
676 static const char* const kNativeActivityPathName = "android/app/NativeActivity";
678 int register_android_app_NativeActivity(JNIEnv* env)
680 //ALOGD("register_android_app_NativeActivity");
681 jclass clazz = FindClassOrDie(env, kNativeActivityPathName);
683 gNativeActivityClassInfo.finish = GetMethodIDOrDie(env, clazz, "finish", "()V");
684 gNativeActivityClassInfo.setWindowFlags = GetMethodIDOrDie(env, clazz, "setWindowFlags",
686 gNativeActivityClassInfo.setWindowFormat = GetMethodIDOrDie(env, clazz, "setWindowFormat",
688 gNativeActivityClassInfo.showIme = GetMethodIDOrDie(env, clazz, "showIme", "(I)V");
689 gNativeActivityClassInfo.hideIme = GetMethodIDOrDie(env, clazz, "hideIme", "(I)V");
691 return RegisterMethodsOrDie(env, kNativeActivityPathName, g_methods, NELEM(g_methods));
694 } // namespace android