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 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 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 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 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 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 164 void app_dummy() { 165 166 } 167 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 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 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 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 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 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 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 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 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 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 331 static void onDestroy(ANativeActivity* activity) { 332 LOGI("Destroy: %p\n", activity); 333 android_app_free((struct android_app*)activity->instance); 334 } 335 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 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 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 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 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 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 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 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 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 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 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 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 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