diff --git a/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a b/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a deleted file mode 100644 index d3073db8f..000000000 Binary files a/ndk/platforms/android-9/arch-arm/usr/lib/libthreaded_app.a and /dev/null differ diff --git a/ndk/samples/native-activity/jni/Android.mk b/ndk/samples/native-activity/jni/Android.mk index b1afdf5f2..60a7fdcb8 100644 --- a/ndk/samples/native-activity/jni/Android.mk +++ b/ndk/samples/native-activity/jni/Android.mk @@ -18,6 +18,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := native-activity LOCAL_SRC_FILES := main.c glutils.c -LOCAL_LDLIBS := -lthreaded_app -llog -landroid -lEGL -lGLESv1_CM +LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM +LOCAL_STATIC_LIBRARIES := android_native_app_glue include $(BUILD_SHARED_LIBRARY) + +$(call import-module,android/native_app_glue) diff --git a/ndk/samples/native-activity/jni/main.c b/ndk/samples/native-activity/jni/main.c index f11c26ef7..c52c95997 100644 --- a/ndk/samples/native-activity/jni/main.c +++ b/ndk/samples/native-activity/jni/main.c @@ -19,13 +19,13 @@ #include -#include +#include #include "glutils.h" struct engine { struct android_app* app; - + int animating; EGLDisplay display; EGLSurface surface; @@ -69,13 +69,13 @@ static int engine_init_display(struct engine* engine) { engine->width = w; engine->height = h; engine->angle = 0; - + // Initialize GL state. glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); glEnable(GL_CULL_FACE); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); - + return 0; } @@ -84,7 +84,7 @@ static void engine_draw_frame(struct engine* engine) { // No display. return; } - + glClearColor(((float)engine->x)/engine->width, engine->angle, ((float)engine->y)/engine->height, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -108,7 +108,7 @@ static void engine_draw_frame(struct engine* engine) { #endif eglSwapBuffers(engine->display, engine->surface); - + //engine->angle += 1.2f; } @@ -144,7 +144,7 @@ static int engine_do_ui_event(struct engine* engine) { } else { LOGI("Failure reading next input event: %s\n", strerror(errno)); } - + return 1; } @@ -169,19 +169,19 @@ static int32_t engine_do_main_cmd(struct engine* engine) { res = android_app_exec_cmd(engine->app, cmd); break; } - + return res; } void android_main(struct android_app* state) { struct engine engine; - + memset(&engine, 0, sizeof(engine)); state->userData = &engine; engine.app = state; - + // loop waiting for stuff to do. - + while (1) { // Read all pending events. int fd; @@ -201,7 +201,7 @@ void android_main(struct android_app* state) { break; } } - + if (engine.animating) { // Done with events; draw next animation frame. engine.angle += .01f; diff --git a/ndk/samples/native-plasma/jni/Android.mk b/ndk/samples/native-plasma/jni/Android.mk index 34e85a1f5..f78df901b 100644 --- a/ndk/samples/native-plasma/jni/Android.mk +++ b/ndk/samples/native-plasma/jni/Android.mk @@ -18,6 +18,9 @@ include $(CLEAR_VARS) LOCAL_MODULE := native-plasma LOCAL_SRC_FILES := plasma.c -LOCAL_LDLIBS := -lthreaded_app -lm -llog -landroid +LOCAL_LDLIBS := -lm -llog -landroid +LOCAL_STATIC_LIBRARIES := android_native_app_glue include $(BUILD_SHARED_LIBRARY) + +$(call import-module,android/native_app_glue) diff --git a/ndk/samples/native-plasma/jni/plasma.c b/ndk/samples/native-plasma/jni/plasma.c index dcc8f6fdf..0ef9788ae 100644 --- a/ndk/samples/native-plasma/jni/plasma.c +++ b/ndk/samples/native-plasma/jni/plasma.c @@ -15,7 +15,7 @@ * */ -#include +#include #include #include @@ -374,9 +374,9 @@ stats_endFrame( Stats* s ) struct engine { struct android_app* app; - + Stats stats; - + int animating; }; @@ -385,20 +385,20 @@ static void engine_draw_frame(struct engine* engine) { // No window. return; } - + ANativeWindow_Buffer buffer; if (ANativeWindow_lock(engine->app->window, &buffer, NULL) < 0) { LOGW("Unable to lock window buffer"); return; } - + stats_startFrame(&engine->stats); struct timespec t; t.tv_sec = t.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &t); int64_t time_ms = (((int64_t)t.tv_sec)*1000000000LL + t.tv_nsec)/1000000; - + /* Now fill the values with a nice little plasma */ fill_plasma(&buffer, time_ms); @@ -429,7 +429,7 @@ static int engine_do_ui_event(struct engine* engine) { } else { LOGI("Failure reading next input event: %s\n", strerror(errno)); } - + return 1; } @@ -453,7 +453,7 @@ static int32_t engine_do_main_cmd(struct engine* engine) { res = android_app_exec_cmd(engine->app, cmd); break; } - + return res; } @@ -461,20 +461,20 @@ void android_main(struct android_app* state) { static int init; struct engine engine; - + memset(&engine, 0, sizeof(engine)); state->userData = &engine; engine.app = state; - + if (!init) { init_tables(); init = 1; } - + stats_init(&engine.stats); - + // loop waiting for stuff to do. - + while (1) { // Read all pending events. int fd; @@ -494,7 +494,7 @@ void android_main(struct android_app* state) { break; } } - + if (engine.animating) { engine_draw_frame(&engine); } diff --git a/ndk/sources/android/native_app_glue/Android.mk b/ndk/sources/android/native_app_glue/Android.mk new file mode 100644 index 000000000..00252fcb0 --- /dev/null +++ b/ndk/sources/android/native_app_glue/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE:= android_native_app_glue +LOCAL_SRC_FILES:= android_native_app_glue.c +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH) +LOCAL_EXPORT_LDLIBS := -llog + +include $(BUILD_STATIC_LIBRARY) diff --git a/ndk/sources/android/native_app_glue/android_native_app_glue.c b/ndk/sources/android/native_app_glue/android_native_app_glue.c new file mode 100644 index 000000000..d7a839171 --- /dev/null +++ b/ndk/sources/android/native_app_glue/android_native_app_glue.c @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "threaded_app", __VA_ARGS__)) + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + return cmd; + } else { + LOGW("No data on command pipe!"); + } + return -1; +} + +int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGI("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGI("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, NULL, (void*)LOOPER_ID_EVENT); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_WINDOW_CHANGED: + LOGI("APP_CMD_WINDOW_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_START: + case APP_CMD_RESUME: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGI("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_DESTROY: + LOGI("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } + + return android_app->destroyRequested ? 0 : 1; +} + +static void android_app_destroy(struct android_app* android_app) { + LOGI("android_app_destroy!"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, POLLIN, NULL, (void*)LOOPER_ID_MAIN); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGI("could not create pipe: %s", strerror(errno)); + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingWindow = window; + android_app_write_cmd(android_app, APP_CMD_WINDOW_CHANGED); + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGI("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGI("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGI("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + LOGI("SaveInstanceState: %p\n", activity); + return NULL; +} + +static void onPause(ANativeActivity* activity) { + LOGI("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGI("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onLowMemory(ANativeActivity* activity) { + LOGI("LowMemory: %p\n", activity); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGI("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity); +} diff --git a/ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h b/ndk/sources/android/native_app_glue/android_native_app_glue.h similarity index 71% rename from ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h rename to ndk/sources/android/native_app_glue/android_native_app_glue.h index 2b58e9c2d..e30e9603e 100644 --- a/ndk/platforms/android-9/arch-arm/usr/include/android_glue/threaded_app.h +++ b/ndk/sources/android/native_app_glue/android_native_app_glue.h @@ -15,6 +15,9 @@ * */ +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + #include #include #include @@ -22,6 +25,60 @@ #include #include +#ifdef __cplusplus +extern "C" +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'threaded_native_app' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper callback that returns a "data" + * value of LOOPER_ID_MAIN and LOOPER_ID_EVENT, respectively. + * + * Your application can use the same ALooper to listen to additionnal + * file-descriptors. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN event from the ALooper, your + * code should call the function android_app_read_cmd() to read the + * command value and act upon it. This is normally done by calling + * android_app_exec_cmd() directly. + * + * XXX: MAKE THIS STUFF MORE CLEAR !! + * + * 5/ Whenever you receive a LOOPER_ID_EVENT event from the ALooper, you + * should read one event from the AInputQueue with AInputQueue_getEvent(). + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. + * + */ + /** * This is the interface for the standard glue code of a threaded * application. In this model, the application's code is running @@ -195,3 +252,9 @@ int32_t android_app_exec_cmd(struct android_app* android_app, int8_t cmd); * the main entry to the app. */ extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */