OLD | NEW |
(Empty) | |
| 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_a
pp", __VA_ARGS__)) |
| 34 #else |
| 35 # define LOGV(...) ((void)0) |
| 36 #endif |
| 37 |
| 38 static 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 |
| 48 int8_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 |
| 63 static 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 |
| 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, |
| 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 |
| 145 void 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 |
| 169 void app_dummy() { |
| 170 |
| 171 } |
| 172 |
| 173 static 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 |
| 187 static void process_input(struct android_app* app, struct android_poll_source* s
ource) { |
| 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 |
| 200 static void process_cmd(struct android_app* app, struct android_poll_source* sou
rce) { |
| 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 |
| 207 static 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_IN
PUT, 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 |
| 242 static 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 |
| 280 static 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 |
| 286 static 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 |
| 296 static void android_app_set_window(struct android_app* android_app, ANativeWindo
w* 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 |
| 311 static 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 |
| 320 static 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 |
| 335 static void onDestroy(ANativeActivity* activity) { |
| 336 LOGV("Destroy: %p\n", activity); |
| 337 android_app_free((struct android_app*)activity->instance); |
| 338 } |
| 339 |
| 340 static 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 |
| 345 static 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 |
| 350 static 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 |
| 374 static 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 |
| 379 static 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 |
| 384 static 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 |
| 390 static 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 |
| 396 static 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 |
| 402 static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* wind
ow) { |
| 403 LOGV("NativeWindowCreated: %p -- %p\n", activity, window); |
| 404 android_app_set_window((struct android_app*)activity->instance, window); |
| 405 } |
| 406 |
| 407 static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* wi
ndow) { |
| 408 LOGV("NativeWindowDestroyed: %p -- %p\n", activity, window); |
| 409 android_app_set_window((struct android_app*)activity->instance, NULL); |
| 410 } |
| 411 |
| 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 |
| 417 static 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 |
| 422 void 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 } |
OLD | NEW |