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.
23 #include <sys/resource.h>
25 #include "android_native_app_glue.h"
26 #include <android/log.h>
28 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
30 static void free_saved_state(struct android_app* android_app) {
31 pthread_mutex_lock(&android_app->mutex);
32 if (android_app->savedState != NULL) {
33 free(android_app->savedState);
34 android_app->savedState = NULL;
35 android_app->savedStateSize = 0;
37 pthread_mutex_unlock(&android_app->mutex);
40 int8_t android_app_read_cmd(struct android_app* android_app) {
42 if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
44 case APP_CMD_SAVE_STATE:
45 free_saved_state(android_app);
50 LOGI("No data on command pipe!");
55 static void print_cur_config(struct android_app* android_app) {
56 char lang[2], country[2];
57 AConfiguration_getLanguage(android_app->config, lang);
58 AConfiguration_getCountry(android_app->config, country);
60 LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
61 "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
62 "modetype=%d modenight=%d",
63 AConfiguration_getMcc(android_app->config),
64 AConfiguration_getMnc(android_app->config),
65 lang[0], lang[1], country[0], country[1],
66 AConfiguration_getOrientation(android_app->config),
67 AConfiguration_getTouchscreen(android_app->config),
68 AConfiguration_getDensity(android_app->config),
69 AConfiguration_getKeyboard(android_app->config),
70 AConfiguration_getNavigation(android_app->config),
71 AConfiguration_getKeysHidden(android_app->config),
72 AConfiguration_getNavHidden(android_app->config),
73 AConfiguration_getSdkVersion(android_app->config),
74 AConfiguration_getScreenSize(android_app->config),
75 AConfiguration_getScreenLong(android_app->config),
76 AConfiguration_getUiModeType(android_app->config),
77 AConfiguration_getUiModeNight(android_app->config));
80 void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
82 case APP_CMD_INPUT_CHANGED:
83 LOGI("APP_CMD_INPUT_CHANGED\n");
84 pthread_mutex_lock(&android_app->mutex);
85 if (android_app->inputQueue != NULL) {
86 AInputQueue_detachLooper(android_app->inputQueue);
88 android_app->inputQueue = android_app->pendingInputQueue;
89 if (android_app->inputQueue != NULL) {
90 LOGI("Attaching input queue to looper");
91 AInputQueue_attachLooper(android_app->inputQueue,
92 android_app->looper, LOOPER_ID_INPUT, NULL,
93 &android_app->inputPollSource);
95 pthread_cond_broadcast(&android_app->cond);
96 pthread_mutex_unlock(&android_app->mutex);
99 case APP_CMD_INIT_WINDOW:
100 LOGI("APP_CMD_INIT_WINDOW\n");
101 pthread_mutex_lock(&android_app->mutex);
102 android_app->window = android_app->pendingWindow;
103 pthread_cond_broadcast(&android_app->cond);
104 pthread_mutex_unlock(&android_app->mutex);
107 case APP_CMD_TERM_WINDOW:
108 LOGI("APP_CMD_TERM_WINDOW\n");
109 pthread_cond_broadcast(&android_app->cond);
116 LOGI("activityState=%d\n", cmd);
117 pthread_mutex_lock(&android_app->mutex);
118 android_app->activityState = cmd;
119 pthread_cond_broadcast(&android_app->cond);
120 pthread_mutex_unlock(&android_app->mutex);
123 case APP_CMD_CONFIG_CHANGED:
124 LOGI("APP_CMD_CONFIG_CHANGED\n");
125 AConfiguration_fromAssetManager(android_app->config,
126 android_app->activity->assetManager);
127 print_cur_config(android_app);
130 case APP_CMD_DESTROY:
131 LOGI("APP_CMD_DESTROY\n");
132 android_app->destroyRequested = 1;
137 void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
139 case APP_CMD_TERM_WINDOW:
140 LOGI("APP_CMD_TERM_WINDOW\n");
141 pthread_mutex_lock(&android_app->mutex);
142 android_app->window = NULL;
143 pthread_cond_broadcast(&android_app->cond);
144 pthread_mutex_unlock(&android_app->mutex);
147 case APP_CMD_SAVE_STATE:
148 LOGI("APP_CMD_SAVE_STATE\n");
149 pthread_mutex_lock(&android_app->mutex);
150 android_app->stateSaved = 1;
151 pthread_cond_broadcast(&android_app->cond);
152 pthread_mutex_unlock(&android_app->mutex);
156 free_saved_state(android_app);
165 static void android_app_destroy(struct android_app* android_app) {
166 LOGI("android_app_destroy!");
167 free_saved_state(android_app);
168 pthread_mutex_lock(&android_app->mutex);
169 if (android_app->inputQueue != NULL) {
170 AInputQueue_detachLooper(android_app->inputQueue);
172 AConfiguration_delete(android_app->config);
173 android_app->destroyed = 1;
174 pthread_cond_broadcast(&android_app->cond);
175 pthread_mutex_unlock(&android_app->mutex);
176 /* // Can't touch android_app object after this. */
179 static void process_input(struct android_app* app, struct android_poll_source* source) {
180 AInputEvent* event = NULL;
181 if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
182 LOGI("New input event: type=%d\n", AInputEvent_getType(event));
183 if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
188 if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
189 AInputQueue_finishEvent(app->inputQueue, event, handled);
192 LOGI("Failure reading next input event: %s\n", strerror(errno));
196 static void process_cmd(struct android_app* app, struct android_poll_source* source) {
197 int8_t cmd = android_app_read_cmd(app);
198 android_app_pre_exec_cmd(app, cmd);
199 if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
200 android_app_post_exec_cmd(app, cmd);
203 static void* android_app_entry(void* param) {
204 struct android_app* android_app = (struct android_app*)param;
207 android_app->config = AConfiguration_new();
208 AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
210 print_cur_config(android_app);
212 android_app->cmdPollSource.id = LOOPER_ID_MAIN;
213 android_app->cmdPollSource.app = android_app;
214 android_app->cmdPollSource.process = process_cmd;
215 android_app->inputPollSource.id = LOOPER_ID_INPUT;
216 android_app->inputPollSource.app = android_app;
217 android_app->inputPollSource.process = process_input;
219 looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
220 ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
221 &android_app->cmdPollSource);
222 android_app->looper = looper;
224 pthread_mutex_lock(&android_app->mutex);
225 android_app->running = 1;
226 pthread_cond_broadcast(&android_app->cond);
227 pthread_mutex_unlock(&android_app->mutex);
229 android_main(android_app);
231 android_app_destroy(android_app);
235 /* // -------------------------------------------------------------------- */
236 /* // Native activity interaction (called from main thread) */
237 /* // -------------------------------------------------------------------- */
239 static struct android_app* android_app_create(ANativeActivity* activity,
240 void* savedState, size_t savedStateSize) {
241 struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
244 memset(android_app, 0, sizeof(struct android_app));
245 android_app->activity = activity;
247 pthread_mutex_init(&android_app->mutex, NULL);
248 pthread_cond_init(&android_app->cond, NULL);
250 if (savedState != NULL) {
251 android_app->savedState = malloc(savedStateSize);
252 android_app->savedStateSize = savedStateSize;
253 memcpy(android_app->savedState, savedState, savedStateSize);
257 LOGI("could not create pipe: %s", strerror(errno));
259 android_app->msgread = msgpipe[0];
260 android_app->msgwrite = msgpipe[1];
262 pthread_attr_init(&attr);
263 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
264 pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
266 /* // Wait for thread to start. */
267 pthread_mutex_lock(&android_app->mutex);
268 while (!android_app->running) {
269 pthread_cond_wait(&android_app->cond, &android_app->mutex);
271 pthread_mutex_unlock(&android_app->mutex);
276 /* static */void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
277 if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
278 LOGI("Failure writing android_app cmd: %s\n", strerror(errno));
282 static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
283 pthread_mutex_lock(&android_app->mutex);
284 android_app->pendingInputQueue = inputQueue;
285 android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
286 while (android_app->inputQueue != android_app->pendingInputQueue) {
287 pthread_cond_wait(&android_app->cond, &android_app->mutex);
289 pthread_mutex_unlock(&android_app->mutex);
292 static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
293 pthread_mutex_lock(&android_app->mutex);
294 if (android_app->pendingWindow != NULL) {
295 android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
297 android_app->pendingWindow = window;
298 if (window != NULL) {
299 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
301 while (android_app->window != android_app->pendingWindow) {
302 pthread_cond_wait(&android_app->cond, &android_app->mutex);
304 pthread_mutex_unlock(&android_app->mutex);
307 static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
308 pthread_mutex_lock(&android_app->mutex);
309 android_app_write_cmd(android_app, cmd);
310 while (android_app->activityState != cmd) {
311 pthread_cond_wait(&android_app->cond, &android_app->mutex);
313 pthread_mutex_unlock(&android_app->mutex);
316 static void android_app_free(struct android_app* android_app) {
317 pthread_mutex_lock(&android_app->mutex);
318 android_app_write_cmd(android_app, APP_CMD_DESTROY);
319 while (!android_app->destroyed) {
320 pthread_cond_wait(&android_app->cond, &android_app->mutex);
322 pthread_mutex_unlock(&android_app->mutex);
324 close(android_app->msgread);
325 close(android_app->msgwrite);
326 pthread_cond_destroy(&android_app->cond);
327 pthread_mutex_destroy(&android_app->mutex);
331 static void onDestroy(ANativeActivity* activity) {
332 LOGI("Destroy: %p\n", (void*)activity);
333 android_app_free((struct android_app*)activity->instance);
336 static void onStart(ANativeActivity* activity) {
337 LOGI("Start: %p\n", (void*)activity);
338 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
341 static void onResume(ANativeActivity* activity) {
342 LOGI("Resume: %p\n", (void*)activity);
343 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
346 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
347 struct android_app* android_app = (struct android_app*)activity->instance;
348 void* savedState = NULL;
350 LOGI("SaveInstanceState: %p\n", (void*)activity);
351 pthread_mutex_lock(&android_app->mutex);
352 android_app->stateSaved = 0;
353 android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
354 while (!android_app->stateSaved) {
355 pthread_cond_wait(&android_app->cond, &android_app->mutex);
358 if (android_app->savedState != NULL) {
359 savedState = android_app->savedState;
360 *outLen = android_app->savedStateSize;
361 android_app->savedState = NULL;
362 android_app->savedStateSize = 0;
365 pthread_mutex_unlock(&android_app->mutex);
370 static void onPause(ANativeActivity* activity) {
371 LOGI("Pause: %p\n", (void*)activity);
372 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
375 static void onStop(ANativeActivity* activity) {
376 LOGI("Stop: %p\n", (void*)activity);
377 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
380 static void onConfigurationChanged(ANativeActivity* activity) {
381 struct android_app* android_app = (struct android_app*)activity->instance;
382 LOGI("ConfigurationChanged: %p\n", (void*)activity);
383 android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
386 static void onLowMemory(ANativeActivity* activity) {
387 struct android_app* android_app = (struct android_app*)activity->instance;
388 LOGI("LowMemory: %p\n", (void*)activity);
389 android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
392 static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
393 LOGI("WindowFocusChanged: %p -- %d\n", (void*)activity, focused);
394 android_app_write_cmd((struct android_app*)activity->instance,
395 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
398 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
399 LOGI("NativeWindowCreated: %p -- %p\n", (void*)activity, (void*)window);
400 android_app_set_window((struct android_app*)activity->instance, window);
403 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
404 LOGI("NativeWindowDestroyed: %p -- %p\n", (void*)activity, (void*)window);
405 android_app_set_window((struct android_app*)activity->instance, NULL);
408 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
409 LOGI("InputQueueCreated: %p -- %p\n", (void*)activity, (void*)queue);
410 android_app_set_input((struct android_app*)activity->instance, queue);
413 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
414 LOGI("InputQueueDestroyed: %p -- %p\n", (void*)activity, (void*)queue);
415 android_app_set_input((struct android_app*)activity->instance, NULL);
418 void ANativeActivity_onCreate(ANativeActivity* activity,
419 void* savedState, size_t savedStateSize) {
420 LOGI("Creating: %p\n", (void*)activity);
421 activity->callbacks->onDestroy = onDestroy;
422 activity->callbacks->onStart = onStart;
423 activity->callbacks->onResume = onResume;
424 activity->callbacks->onSaveInstanceState = onSaveInstanceState;
425 activity->callbacks->onPause = onPause;
426 activity->callbacks->onStop = onStop;
427 activity->callbacks->onConfigurationChanged = onConfigurationChanged;
428 activity->callbacks->onLowMemory = onLowMemory;
429 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
430 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
431 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
432 activity->callbacks->onInputQueueCreated = onInputQueueCreated;
433 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
435 activity->instance = android_app_create(activity, savedState, savedStateSize);