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
18 #include <jni.h>
19
20 #include <errno.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <sys/resource.h>
24
25 #include "android_native_app_glue.h"
26 #include <android/log.h>
27
28 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
29
free_saved_state(struct android_app * android_app)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;
36 }
37 pthread_mutex_unlock(&android_app->mutex);
38 }
39
android_app_read_cmd(struct android_app * android_app)40 int8_t android_app_read_cmd(struct android_app* android_app) {
41 int8_t cmd;
42 if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
43 switch (cmd) {
44 case APP_CMD_SAVE_STATE:
45 free_saved_state(android_app);
46 break;
47 }
48 return cmd;
49 } else {
50 LOGI("No data on command pipe!");
51 }
52 return -1;
53 }
54
print_cur_config(struct android_app * android_app)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);
59
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));
78 }
79
android_app_pre_exec_cmd(struct android_app * android_app,int8_t cmd)80 void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
81 switch (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);
87 }
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);
94 }
95 pthread_cond_broadcast(&android_app->cond);
96 pthread_mutex_unlock(&android_app->mutex);
97 break;
98
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);
105 break;
106
107 case APP_CMD_TERM_WINDOW:
108 LOGI("APP_CMD_TERM_WINDOW\n");
109 pthread_mutex_lock(&android_app->mutex);
110 android_app->window = NULL;
111 pthread_cond_broadcast(&android_app->cond);
112 pthread_mutex_unlock(&android_app->mutex);
113 break;
114
115 case APP_CMD_RESUME:
116 case APP_CMD_START:
117 case APP_CMD_PAUSE:
118 case APP_CMD_STOP:
119 LOGI("activityState=%d\n", cmd);
120 pthread_mutex_lock(&android_app->mutex);
121 android_app->activityState = cmd;
122 pthread_cond_broadcast(&android_app->cond);
123 pthread_mutex_unlock(&android_app->mutex);
124 break;
125
126 case APP_CMD_CONFIG_CHANGED:
127 LOGI("APP_CMD_CONFIG_CHANGED\n");
128 AConfiguration_fromAssetManager(android_app->config,
129 android_app->activity->assetManager);
130 print_cur_config(android_app);
131 break;
132
133 case APP_CMD_DESTROY:
134 LOGI("APP_CMD_DESTROY\n");
135 android_app->destroyRequested = 1;
136 break;
137 }
138 }
139
android_app_post_exec_cmd(struct android_app * android_app,int8_t cmd)140 void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
141 switch (cmd) {
142 case APP_CMD_TERM_WINDOW:
143 LOGI("APP_CMD_TERM_WINDOW\n");
144 pthread_mutex_lock(&android_app->mutex);
145 android_app->window = NULL;
146 pthread_cond_broadcast(&android_app->cond);
147 pthread_mutex_unlock(&android_app->mutex);
148 break;
149
150 case APP_CMD_SAVE_STATE:
151 LOGI("APP_CMD_SAVE_STATE\n");
152 pthread_mutex_lock(&android_app->mutex);
153 android_app->stateSaved = 1;
154 pthread_cond_broadcast(&android_app->cond);
155 pthread_mutex_unlock(&android_app->mutex);
156 break;
157
158 case APP_CMD_RESUME:
159 free_saved_state(android_app);
160 break;
161 }
162 }
163
app_dummy()164 void app_dummy() {
165
166 }
167
android_app_destroy(struct android_app * android_app)168 static void android_app_destroy(struct android_app* android_app) {
169 LOGI("android_app_destroy!");
170 free_saved_state(android_app);
171 pthread_mutex_lock(&android_app->mutex);
172 if (android_app->inputQueue != NULL) {
173 AInputQueue_detachLooper(android_app->inputQueue);
174 }
175 AConfiguration_delete(android_app->config);
176 android_app->destroyed = 1;
177 pthread_cond_broadcast(&android_app->cond);
178 pthread_mutex_unlock(&android_app->mutex);
179 // Can't touch android_app object after this.
180 }
181
process_input(struct android_app * app,struct android_poll_source * source)182 static void process_input(struct android_app* app, struct android_poll_source* source) {
183 AInputEvent* event = NULL;
184 if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
185 LOGI("New input event: type=%d\n", AInputEvent_getType(event));
186 if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
187 return;
188 }
189 int32_t handled = 0;
190 if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
191 AInputQueue_finishEvent(app->inputQueue, event, handled);
192 } else {
193 LOGI("Failure reading next input event: %s\n", strerror(errno));
194 }
195 }
196
process_cmd(struct android_app * app,struct android_poll_source * source)197 static void process_cmd(struct android_app* app, struct android_poll_source* source) {
198 int8_t cmd = android_app_read_cmd(app);
199 android_app_pre_exec_cmd(app, cmd);
200 if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
201 android_app_post_exec_cmd(app, cmd);
202 }
203
android_app_entry(void * param)204 static void* android_app_entry(void* param) {
205 struct android_app* android_app = (struct android_app*)param;
206
207 android_app->config = AConfiguration_new();
208 AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
209
210 print_cur_config(android_app);
211
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;
218
219 ALooper* 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;
223
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);
228
229 android_main(android_app);
230
231 android_app_destroy(android_app);
232 return NULL;
233 }
234
235 // --------------------------------------------------------------------
236 // Native activity interaction (called from main thread)
237 // --------------------------------------------------------------------
238
android_app_create(ANativeActivity * activity,void * savedState,size_t savedStateSize)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));
242 memset(android_app, 0, sizeof(struct android_app));
243 android_app->activity = activity;
244
245 pthread_mutex_init(&android_app->mutex, NULL);
246 pthread_cond_init(&android_app->cond, NULL);
247
248 if (savedState != NULL) {
249 android_app->savedState = malloc(savedStateSize);
250 android_app->savedStateSize = savedStateSize;
251 memcpy(android_app->savedState, savedState, savedStateSize);
252 }
253
254 int msgpipe[2];
255 if (pipe(msgpipe)) {
256 LOGI("could not create pipe: %s", strerror(errno));
257 }
258 android_app->msgread = msgpipe[0];
259 android_app->msgwrite = msgpipe[1];
260
261 pthread_attr_t attr;
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);
265
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);
270 }
271 pthread_mutex_unlock(&android_app->mutex);
272
273 return android_app;
274 }
275
android_app_write_cmd(struct android_app * android_app,int8_t cmd)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));
279 }
280 }
281
android_app_set_input(struct android_app * android_app,AInputQueue * inputQueue)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);
288 }
289 pthread_mutex_unlock(&android_app->mutex);
290 }
291
android_app_set_window(struct android_app * android_app,ANativeWindow * window)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);
296 }
297 android_app->pendingWindow = window;
298 if (window != NULL) {
299 android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
300 }
301 while (android_app->window != android_app->pendingWindow) {
302 pthread_cond_wait(&android_app->cond, &android_app->mutex);
303 }
304 pthread_mutex_unlock(&android_app->mutex);
305 }
306
android_app_set_activity_state(struct android_app * android_app,int8_t cmd)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);
312 }
313 pthread_mutex_unlock(&android_app->mutex);
314 }
315
android_app_free(struct android_app * android_app)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);
321 }
322 pthread_mutex_unlock(&android_app->mutex);
323
324 close(android_app->msgread);
325 close(android_app->msgwrite);
326 pthread_cond_destroy(&android_app->cond);
327 pthread_mutex_destroy(&android_app->mutex);
328 free(android_app);
329 }
330
onDestroy(ANativeActivity * activity)331 static void onDestroy(ANativeActivity* activity) {
332 LOGI("Destroy: %p\n", activity);
333 android_app_free((struct android_app*)activity->instance);
334 }
335
onStart(ANativeActivity * activity)336 static void onStart(ANativeActivity* activity) {
337 LOGI("Start: %p\n", activity);
338 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
339 }
340
onResume(ANativeActivity * activity)341 static void onResume(ANativeActivity* activity) {
342 LOGI("Resume: %p\n", activity);
343 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
344 }
345
onSaveInstanceState(ANativeActivity * activity,size_t * outLen)346 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
347 struct android_app* android_app = (struct android_app*)activity->instance;
348 void* savedState = NULL;
349
350 LOGI("SaveInstanceState: %p\n", 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);
356 }
357
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;
363 }
364
365 pthread_mutex_unlock(&android_app->mutex);
366
367 return savedState;
368 }
369
onPause(ANativeActivity * activity)370 static void onPause(ANativeActivity* activity) {
371 LOGI("Pause: %p\n", activity);
372 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
373 }
374
onStop(ANativeActivity * activity)375 static void onStop(ANativeActivity* activity) {
376 LOGI("Stop: %p\n", activity);
377 android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
378 }
379
onConfigurationChanged(ANativeActivity * activity)380 static void onConfigurationChanged(ANativeActivity* activity) {
381 struct android_app* android_app = (struct android_app*)activity->instance;
382 LOGI("ConfigurationChanged: %p\n", activity);
383 android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
384 }
385
onLowMemory(ANativeActivity * activity)386 static void onLowMemory(ANativeActivity* activity) {
387 struct android_app* android_app = (struct android_app*)activity->instance;
388 LOGI("LowMemory: %p\n", activity);
389 android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
390 }
391
onWindowFocusChanged(ANativeActivity * activity,int focused)392 static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
393 LOGI("WindowFocusChanged: %p -- %d\n", activity, focused);
394 android_app_write_cmd((struct android_app*)activity->instance,
395 focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
396 }
397
onNativeWindowCreated(ANativeActivity * activity,ANativeWindow * window)398 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
399 LOGI("NativeWindowCreated: %p -- %p\n", activity, window);
400 android_app_set_window((struct android_app*)activity->instance, window);
401 }
402
onNativeWindowDestroyed(ANativeActivity * activity,ANativeWindow * window)403 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
404 LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window);
405 android_app_set_window((struct android_app*)activity->instance, NULL);
406 }
407
onInputQueueCreated(ANativeActivity * activity,AInputQueue * queue)408 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
409 LOGI("InputQueueCreated: %p -- %p\n", activity, queue);
410 android_app_set_input((struct android_app*)activity->instance, queue);
411 }
412
onInputQueueDestroyed(ANativeActivity * activity,AInputQueue * queue)413 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
414 LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue);
415 android_app_set_input((struct android_app*)activity->instance, NULL);
416 }
417
ANativeActivity_onCreate(ANativeActivity * activity,void * savedState,size_t savedStateSize)418 void ANativeActivity_onCreate(ANativeActivity* activity,
419 void* savedState, size_t savedStateSize) {
420 LOGI("Creating: %p\n", 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;
434
435 activity->instance = android_app_create(activity, savedState, savedStateSize);
436 }
437