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