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