1 /*
2  * Copyright (C) 2023 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 "android_native_app_glue.h"
19 
20 #include <android/log.h>
21 #include <errno.h>
22 #include <jni.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/resource.h>
26 #include <unistd.h>
27 
28 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__))
29 #define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "threaded_app", __VA_ARGS__))
30 #define UNUSED(x) (void)(x)
31 
32 /* For debug builds, always enable the debug traces in this library */
33 #ifndef NDEBUG
34 #define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, "threaded_app", __VA_ARGS__))
35 #else
36 #define LOGV(...) ((void)0)
37 #endif
38 
free_saved_state(struct android_app * android_app)39 static void free_saved_state(struct android_app* android_app) {
40     pthread_mutex_lock(&android_app->mutex);
41     if (android_app->savedState != NULL) {
42         free(android_app->savedState);
43         android_app->savedState = NULL;
44         android_app->savedStateSize = 0;
45     }
46     pthread_mutex_unlock(&android_app->mutex);
47 }
48 
android_app_read_cmd(struct android_app * android_app)49 int8_t android_app_read_cmd(struct android_app* android_app) {
50     int8_t cmd;
51     if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) {
52         switch (cmd) {
53             case APP_CMD_SAVE_STATE:
54                 free_saved_state(android_app);
55                 break;
56         }
57         return cmd;
58     } else {
59         LOGE("No data on command pipe!");
60     }
61     return -1;
62 }
63 
print_cur_config(struct android_app * android_app)64 static void print_cur_config(struct android_app* android_app) {
65     char lang[2], country[2];
66     AConfiguration_getLanguage(android_app->config, lang);
67     AConfiguration_getCountry(android_app->config, country);
68 
69     LOGV("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d "
70          "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d "
71          "modetype=%d modenight=%d",
72          AConfiguration_getMcc(android_app->config), AConfiguration_getMnc(android_app->config),
73          lang[0], lang[1], country[0], country[1],
74          AConfiguration_getOrientation(android_app->config),
75          AConfiguration_getTouchscreen(android_app->config),
76          AConfiguration_getDensity(android_app->config),
77          AConfiguration_getKeyboard(android_app->config),
78          AConfiguration_getNavigation(android_app->config),
79          AConfiguration_getKeysHidden(android_app->config),
80          AConfiguration_getNavHidden(android_app->config),
81          AConfiguration_getSdkVersion(android_app->config),
82          AConfiguration_getScreenSize(android_app->config),
83          AConfiguration_getScreenLong(android_app->config),
84          AConfiguration_getUiModeType(android_app->config),
85          AConfiguration_getUiModeNight(android_app->config));
86 }
87 
android_app_pre_exec_cmd(struct android_app * android_app,int8_t cmd)88 void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) {
89     switch (cmd) {
90         case APP_CMD_INPUT_CHANGED:
91             LOGV("APP_CMD_INPUT_CHANGED\n");
92             pthread_mutex_lock(&android_app->mutex);
93             if (android_app->inputQueue != NULL) {
94                 AInputQueue_detachLooper(android_app->inputQueue);
95             }
96             android_app->inputQueue = android_app->pendingInputQueue;
97             if (android_app->inputQueue != NULL) {
98                 LOGV("Attaching input queue to looper");
99                 AInputQueue_attachLooper(android_app->inputQueue, android_app->looper,
100                                          LOOPER_ID_INPUT, NULL, &android_app->inputPollSource);
101             }
102             pthread_cond_broadcast(&android_app->cond);
103             pthread_mutex_unlock(&android_app->mutex);
104             break;
105 
106         case APP_CMD_INIT_WINDOW:
107             LOGV("APP_CMD_INIT_WINDOW\n");
108             pthread_mutex_lock(&android_app->mutex);
109             android_app->window = android_app->pendingWindow;
110             pthread_cond_broadcast(&android_app->cond);
111             pthread_mutex_unlock(&android_app->mutex);
112             break;
113 
114         case APP_CMD_TERM_WINDOW:
115             LOGV("APP_CMD_TERM_WINDOW\n");
116             pthread_cond_broadcast(&android_app->cond);
117             break;
118 
119         case APP_CMD_RESUME:
120         case APP_CMD_START:
121         case APP_CMD_PAUSE:
122         case APP_CMD_STOP:
123             LOGV("activityState=%d\n", cmd);
124             pthread_mutex_lock(&android_app->mutex);
125             android_app->activityState = cmd;
126             pthread_cond_broadcast(&android_app->cond);
127             pthread_mutex_unlock(&android_app->mutex);
128             break;
129 
130         case APP_CMD_CONFIG_CHANGED:
131             LOGV("APP_CMD_CONFIG_CHANGED\n");
132             AConfiguration_fromAssetManager(android_app->config,
133                                             android_app->activity->assetManager);
134             print_cur_config(android_app);
135             break;
136 
137         case APP_CMD_DESTROY:
138             LOGV("APP_CMD_DESTROY\n");
139             android_app->destroyRequested = 1;
140             break;
141     }
142 }
143 
android_app_post_exec_cmd(struct android_app * android_app,int8_t cmd)144 void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) {
145     switch (cmd) {
146         case APP_CMD_TERM_WINDOW:
147             LOGV("APP_CMD_TERM_WINDOW\n");
148             pthread_mutex_lock(&android_app->mutex);
149             android_app->window = NULL;
150             pthread_cond_broadcast(&android_app->cond);
151             pthread_mutex_unlock(&android_app->mutex);
152             break;
153 
154         case APP_CMD_SAVE_STATE:
155             LOGV("APP_CMD_SAVE_STATE\n");
156             pthread_mutex_lock(&android_app->mutex);
157             android_app->stateSaved = 1;
158             pthread_cond_broadcast(&android_app->cond);
159             pthread_mutex_unlock(&android_app->mutex);
160             break;
161 
162         case APP_CMD_RESUME:
163             free_saved_state(android_app);
164             break;
165     }
166 }
167 
app_dummy(void)168 void app_dummy(void) {}
169 
android_app_destroy(struct android_app * android_app)170 static void android_app_destroy(struct android_app* android_app) {
171     LOGV("android_app_destroy!");
172     free_saved_state(android_app);
173     pthread_mutex_lock(&android_app->mutex);
174     if (android_app->inputQueue != NULL) {
175         AInputQueue_detachLooper(android_app->inputQueue);
176     }
177     AConfiguration_delete(android_app->config);
178     android_app->destroyed = 1;
179     pthread_cond_broadcast(&android_app->cond);
180     pthread_mutex_unlock(&android_app->mutex);
181     // Can't touch android_app object after this.
182 }
183 
process_input(struct android_app * app,struct android_poll_source * source)184 static void process_input(struct android_app* app, struct android_poll_source* source) {
185     UNUSED(source);
186     AInputEvent* event = NULL;
187     while (AInputQueue_getEvent(app->inputQueue, &event) >= 0) {
188         LOGV("New input event: type=%d\n", AInputEvent_getType(event));
189         if (AInputQueue_preDispatchEvent(app->inputQueue, event)) {
190             continue;
191         }
192         int32_t handled = 0;
193         if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event);
194         AInputQueue_finishEvent(app->inputQueue, event, handled);
195     }
196 }
197 
process_cmd(struct android_app * app,struct android_poll_source * source)198 static void process_cmd(struct android_app* app, struct android_poll_source* source) {
199     UNUSED(source);
200     int8_t cmd = android_app_read_cmd(app);
201     android_app_pre_exec_cmd(app, cmd);
202     if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
203     android_app_post_exec_cmd(app, cmd);
204 }
205 
android_app_entry(void * param)206 static void* android_app_entry(void* param) {
207     struct android_app* android_app = (struct android_app*)param;
208 
209     android_app->config = AConfiguration_new();
210     AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager);
211 
212     print_cur_config(android_app);
213 
214     android_app->cmdPollSource.id = LOOPER_ID_MAIN;
215     android_app->cmdPollSource.app = android_app;
216     android_app->cmdPollSource.process = process_cmd;
217     android_app->inputPollSource.id = LOOPER_ID_INPUT;
218     android_app->inputPollSource.app = android_app;
219     android_app->inputPollSource.process = process_input;
220 
221     ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
222     ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL,
223                   &android_app->cmdPollSource);
224     android_app->looper = looper;
225 
226     pthread_mutex_lock(&android_app->mutex);
227     android_app->running = 1;
228     pthread_cond_broadcast(&android_app->cond);
229     pthread_mutex_unlock(&android_app->mutex);
230 
231     android_main(android_app);
232 
233     android_app_destroy(android_app);
234     return NULL;
235 }
236 
237 // --------------------------------------------------------------------
238 // Native activity interaction (called from main thread)
239 // --------------------------------------------------------------------
240 
android_app_create(ANativeActivity * activity,void * savedState,size_t savedStateSize)241 static struct android_app* android_app_create(ANativeActivity* activity, void* savedState,
242                                               size_t savedStateSize) {
243     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;
246 
247     pthread_mutex_init(&android_app->mutex, NULL);
248     pthread_cond_init(&android_app->cond, NULL);
249 
250     if (savedState != NULL) {
251         android_app->savedState = malloc(savedStateSize);
252         android_app->savedStateSize = savedStateSize;
253         memcpy(android_app->savedState, savedState, savedStateSize);
254     }
255 
256     int msgpipe[2];
257     if (pipe(msgpipe)) {
258         LOGE("could not create pipe: %s", strerror(errno));
259         return NULL;
260     }
261     android_app->msgread = msgpipe[0];
262     android_app->msgwrite = msgpipe[1];
263 
264     pthread_attr_t attr;
265     pthread_attr_init(&attr);
266     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
267     pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
268 
269     // Wait for thread to start.
270     pthread_mutex_lock(&android_app->mutex);
271     while (!android_app->running) {
272         pthread_cond_wait(&android_app->cond, &android_app->mutex);
273     }
274     pthread_mutex_unlock(&android_app->mutex);
275 
276     return android_app;
277 }
278 
android_app_write_cmd(struct android_app * android_app,int8_t cmd)279 static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) {
280     if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) {
281         LOGE("Failure writing android_app cmd: %s\n", strerror(errno));
282     }
283 }
284 
android_app_set_input(struct android_app * android_app,AInputQueue * inputQueue)285 static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) {
286     pthread_mutex_lock(&android_app->mutex);
287     android_app->pendingInputQueue = inputQueue;
288     android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED);
289     while (android_app->inputQueue != android_app->pendingInputQueue) {
290         pthread_cond_wait(&android_app->cond, &android_app->mutex);
291     }
292     pthread_mutex_unlock(&android_app->mutex);
293 }
294 
android_app_set_window(struct android_app * android_app,ANativeWindow * window)295 static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) {
296     pthread_mutex_lock(&android_app->mutex);
297     if (android_app->pendingWindow != NULL) {
298         android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
299     }
300     android_app->pendingWindow = window;
301     if (window != NULL) {
302         android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW);
303     }
304     while (android_app->window != android_app->pendingWindow) {
305         pthread_cond_wait(&android_app->cond, &android_app->mutex);
306     }
307     pthread_mutex_unlock(&android_app->mutex);
308 }
309 
android_app_set_activity_state(struct android_app * android_app,int8_t cmd)310 static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) {
311     pthread_mutex_lock(&android_app->mutex);
312     android_app_write_cmd(android_app, cmd);
313     while (android_app->activityState != cmd) {
314         pthread_cond_wait(&android_app->cond, &android_app->mutex);
315     }
316     pthread_mutex_unlock(&android_app->mutex);
317 }
318 
android_app_free(struct android_app * android_app)319 static void android_app_free(struct android_app* android_app) {
320     pthread_mutex_lock(&android_app->mutex);
321     android_app_write_cmd(android_app, APP_CMD_DESTROY);
322     while (!android_app->destroyed) {
323         pthread_cond_wait(&android_app->cond, &android_app->mutex);
324     }
325     pthread_mutex_unlock(&android_app->mutex);
326 
327     close(android_app->msgread);
328     close(android_app->msgwrite);
329     pthread_cond_destroy(&android_app->cond);
330     pthread_mutex_destroy(&android_app->mutex);
331     free(android_app);
332 }
333 
onDestroy(ANativeActivity * activity)334 static void onDestroy(ANativeActivity* activity) {
335     LOGV("Destroy: %p\n", activity);
336     android_app_free((struct android_app*)activity->instance);
337 }
338 
onStart(ANativeActivity * activity)339 static void onStart(ANativeActivity* activity) {
340     LOGV("Start: %p\n", activity);
341     android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START);
342 }
343 
onResume(ANativeActivity * activity)344 static void onResume(ANativeActivity* activity) {
345     LOGV("Resume: %p\n", activity);
346     android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME);
347 }
348 
onSaveInstanceState(ANativeActivity * activity,size_t * outLen)349 static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) {
350     struct android_app* android_app = (struct android_app*)activity->instance;
351     void* savedState = NULL;
352 
353     LOGV("SaveInstanceState: %p\n", activity);
354     pthread_mutex_lock(&android_app->mutex);
355     android_app->stateSaved = 0;
356     android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
357     while (!android_app->stateSaved) {
358         pthread_cond_wait(&android_app->cond, &android_app->mutex);
359     }
360 
361     if (android_app->savedState != NULL) {
362         savedState = android_app->savedState;
363         *outLen = android_app->savedStateSize;
364         android_app->savedState = NULL;
365         android_app->savedStateSize = 0;
366     }
367 
368     pthread_mutex_unlock(&android_app->mutex);
369 
370     return savedState;
371 }
372 
onPause(ANativeActivity * activity)373 static void onPause(ANativeActivity* activity) {
374     LOGV("Pause: %p\n", activity);
375     android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE);
376 }
377 
onStop(ANativeActivity * activity)378 static void onStop(ANativeActivity* activity) {
379     LOGV("Stop: %p\n", activity);
380     android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP);
381 }
382 
onConfigurationChanged(ANativeActivity * activity)383 static void onConfigurationChanged(ANativeActivity* activity) {
384     struct android_app* android_app = (struct android_app*)activity->instance;
385     LOGV("ConfigurationChanged: %p\n", activity);
386     android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED);
387 }
388 
onLowMemory(ANativeActivity * activity)389 static void onLowMemory(ANativeActivity* activity) {
390     struct android_app* android_app = (struct android_app*)activity->instance;
391     LOGV("LowMemory: %p\n", activity);
392     android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY);
393 }
394 
onWindowFocusChanged(ANativeActivity * activity,int focused)395 static void onWindowFocusChanged(ANativeActivity* activity, int focused) {
396     LOGV("WindowFocusChanged: %p -- %d\n", activity, focused);
397     android_app_write_cmd((struct android_app*)activity->instance,
398                           focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS);
399 }
400 
onNativeWindowCreated(ANativeActivity * activity,ANativeWindow * window)401 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) {
402     LOGV("NativeWindowCreated: %p -- %p\n", activity, window);
403     android_app_set_window((struct android_app*)activity->instance, window);
404 }
405 
onNativeWindowDestroyed(ANativeActivity * activity,ANativeWindow * window)406 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) {
407     UNUSED(window);
408     LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window);
409     android_app_set_window((struct android_app*)activity->instance, NULL);
410 }
411 
onInputQueueCreated(ANativeActivity * activity,AInputQueue * queue)412 static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) {
413     LOGV("InputQueueCreated: %p -- %p\n", activity, queue);
414     android_app_set_input((struct android_app*)activity->instance, queue);
415 }
416 
onInputQueueDestroyed(ANativeActivity * activity,AInputQueue * queue)417 static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) {
418     UNUSED(queue);
419     LOGV("InputQueueDestroyed: %p -- %p\n", activity, queue);
420     android_app_set_input((struct android_app*)activity->instance, NULL);
421 }
422 
ANativeActivity_onCreate(ANativeActivity * activity,void * savedState,size_t savedStateSize)423 __attribute__((visibility("default"))) void ANativeActivity_onCreate(ANativeActivity* activity,
424                                                                      void* savedState,
425                                                                      size_t savedStateSize) {
426     LOGV("Creating: %p\n", activity);
427     activity->callbacks->onDestroy = onDestroy;
428     activity->callbacks->onStart = onStart;
429     activity->callbacks->onResume = onResume;
430     activity->callbacks->onSaveInstanceState = onSaveInstanceState;
431     activity->callbacks->onPause = onPause;
432     activity->callbacks->onStop = onStop;
433     activity->callbacks->onConfigurationChanged = onConfigurationChanged;
434     activity->callbacks->onLowMemory = onLowMemory;
435     activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
436     activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
437     activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
438     activity->callbacks->onInputQueueCreated = onInputQueueCreated;
439     activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
440 
441     activity->instance = android_app_create(activity, savedState, savedStateSize);
442 }