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