From 5510127e89d6971a219ce3664e4631d6c6dda2b1 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Wed, 30 Nov 2011 21:39:18 +0200 Subject: Android code refactorig and hacking Sorry for the large unstructured commit. But hey, the Android code is experimental so far. Extract the native lo-bootstrap code into a fairly normal library built in sal. (Previously it was the JNI part of the "Bootstrap" app.) Just linkink normally to liblo-bootstrap from C++ code that uses it works fine, no need to do a dlsym lookup. Bootstrap is still a subclass of NativeActivity and can thus still be used as an "app" (to start unit tests, or whatever), but can also be used from some other app's Java code to just get access to the lo-bootstrap native methods. Introduce a new top-level "module", android, for Bootstrap and the experiments with DocumentLoader. Note that the experimental DocumentLoader app still crashes. It can't create the com.sun.star.frame.Desktop instance. I spent lots of time debugging in the painfully inadequate ndk-gdb. (Even the newer gdb build from the "mingw-and-ndk" project is quite crappy in many ways.) I should really experiment with corresponding code on a normal platform first before even trying on Android. Basically, I think that if I just can get the concept of Java code that instantiates and uses LO components *in-process* working on a normal desktop platform, it should work on Android, too. --- sal/android/android_native_app_glue.c | 435 ++++++++ sal/android/android_native_app_glue.h | 349 +++++++ sal/android/lo-bootstrap.c | 1057 ++++++++++++++++++++ sal/android/makefile.mk | 59 ++ sal/osl/android/.gitignore | 6 - sal/osl/android/AndroidManifest.xml | 24 - sal/osl/android/Makefile | 126 --- sal/osl/android/ant.properties | 17 - sal/osl/android/build.xml | 114 --- sal/osl/android/jni/android_native_app_glue.c | 435 -------- sal/osl/android/jni/android_native_app_glue.h | 349 ------- sal/osl/android/jni/lo-bootstrap.c | 1041 ------------------- sal/osl/android/jni/lo-bootstrap.h | 64 -- sal/osl/android/project.properties | 11 - sal/osl/android/res/layout/main.xml | 12 - sal/osl/android/res/values/strings.xml | 4 - sal/osl/android/src/fi/iki/tml/CommandLine.java | 176 ---- .../src/org/libreoffice/android/Bootstrap.java | 169 ---- sal/osl/unx/file.cxx | 7 +- sal/osl/unx/module.c | 7 +- sal/osl/unx/process_impl.cxx | 26 +- sal/prj/build.lst | 3 +- sal/prj/d.lst | 2 + sal/util/makefile.mk | 4 + 24 files changed, 1940 insertions(+), 2557 deletions(-) create mode 100644 sal/android/android_native_app_glue.c create mode 100644 sal/android/android_native_app_glue.h create mode 100644 sal/android/lo-bootstrap.c create mode 100644 sal/android/makefile.mk delete mode 100644 sal/osl/android/.gitignore delete mode 100644 sal/osl/android/AndroidManifest.xml delete mode 100644 sal/osl/android/Makefile delete mode 100644 sal/osl/android/ant.properties delete mode 100644 sal/osl/android/build.xml delete mode 100644 sal/osl/android/jni/android_native_app_glue.c delete mode 100644 sal/osl/android/jni/android_native_app_glue.h delete mode 100644 sal/osl/android/jni/lo-bootstrap.c delete mode 100644 sal/osl/android/jni/lo-bootstrap.h delete mode 100644 sal/osl/android/project.properties delete mode 100644 sal/osl/android/res/layout/main.xml delete mode 100644 sal/osl/android/res/values/strings.xml delete mode 100644 sal/osl/android/src/fi/iki/tml/CommandLine.java delete mode 100644 sal/osl/android/src/org/libreoffice/android/Bootstrap.java (limited to 'sal') diff --git a/sal/android/android_native_app_glue.c b/sal/android/android_native_app_glue.c new file mode 100644 index 000000000000..8ff9f69d9946 --- /dev/null +++ b/sal/android/android_native_app_glue.c @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include +#include +#include + +#include "android_native_app_glue.h" +#include + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) + +static void free_saved_state(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->savedState != NULL) { + free(android_app->savedState); + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + pthread_mutex_unlock(&android_app->mutex); +} + +int8_t android_app_read_cmd(struct android_app* android_app) { + int8_t cmd; + if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { + switch (cmd) { + case APP_CMD_SAVE_STATE: + free_saved_state(android_app); + break; + } + return cmd; + } else { + LOGI("No data on command pipe!"); + } + return -1; +} + +static void print_cur_config(struct android_app* android_app) { + char lang[2], country[2]; + AConfiguration_getLanguage(android_app->config, lang); + AConfiguration_getCountry(android_app->config, country); + + LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " + "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " + "modetype=%d modenight=%d", + AConfiguration_getMcc(android_app->config), + AConfiguration_getMnc(android_app->config), + lang[0], lang[1], country[0], country[1], + AConfiguration_getOrientation(android_app->config), + AConfiguration_getTouchscreen(android_app->config), + AConfiguration_getDensity(android_app->config), + AConfiguration_getKeyboard(android_app->config), + AConfiguration_getNavigation(android_app->config), + AConfiguration_getKeysHidden(android_app->config), + AConfiguration_getNavHidden(android_app->config), + AConfiguration_getSdkVersion(android_app->config), + AConfiguration_getScreenSize(android_app->config), + AConfiguration_getScreenLong(android_app->config), + AConfiguration_getUiModeType(android_app->config), + AConfiguration_getUiModeNight(android_app->config)); +} + +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_INPUT_CHANGED: + LOGI("APP_CMD_INPUT_CHANGED\n"); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + android_app->inputQueue = android_app->pendingInputQueue; + if (android_app->inputQueue != NULL) { + LOGI("Attaching input queue to looper"); + AInputQueue_attachLooper(android_app->inputQueue, + android_app->looper, LOOPER_ID_INPUT, NULL, + &android_app->inputPollSource); + } + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_INIT_WINDOW: + LOGI("APP_CMD_INIT_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = android_app->pendingWindow; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_TERM_WINDOW: + LOGI("APP_CMD_TERM_WINDOW\n"); + pthread_cond_broadcast(&android_app->cond); + break; + + case APP_CMD_RESUME: + case APP_CMD_START: + case APP_CMD_PAUSE: + case APP_CMD_STOP: + LOGI("activityState=%d\n", cmd); + pthread_mutex_lock(&android_app->mutex); + android_app->activityState = cmd; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_CONFIG_CHANGED: + LOGI("APP_CMD_CONFIG_CHANGED\n"); + AConfiguration_fromAssetManager(android_app->config, + android_app->activity->assetManager); + print_cur_config(android_app); + break; + + case APP_CMD_DESTROY: + LOGI("APP_CMD_DESTROY\n"); + android_app->destroyRequested = 1; + break; + } +} + +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { + switch (cmd) { + case APP_CMD_TERM_WINDOW: + LOGI("APP_CMD_TERM_WINDOW\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->window = NULL; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_SAVE_STATE: + LOGI("APP_CMD_SAVE_STATE\n"); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + break; + + case APP_CMD_RESUME: + free_saved_state(android_app); + break; + } +} + +void app_dummy() { + +} + +static void android_app_destroy(struct android_app* android_app) { + LOGI("android_app_destroy!"); + free_saved_state(android_app); + pthread_mutex_lock(&android_app->mutex); + if (android_app->inputQueue != NULL) { + AInputQueue_detachLooper(android_app->inputQueue); + } + AConfiguration_delete(android_app->config); + android_app->destroyed = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + // Can't touch android_app object after this. +} + +static void process_input(struct android_app* app, struct android_poll_source* source) { + AInputEvent* event = NULL; + (void) source; + if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { + LOGI("New input event: type=%d\n", AInputEvent_getType(event)); + if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { + return; + } + int32_t handled = 0; + if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); + AInputQueue_finishEvent(app->inputQueue, event, handled); + } else { + LOGI("Failure reading next input event: %s\n", strerror(errno)); + } +} + +static void process_cmd(struct android_app* app, struct android_poll_source* source) { + int8_t cmd = android_app_read_cmd(app); + (void) source; + android_app_pre_exec_cmd(app, cmd); + if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); + android_app_post_exec_cmd(app, cmd); +} + +static void* android_app_entry(void* param) { + struct android_app* android_app = (struct android_app*)param; + + android_app->config = AConfiguration_new(); + AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); + + print_cur_config(android_app); + + android_app->cmdPollSource.id = LOOPER_ID_MAIN; + android_app->cmdPollSource.app = android_app; + android_app->cmdPollSource.process = process_cmd; + android_app->inputPollSource.id = LOOPER_ID_INPUT; + android_app->inputPollSource.app = android_app; + android_app->inputPollSource.process = process_input; + + ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); + ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, + &android_app->cmdPollSource); + android_app->looper = looper; + + pthread_mutex_lock(&android_app->mutex); + android_app->running = 1; + pthread_cond_broadcast(&android_app->cond); + pthread_mutex_unlock(&android_app->mutex); + + android_main(android_app); + + android_app_destroy(android_app); + return NULL; +} + +// -------------------------------------------------------------------- +// Native activity interaction (called from main thread) +// -------------------------------------------------------------------- + +static struct android_app* android_app_create(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); + memset(android_app, 0, sizeof(struct android_app)); + android_app->activity = activity; + + pthread_mutex_init(&android_app->mutex, NULL); + pthread_cond_init(&android_app->cond, NULL); + + if (savedState != NULL) { + android_app->savedState = malloc(savedStateSize); + android_app->savedStateSize = savedStateSize; + memcpy(android_app->savedState, savedState, savedStateSize); + } + + int msgpipe[2]; + if (pipe(msgpipe)) { + LOGI("could not create pipe: %s", strerror(errno)); + } + android_app->msgread = msgpipe[0]; + android_app->msgwrite = msgpipe[1]; + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&android_app->thread, &attr, android_app_entry, android_app); + + // Wait for thread to start. + pthread_mutex_lock(&android_app->mutex); + while (!android_app->running) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + return android_app; +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { + pthread_mutex_lock(&android_app->mutex); + android_app->pendingInputQueue = inputQueue; + android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); + while (android_app->inputQueue != android_app->pendingInputQueue) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { + pthread_mutex_lock(&android_app->mutex); + if (android_app->pendingWindow != NULL) { + android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); + } + android_app->pendingWindow = window; + if (window != NULL) { + android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); + } + while (android_app->window != android_app->pendingWindow) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, cmd); + while (android_app->activityState != cmd) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); +} + +static void android_app_free(struct android_app* android_app) { + pthread_mutex_lock(&android_app->mutex); + android_app_write_cmd(android_app, APP_CMD_DESTROY); + while (!android_app->destroyed) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + pthread_mutex_unlock(&android_app->mutex); + + close(android_app->msgread); + close(android_app->msgwrite); + pthread_cond_destroy(&android_app->cond); + pthread_mutex_destroy(&android_app->mutex); + free(android_app); +} + +static void onDestroy(ANativeActivity* activity) { + LOGI("Destroy: %p\n", activity); + android_app_free((struct android_app*)activity->instance); +} + +static void onStart(ANativeActivity* activity) { + LOGI("Start: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); +} + +static void onResume(ANativeActivity* activity) { + LOGI("Resume: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); +} + +static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { + struct android_app* android_app = (struct android_app*)activity->instance; + void* savedState = NULL; + + LOGI("SaveInstanceState: %p\n", activity); + pthread_mutex_lock(&android_app->mutex); + android_app->stateSaved = 0; + android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); + while (!android_app->stateSaved) { + pthread_cond_wait(&android_app->cond, &android_app->mutex); + } + + if (android_app->savedState != NULL) { + savedState = android_app->savedState; + *outLen = android_app->savedStateSize; + android_app->savedState = NULL; + android_app->savedStateSize = 0; + } + + pthread_mutex_unlock(&android_app->mutex); + + return savedState; +} + +static void onPause(ANativeActivity* activity) { + LOGI("Pause: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); +} + +static void onStop(ANativeActivity* activity) { + LOGI("Stop: %p\n", activity); + android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); +} + +static void onConfigurationChanged(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGI("ConfigurationChanged: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); +} + +static void onLowMemory(ANativeActivity* activity) { + struct android_app* android_app = (struct android_app*)activity->instance; + LOGI("LowMemory: %p\n", activity); + android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); +} + +static void onWindowFocusChanged(ANativeActivity* activity, int focused) { + LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); + android_app_write_cmd((struct android_app*)activity->instance, + focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); +} + +static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowCreated: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, window); +} + +static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { + LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); + android_app_set_window((struct android_app*)activity->instance, NULL); +} + +static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueCreated: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, queue); +} + +static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { + LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); + android_app_set_input((struct android_app*)activity->instance, NULL); +} + +void ANativeActivity_onCreate(ANativeActivity* activity, + void* savedState, size_t savedStateSize) { + LOGI("Creating: %p\n", activity); + activity->callbacks->onDestroy = onDestroy; + activity->callbacks->onStart = onStart; + activity->callbacks->onResume = onResume; + activity->callbacks->onSaveInstanceState = onSaveInstanceState; + activity->callbacks->onPause = onPause; + activity->callbacks->onStop = onStop; + activity->callbacks->onConfigurationChanged = onConfigurationChanged; + activity->callbacks->onLowMemory = onLowMemory; + activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; + activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; + activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; + activity->callbacks->onInputQueueCreated = onInputQueueCreated; + activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; + + activity->instance = android_app_create(activity, savedState, savedStateSize); +} diff --git a/sal/android/android_native_app_glue.h b/sal/android/android_native_app_glue.h new file mode 100644 index 000000000000..1b8c1f10725d --- /dev/null +++ b/sal/android/android_native_app_glue.h @@ -0,0 +1,349 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#ifndef _ANDROID_NATIVE_APP_GLUE_H +#define _ANDROID_NATIVE_APP_GLUE_H + +#include +#include +#include + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The native activity interface provided by + * is based on a set of application-provided callbacks that will be called + * by the Activity's main thread when certain events occur. + * + * This means that each one of this callbacks _should_ _not_ block, or they + * risk having the system force-close the application. This programming + * model is direct, lightweight, but constraining. + * + * The 'threaded_native_app' static library is used to provide a different + * execution model where the application can implement its own main event + * loop in a different thread instead. Here's how it works: + * + * 1/ The application must provide a function named "android_main()" that + * will be called when the activity is created, in a new thread that is + * distinct from the activity's main thread. + * + * 2/ android_main() receives a pointer to a valid "android_app" structure + * that contains references to other important objects, e.g. the + * ANativeActivity obejct instance the application is running in. + * + * 3/ the "android_app" object holds an ALooper instance that already + * listens to two important things: + * + * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX + * declarations below. + * + * - input events coming from the AInputQueue attached to the activity. + * + * Each of these correspond to an ALooper identifier returned by + * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, + * respectively. + * + * Your application can use the same ALooper to listen to additional + * file-descriptors. They can either be callback based, or with return + * identifiers starting with LOOPER_ID_USER. + * + * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, + * the returned data will point to an android_poll_source structure. You + * can call the process() function on it, and fill in android_app->onAppCmd + * and android_app->onInputEvent to be called for your own processing + * of the event. + * + * Alternatively, you can call the low-level functions to read and process + * the data directly... look at the process_cmd() and process_input() + * implementations in the glue to see how to do this. + * + * See the sample named "native-activity" that comes with the NDK with a + * full usage example. Also look at the JavaDoc of NativeActivity. + */ + +struct android_app; + +/** + * Data associated with an ALooper fd that will be returned as the "outData" + * when that source has data ready. + */ +struct android_poll_source { + // The identifier of this source. May be LOOPER_ID_MAIN or + // LOOPER_ID_INPUT. + int32_t id; + + // The android_app this ident is associated with. + struct android_app* app; + + // Function to call to perform the standard processing of data from + // this source. + void (*process)(struct android_app* app, struct android_poll_source* source); +}; + +/** + * This is the interface for the standard glue code of a threaded + * application. In this model, the application's code is running + * in its own thread separate from the main thread of the process. + * It is not required that this thread be associated with the Java + * VM, although it will need to be in order to make JNI calls any + * Java objects. + */ +struct android_app { + // The application can place a pointer to its own state object + // here if it likes. + void* userData; + + // Fill this in with the function to process main app commands (APP_CMD_*) + void (*onAppCmd)(struct android_app* app, int32_t cmd); + + // Fill this in with the function to process input events. At this point + // the event has already been pre-dispatched, and it will be finished upon + // return. Return 1 if you have handled the event, 0 for any default + // dispatching. + int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); + + // The ANativeActivity object instance that this app is running in. + ANativeActivity* activity; + + // The current configuration the app is running in. + AConfiguration* config; + + // This is the last instance's saved state, as provided at creation time. + // It is NULL if there was no state. You can use this as you need; the + // memory will remain around until you call android_app_exec_cmd() for + // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. + // These variables should only be changed when processing a APP_CMD_SAVE_STATE, + // at which point they will be initialized to NULL and you can malloc your + // state and place the information here. In that case the memory will be + // freed for you later. + void* savedState; + size_t savedStateSize; + + // The ALooper associated with the app's thread. + ALooper* looper; + + // When non-NULL, this is the input queue from which the app will + // receive user input events. + AInputQueue* inputQueue; + + // When non-NULL, this is the window surface that the app can draw in. + ANativeWindow* window; + + // Current content rectangle of the window; this is the area where the + // window's content should be placed to be seen by the user. + ARect contentRect; + + // Current state of the app's activity. May be either APP_CMD_START, + // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. + int activityState; + + // This is non-zero when the application's NativeActivity is being + // destroyed and waiting for the app thread to complete. + int destroyRequested; + + // ------------------------------------------------- + // Below are "private" implementation of the glue code. + + pthread_mutex_t mutex; + pthread_cond_t cond; + + int msgread; + int msgwrite; + + pthread_t thread; + + struct android_poll_source cmdPollSource; + struct android_poll_source inputPollSource; + + int running; + int stateSaved; + int destroyed; + int redrawNeeded; + AInputQueue* pendingInputQueue; + ANativeWindow* pendingWindow; + ARect pendingContentRect; +}; + +enum { + /** + * Looper data ID of commands coming from the app's main thread, which + * is returned as an identifier from ALooper_pollOnce(). The data for this + * identifier is a pointer to an android_poll_source structure. + * These can be retrieved and processed with android_app_read_cmd() + * and android_app_exec_cmd(). + */ + LOOPER_ID_MAIN = 1, + + /** + * Looper data ID of events coming from the AInputQueue of the + * application's window, which is returned as an identifier from + * ALooper_pollOnce(). The data for this identifier is a pointer to an + * android_poll_source structure. These can be read via the inputQueue + * object of android_app. + */ + LOOPER_ID_INPUT = 2, + + /** + * Start of user-defined ALooper identifiers. + */ + LOOPER_ID_USER = 3, +}; + +enum { + /** + * Command from main thread: the AInputQueue has changed. Upon processing + * this command, android_app->inputQueue will be updated to the new queue + * (or NULL). + */ + APP_CMD_INPUT_CHANGED, + + /** + * Command from main thread: a new ANativeWindow is ready for use. Upon + * receiving this command, android_app->window will contain the new window + * surface. + */ + APP_CMD_INIT_WINDOW, + + /** + * Command from main thread: the existing ANativeWindow needs to be + * terminated. Upon receiving this command, android_app->window still + * contains the existing window; after calling android_app_exec_cmd + * it will be set to NULL. + */ + APP_CMD_TERM_WINDOW, + + /** + * Command from main thread: the current ANativeWindow has been resized. + * Please redraw with its new size. + */ + APP_CMD_WINDOW_RESIZED, + + /** + * Command from main thread: the system needs that the current ANativeWindow + * be redrawn. You should redraw the window before handing this to + * android_app_exec_cmd() in order to avoid transient drawing glitches. + */ + APP_CMD_WINDOW_REDRAW_NEEDED, + + /** + * Command from main thread: the content area of the window has changed, + * such as from the soft input window being shown or hidden. You can + * find the new content rect in android_app::contentRect. + */ + APP_CMD_CONTENT_RECT_CHANGED, + + /** + * Command from main thread: the app's activity window has gained + * input focus. + */ + APP_CMD_GAINED_FOCUS, + + /** + * Command from main thread: the app's activity window has lost + * input focus. + */ + APP_CMD_LOST_FOCUS, + + /** + * Command from main thread: the current device configuration has changed. + */ + APP_CMD_CONFIG_CHANGED, + + /** + * Command from main thread: the system is running low on memory. + * Try to reduce your memory use. + */ + APP_CMD_LOW_MEMORY, + + /** + * Command from main thread: the app's activity has been started. + */ + APP_CMD_START, + + /** + * Command from main thread: the app's activity has been resumed. + */ + APP_CMD_RESUME, + + /** + * Command from main thread: the app should generate a new saved state + * for itself, to restore from later if needed. If you have saved state, + * allocate it with malloc and place it in android_app.savedState with + * the size in android_app.savedStateSize. The will be freed for you + * later. + */ + APP_CMD_SAVE_STATE, + + /** + * Command from main thread: the app's activity has been paused. + */ + APP_CMD_PAUSE, + + /** + * Command from main thread: the app's activity has been stopped. + */ + APP_CMD_STOP, + + /** + * Command from main thread: the app's activity is being destroyed, + * and waiting for the app thread to clean up and exit before proceeding. + */ + APP_CMD_DESTROY, +}; + +/** + * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next + * app command message. + */ +int8_t android_app_read_cmd(struct android_app* android_app); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * initial pre-processing of the given command. You can perform your own + * actions for the command after calling this function. + */ +void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Call with the command returned by android_app_read_cmd() to do the + * final post-processing of the given command. You must have done your own + * actions for the command before calling this function. + */ +void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); + +/** + * Dummy function you can call to ensure glue code isn't stripped. + */ +void app_dummy(); + +/** + * This is the function that application code must implement, representing + * the main entry to the app. + */ +extern void android_main(struct android_app* app); + +#ifdef __cplusplus +} +#endif + +#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/sal/android/lo-bootstrap.c b/sal/android/lo-bootstrap.c new file mode 100644 index 000000000000..6ac285d498d1 --- /dev/null +++ b/sal/android/lo-bootstrap.c @@ -0,0 +1,1057 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * Version: MPL 1.1 / GPLv3+ / LGPLv3+ + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License or as specified alternatively below. You may obtain a copy of + * the License at http: *www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * Major Contributor(s): + * Copyright (C) 2011 Tor Lillqvist (initial developer) + * Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer) + * + * Zip parsing code lifted from Mozilla's other-licenses/android/APKOpen.cpp, + * by Michael Wu . + * + * All Rights Reserved. + * + * For minor contributions see the git repository. + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 3 or later (the "GPLv3+"), or + * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), + * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable + * instead of those above. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +#include + +#include "lo-bootstrap.h" + +#include "android_native_app_glue.c" + +#undef LOGI +#undef LOGW + +#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__)) +#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "lo-bootstrap", __VA_ARGS__)) +#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "lo-bootstrap", __VA_ARGS__)) +#define LOGF(...) ((void)__android_log_print(ANDROID_LOG_FATAL, "lo-bootstrap", __VA_ARGS__)) + +#define ROUND_DOWN(ptr,multiple) (void *)(((unsigned) (ptr)) & ~((multiple)-1)) + +struct engine { + int dummy; +}; + +static struct android_app *app; +static const char **library_locations; +static void *apk_file; +static int apk_file_size; +static int (*lo_main)(int, const char **); +static int lo_main_argc; +static const char **lo_main_argv; +static int sleep_time = 0; + +/* Zip data structures */ + +/* compression methods */ +#define STORE 0 +#define DEFLATE 8 +#define LZMA 14 + +struct local_file_header { + uint32_t signature; + uint16_t min_version; + uint16_t general_flag; + uint16_t compression; + uint16_t lastmod_time; + uint16_t lastmod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_size; + uint16_t extra_field_size; + char data[0]; +} __attribute__((__packed__)); + +struct cdir_entry { + uint32_t signature; + uint16_t creator_version; + uint16_t min_version; + uint16_t general_flag; + uint16_t compression; + uint16_t lastmod_time; + uint16_t lastmod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_size; + uint16_t extra_field_size; + uint16_t file_comment_size; + uint16_t disk_num; + uint16_t internal_attr; + uint32_t external_attr; + uint32_t offset; + char data[0]; +} __attribute__((__packed__)); + +#define CDIR_END_SIG 0x06054b50 + +struct cdir_end { + uint32_t signature; + uint16_t disk_num; + uint16_t cdir_disk; + uint16_t disk_entries; + uint16_t cdir_entries; + uint32_t cdir_size; + uint32_t cdir_offset; + uint16_t comment_size; + char comment[0]; +} __attribute__((__packed__)); + +/* End of Zip data structures */ + +static void +engine_handle_cmd(struct android_app* app, + int32_t cmd) +{ + struct engine* engine = (struct engine*)app->userData; + switch (cmd) { + case APP_CMD_SAVE_STATE: + break; + case APP_CMD_INIT_WINDOW: + break; + case APP_CMD_TERM_WINDOW: + break; + case APP_CMD_GAINED_FOCUS: + break; + case APP_CMD_LOST_FOCUS: + break; + } +} + +static char * +read_section(int fd, + Elf32_Shdr *shdr) +{ + char *result; + + result = malloc(shdr->sh_size); + if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0) { + close(fd); + free(result); + return NULL; + } + if (read(fd, result, shdr->sh_size) < shdr->sh_size) { + close(fd); + free(result); + return NULL; + } + + return result; +} + +static void +free_ptrarray(void **pa) +{ + void **rover = pa; + + while (*rover != NULL) + free(*rover++); + + free(pa); +} + +jobjectArray +Java_org_libreoffice_android_Bootstrap_dlneeds(JNIEnv* env, + jobject clazz, + jstring library) +{ + char **needed; + int n_needed; + const jbyte *libName; + jclass String; + jobjectArray result; + + libName = (*env)->GetStringUTFChars(env, library, NULL); + + needed = lo_dlneeds(libName); + + (*env)->ReleaseStringUTFChars(env, library, libName); + + if (needed == NULL) + return NULL; + + n_needed = 0; + while (needed[n_needed] != NULL) + n_needed++; + + /* Allocate return value */ + + String = (*env)->FindClass(env, "java/lang/String"); + if (String == NULL) { + LOGE("Could not find the String class"); + free_ptrarray((void **) needed); + return NULL; + } + + result = (*env)->NewObjectArray(env, n_needed, String, NULL); + if (result == NULL) { + LOGE("Could not create the String array"); + free_ptrarray((void **) needed); + return NULL; + } + + for (n_needed = 0; needed[n_needed] != NULL; n_needed++) + (*env)->SetObjectArrayElement(env, result, n_needed, (*env)->NewStringUTF(env, needed[n_needed])); + + free_ptrarray((void **) needed); + + return result; +} + +jint +Java_org_libreoffice_android_Bootstrap_dlopen(JNIEnv* env, + jobject clazz, + jstring library) +{ + const jbyte *libName = (*env)->GetStringUTFChars(env, library, NULL); + void *p = lo_dlopen (libName); + + (*env)->ReleaseStringUTFChars(env, library, libName); + + return (jint) p; +} + +jint +Java_org_libreoffice_android_Bootstrap_dlsym(JNIEnv* env, + jobject clazz, + jint handle, + jstring symbol) +{ + const jbyte *symName = (*env)->GetStringUTFChars(env, symbol, NULL); + void *p = lo_dlsym ((void *) handle, symName); + + (*env)->ReleaseStringUTFChars(env, symbol, symName); + + return (jint) p; +} + +jint +Java_org_libreoffice_android_Bootstrap_dlcall(JNIEnv* env, + jobject clazz, + jint function, + jobject argument) +{ + jclass StringArray = (*env)->FindClass(env, "[Ljava/lang/String;"); + + if (StringArray == NULL) { + LOGE("Could not find String[] class"); + return 0; + } + + if ((*env)->IsInstanceOf(env, argument, StringArray)) { + int argc = (*env)->GetArrayLength(env, argument); + const char **argv = malloc(sizeof(char *) * (argc+1)); + int i, result; + for (i = 0; i < argc; i++) { + argv[i] = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, argument, i), NULL); + /* LOGI("argv[%d] = %s", i, argv[i]); */ + } + argv[argc] = NULL; + + result = lo_dlcall_argc_argv((void *) function, argc, argv); + + for (i = 0; i < argc; i++) + (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, argument, i), argv[i]); + + free(argv); + return result; + } + + return 0; +} + +// public static native boolean setup(String dataDir, +// String apkFile, +// String[] ld_library_path); + +jboolean +Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_String_2_3Ljava_lang_String_2 + (JNIEnv* env, + jobject clazz, + jstring dataDir, + jstring apkFile, + jobjectArray ld_library_path) +{ + struct stat st; + int i, n, fd; + const jbyte *dataDirPath; + const jbyte *apkFilePath; + char *lib_dir; + + n = (*env)->GetArrayLength(env, ld_library_path); + + library_locations = malloc((n+2) * sizeof(char *)); + + dataDirPath = (*env)->GetStringUTFChars(env, dataDir, NULL); + + lib_dir = malloc(strlen(dataDirPath) + 5); + strcpy(lib_dir, dataDirPath); + strcat(lib_dir, "/lib"); + + (*env)->ReleaseStringUTFChars(env, dataDir, dataDirPath); + + library_locations[0] = lib_dir; + + for (i = 0; i < n; i++) { + const jbyte *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, ld_library_path, i), NULL); + library_locations[i+1] = strdup(s); + (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, ld_library_path, i), s); + } + + library_locations[n+1] = NULL; + + for (n = 0; library_locations[n] != NULL; n++) + LOGI("library_locations[%d] = %s", n, library_locations[n]); + + apkFilePath = (*env)->GetStringUTFChars(env, apkFile, NULL); + + fd = open(apkFilePath, O_RDONLY); + if (fd == -1) { + LOGE("Could not open %s", apkFilePath); + (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); + return JNI_FALSE; + } + if (fstat(fd, &st) == -1) { + LOGE("Could not fstat %s", apkFilePath); + close(fd); + (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); + return JNI_FALSE; + } + apk_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + + if (apk_file == MAP_FAILED) { + LOGE("Could not mmap %s", apkFilePath); + (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); + return JNI_FALSE; + } + apk_file_size = st.st_size; + + (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); + + return JNI_TRUE; +} + +// public statuc native boolean setup(int lo_main_ptr, +// Object lo_main_argument, +// int lo_main_delay); + +jboolean +Java_org_libreoffice_android_Bootstrap_setup__ILjava_lang_Object_2I(JNIEnv* env, + jobject clazz, + void *lo_main_ptr, + jobject lo_main_argument, + jint lo_main_delay) +{ + jclass StringArray; + int i; + + lo_main = lo_main_ptr; + + StringArray = (*env)->FindClass(env, "[Ljava/lang/String;"); + if (StringArray == NULL) { + LOGE("Could not find String[] class"); + return JNI_FALSE; + } + + if (!(*env)->IsInstanceOf(env, lo_main_argument, StringArray)) { + LOGE("lo_main_argument is not a String[]?"); + return JNI_FALSE; + } + + lo_main_argc = (*env)->GetArrayLength(env, lo_main_argument); + lo_main_argv = malloc(sizeof(char *) * (lo_main_argc+1)); + + for (i = 0; i < lo_main_argc; i++) { + const jbyte *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, lo_main_argument, i), NULL); + lo_main_argv[i] = strdup(s); + (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, lo_main_argument, i), s); + /* LOGI("argv[%d] = %s", i, lo_main_argv[i]); */ + } + lo_main_argv[lo_main_argc] = NULL; + + sleep_time = lo_main_delay; + + return JNI_TRUE; +} + +// public static native int getpid(); + +jint +Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv* env, + jobject clazz) +{ + return getpid(); +} + + +// public static native void system(String cmdline); + +jint +Java_org_libreoffice_android_Bootstrap_system(JNIEnv* env, + jobject clazz, + jstring cmdline) +{ + const jbyte *s = (*env)->GetStringUTFChars(env, cmdline, NULL); + + LOGI("system(%s)", s); + + system(s); + + (*env)->ReleaseStringUTFChars(env, cmdline, s); +} + +// public static native void putenv(String string); + +void +Java_org_libreoffice_android_Bootstrap_putenv(JNIEnv* env, + jobject clazz, + jstring string) +{ + const jbyte *s = (*env)->GetStringUTFChars(env, string, NULL); + + LOGI("putenv(%s)", s); + + putenv(s); + + (*env)->ReleaseStringUTFChars(env, string, s); +} + +char ** +lo_dlneeds(const char *library) +{ + int i, fd; + int n_needed; + char **result; + char *shstrtab, *dynstr; + Elf32_Ehdr hdr; + Elf32_Shdr shdr; + Elf32_Dyn dyn; + + /* Open library and read ELF header */ + + fd = open(library, O_RDONLY); + + if (fd == -1) { + LOGE("lo_dlneeds: Could not open library %s: %s", library, strerror(errno)); + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { + LOGE("lo_dlneeds: Could not read ELF header of %s", library); + close(fd); + return NULL; + } + + /* Read in .shstrtab */ + + if (lseek(fd, hdr.e_shoff + hdr.e_shstrndx * sizeof(shdr), SEEK_SET) < 0) { + LOGE("lo_dlneeds: Could not seek to .shstrtab section header of %s", library); + close(fd); + return NULL; + } + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + LOGE("lo_dlneeds: Could not read section header of %s", library); + close(fd); + return NULL; + } + + shstrtab = read_section(fd, &shdr); + if (shstrtab == NULL) + return NULL; + + /* Read section headers, looking for .dynstr section */ + + if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { + LOGE("lo_dlneeds: Could not seek to section headers of %s", library); + close(fd); + return NULL; + } + for (i = 0; i < hdr.e_shnum; i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + LOGE("lo_dlneeds: Could not read section header of %s", library); + close(fd); + return NULL; + } + if (shdr.sh_type == SHT_STRTAB && + strcmp(shstrtab + shdr.sh_name, ".dynstr") == 0) { + dynstr = read_section(fd, &shdr); + if (dynstr == NULL) { + free(shstrtab); + return NULL; + } + break; + } + } + + if (i == hdr.e_shnum) { + LOGE("lo_dlneeds: No .dynstr section in %s", library); + close(fd); + return NULL; + } + + /* Read section headers, looking for .dynamic section */ + + if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { + LOGE("lo_dlneeds: Could not seek to section headers of %s", library); + close(fd); + return NULL; + } + for (i = 0; i < hdr.e_shnum; i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + LOGE("lo_dlneeds: Could not read section header of %s", library); + close(fd); + return NULL; + } + if (shdr.sh_type == SHT_DYNAMIC) { + int dynoff; + int *libnames; + + /* Count number of DT_NEEDED entries */ + n_needed = 0; + if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { + LOGE("lo_dlneeds: Could not seek to .dynamic section of %s", library); + close(fd); + return NULL; + } + for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { + if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { + LOGE("lo_dlneeds: Could not read .dynamic entry of %s", library); + close(fd); + return NULL; + } + if (dyn.d_tag == DT_NEEDED) + n_needed++; + } + + /* LOGI("Found %d DT_NEEDED libs", n_needed); */ + + result = malloc((n_needed+1) * sizeof(char *)); + + n_needed = 0; + if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { + LOGE("lo_dlneeds: Could not seek to .dynamic section of %s", library); + close(fd); + free(result); + return NULL; + } + for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { + if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { + LOGE("lo_dlneeds: Could not read .dynamic entry in %s", library); + close(fd); + free(result); + return NULL; + } + if (dyn.d_tag == DT_NEEDED) { + /* LOGI("needs: %s\n", dynstr + dyn.d_un.d_val); */ + result[n_needed] = strdup(dynstr + dyn.d_un.d_val); + n_needed++; + } + } + + close(fd); + free(dynstr); + free(shstrtab); + result[n_needed] = NULL; + return result; + } + } + + LOGE("lo_dlneeds: Could not find .dynamic section in %s", library); + close(fd); + return NULL; +} + +void * +lo_dlopen(const char *library) +{ + /* + * We should *not* try to just dlopen() the bare library name + * first, as the stupid dynamic linker remembers for each library + * basename if loading it has failed. Thus if you try loading it + * once, and it fails because of missing needed libraries, and + * your load those, and then try again, it fails with an + * infuriating message "failed to load previously" in the log. + * + * We *must* first dlopen() all needed libraries, recursively. It + * shouldn't matter if we dlopen() a library that already is + * loaded, dlopen() just returns the same value then. + */ + + typedef struct loadedLib { + const char *name; + void *handle; + struct loadedLib *next; + } *loadedLib; + static loadedLib loaded_libraries = NULL; + + loadedLib rover; + loadedLib new_loaded_lib; + + struct stat st; + void *p; + char *full_name; + char **needed; + int i; + int found; + + rover = loaded_libraries; + while (rover != NULL && + strcmp(rover->name, library) != 0) + rover = rover->next; + + if (rover != NULL) + return rover->handle; + + /* LOGI("lo_dlopen(%s)", library); */ + + found = 0; + if (library[0] == '/') { + full_name = strdup(library); + + if (stat(full_name, &st) == 0 && + S_ISREG(st.st_mode)) + found = 1; + else + free(full_name); + } else { + for (i = 0; !found && library_locations[i] != NULL; i++) { + full_name = malloc(strlen(library_locations[i]) + 1 + strlen(library) + 1); + strcpy(full_name, library_locations[i]); + strcat(full_name, "/"); + strcat(full_name, library); + + if (stat(full_name, &st) == 0 && + S_ISREG(st.st_mode)) + found = 1; + else + free(full_name); + } + } + + if (!found) { + LOGE("lo_dlopen: Library %s not found", library); + return NULL; + } + + needed = lo_dlneeds(full_name); + if (needed == NULL) { + free(full_name); + return NULL; + } + + for (i = 0; needed[i] != NULL; i++) { + if (lo_dlopen(needed[i]) == NULL) { + free_ptrarray((void **) needed); + free(full_name); + return NULL; + } + } + free_ptrarray((void **) needed); + + p = dlopen(full_name, RTLD_LOCAL); + LOGI("dlopen(%s) = %p", full_name, p); + free(full_name); + if (p == NULL) + LOGE("lo_dlopen: Error from dlopen(%s): %s", library, dlerror()); + + new_loaded_lib = malloc(sizeof(*new_loaded_lib)); + new_loaded_lib->name = strdup(library); + new_loaded_lib->handle = p; + + new_loaded_lib->next = loaded_libraries; + loaded_libraries = new_loaded_lib; + + return p; +} + +void * +lo_dlsym(void *handle, + const char *symbol) +{ + void *p = dlsym(handle, symbol); + /* LOGI("dlsym(%p, %s) = %p", handle, symbol, p); */ + if (p == NULL) + LOGE("lo_dlsym: %s", dlerror()); + return p; +} + +int +lo_dladdr(void *addr, + Dl_info *info) +{ + FILE *maps; + char line[200]; + int result; + int found; + + result = dladdr(addr, info); + if (result == 0) { + /* LOGI("dladdr(%p) = 0", addr); */ + return 0; + } + + maps = fopen("/proc/self/maps", "r"); + if (maps == NULL) { + LOGE("lo_dladdr: Could not open /proc/self/maps: %s", strerror(errno)); + return 0; + } + + found = 0; + while (fgets(line, sizeof(line), maps) != NULL && + line[strlen(line)-1] == '\n') { + void *lo, *hi; + char file[sizeof(line)]; + file[0] = '\0'; + if (sscanf(line, "%x-%x %*s %*x %*x:%*x %*d %[^\n]", &lo, &hi, file) == 3) { + /* LOGI("got %p-%p: %s", lo, hi, file); */ + if (addr >= lo && addr < hi) { + if (info->dli_fbase != lo) { + LOGE("lo_dladdr: Base for %s in /proc/self/maps %p doesn't match what dladdr() said", file, lo); + fclose(maps); + return 0; + } + /* LOGI("dladdr(%p) = { %s:%p, %s:%p }: %s", + addr, + info->dli_fname, info->dli_fbase, + info->dli_sname ? info->dli_sname : "(none)", info->dli_saddr, + file); */ + info->dli_fname = strdup(file); + found = 1; + break; + } + } + } + if (!found) + LOGE("lo_dladdr: Did not find %p in /proc/self/maps", addr); + fclose(maps); + + return result; +} + +static uint32_t cdir_entry_size (struct cdir_entry *entry) +{ + return sizeof(*entry) + + letoh16(entry->filename_size) + + letoh16(entry->extra_field_size) + + letoh16(entry->file_comment_size); +} + +static struct cdir_entry * +find_cdir_entry (struct cdir_entry *entry, int count, const char *name) +{ + size_t name_size = strlen(name); + while (count--) { + if (letoh16(entry->filename_size) == name_size && + !memcmp(entry->data, name, name_size)) + return entry; + entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry)); + } + return NULL; +} + +void * +lo_apkentry(const char *filename, + size_t *size) +{ + struct cdir_end *dirend = (struct cdir_end *)((char *) apk_file + apk_file_size - sizeof(*dirend)); + uint32_t cdir_offset; + uint16_t cdir_entries; + struct cdir_entry *cdir_start; + struct cdir_entry *entry; + struct local_file_header *file; + void *data; + + while ((void *)dirend > apk_file && + letoh32(dirend->signature) != CDIR_END_SIG) + dirend = (struct cdir_end *)((char *)dirend - 1); + if (letoh32(dirend->signature) != CDIR_END_SIG) { + LOGE("lo_apkentry: Could not find end of central directory record"); + return; + } + + cdir_offset = letoh32(dirend->cdir_offset); + cdir_entries = letoh16(dirend->cdir_entries); + cdir_start = (struct cdir_entry *)((char *)apk_file + cdir_offset); + + if (*filename == '/') + filename++; + + entry = find_cdir_entry(cdir_start, cdir_entries, filename); + + if (entry == NULL) { + LOGE("lo_apkentry: Could not find %s", filename); + return NULL; + } + file = (struct local_file_header *)((char *)apk_file + letoh32(entry->offset)); + + if (letoh16(file->compression) != STORE) { + LOGE("lo_apkentry: File %s is compressed", filename); + return NULL; + } + + data = ((char *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size); + *size = file->uncompressed_size; + + /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */ + + return data; +} + +int +lo_dlcall_argc_argv(void *function, + int argc, + const char **argv) +{ + int (*fp)(int, const char **) = function; + int result = fp(argc, argv); + + return result; +} + +/* There is a bug in std::type_info::operator== and + * std::type_info::before() in libgnustl_shared.so in NDK r7 at + * least. They compare the type name pointers instead of comparing the + * type name strings. See + * http://code.google.com/p/android/issues/detail?id=22165 . So patch + * that, poke in jumps to our own code snippets below instead. + */ + +/* Replacement std::type_info::operator== */ + +__asm(" .arm\n" + " .global replacement_operator_equals_arm\n" + "replacement_operator_equals_arm:\n" + " push {lr}\n" + + /* Load name pointers into r0 and r1 */ + " ldr r0, [r0, #4]\n" + " ldr r1, [r1, #4]\n" + + /* First compare pointers */ + " cmp r0, r1\n" + + /* If equal, return true */ + " beq .L.equals.1\n" + + /* Otherwise call strcmp */ + " bl strcmp\n" + + /* And return true or false */ + " cmp r0, #0\n" + " moveq r0, #1\n" + " movne r0, #0\n" + " b .L.equals.9\n" + + ".L.equals.1:\n" + " mov r0, #1\n" + + ".L.equals.9:\n" + " pop {pc}\n" + ); + +extern unsigned int replacement_operator_equals_arm; + +/* The ARM (not Thumb) code of the operator== in NDK r7 */ +static unsigned int expected_operator_equals_r7_code[] = { + 0xe5903004, /* ldr r3, [r0, #4] */ + 0xe5910004, /* ldr r0, [r1, #4] */ + 0xe1530000, /* cmp r3, r0 */ + 0x13a00000, /* movne, #0 */ + 0x03a00001, /* moveq r0, #1 */ + 0xe12fff1e /* bx lr */ +}; + +/* Ditto for std::type_info::before() */ + +__asm(" .arm\n" + " .global replacement_method_before_arm\n" + "replacement_method_before_arm:\n" + " push {lr}\n" + + /* Load name pointers into r0 and r1 */ + " ldr r0, [r0, #4]\n" + " ldr r1, [r1, #4]\n" + + /* First compare pointers */ + " cmp r0, r1\n" + + /* If equal, return false */ + " beq .L.before.1\n" + + /* Otherwise call strcmp */ + " bl strcmp\n" + + /* And return true or false */ + " cmp r0, #0\n" + " movlt r0, #1\n" + " movge r0, #0\n" + " b .L.before.9\n" + + ".L.before.1:\n" + " mov r0, #0\n" + + ".L.before.9:\n" + " pop {pc}\n" + ); + +extern unsigned int replacement_method_before_arm; + +static unsigned int expected_method_before_r7_code[] = { + 0xe5903004, /* ldr r3, [r0, #4] */ + 0xe5910004, /* ldr r0, [r1, #4] */ + 0xe1530000, /* cmp r3, r0 */ + 0x23a00000, /* movcs r0, #0 */ + 0x33a00001, /* movcc r0, #1 */ + 0xe12fff1e /* bx lr */ +}; + +static void +patch(const char *symbol, + const char *plaintext, + unsigned *expected_code, + size_t expected_code_size, + unsigned *replacement_code) +{ + + void *libgnustl_shared; + void *code; + + void *base; + size_t size; + + /* libgnustl_shared.so should be already loaded as we build + * all LO code against it, so as we have loaded the .so + * containing lo_main() already, libgnustl_shared.so will have + * been brought in, too. + */ + libgnustl_shared = dlopen("libgnustl_shared.so", RTLD_LOCAL); + if (libgnustl_shared == NULL) { + LOGF("android_main: libgnustl_shared.so not mapped??"); + exit(0); + } + + code = dlsym(libgnustl_shared, symbol); + if (code == NULL) { + LOGF("android_main: %s not found!?", plaintext); + exit(0); + } + /* LOGI("%s is at %p", plaintext, operator_equals); */ + + if ((((unsigned) code) & 0x03) != 0) { + LOGE("android_main: Address of %s is not at word boundary, huh?", plaintext); + return; + } + + if ((((unsigned) &replacement_code) & 0x03) != 0) { + LOGE("android_main: Address of replacement %s is not at word boundary, huh?", plaintext); + return; + } + + if (memcmp(code, expected_code, expected_code_size) != 0) { + LOGI("android_main: Code for %s does not match that in NDK r7; not patching it", plaintext); + return; + } + + base = ROUND_DOWN(code, getpagesize()); + size = code + sizeof(expected_code_size) - ROUND_DOWN(code, getpagesize()); + if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { + LOGE("android_main: mprotect() failed: %s", strerror(errno)); + return; + } + + /* Poke a "b replacement_code" into it instead */ + *((unsigned *) code) = + (0xEA000000 | + ((((int) replacement_code - ((int) code + 8)) / 4) & 0x00FFFFFF)); +} + +static void +patch_libgnustl_shared(void) +{ + patch("_ZNKSt9type_infoeqERKS_", + "std::type_info::operator==", + expected_operator_equals_r7_code, + sizeof(expected_operator_equals_r7_code), + &replacement_operator_equals_arm); + + patch("_ZNKSt9type_info6beforeERKS_", + "std::type_info::before()", + expected_method_before_r7_code, + sizeof(expected_method_before_r7_code), + &replacement_method_before_arm); +} + +void +Java_org_libreoffice_android_Bootstrap_patch_libgnustl_shared(JNIEnv* env, + jobject clazz) +{ + patch_libgnustl_shared(); +} + +JavaVM * +lo_get_javavm(void) +{ + return app->activity->vm; +} + +void +android_main(struct android_app* state) +{ + struct engine engine; + Dl_info lo_main_info; + + app = state; + + memset(&engine, 0, sizeof(engine)); + state->userData = &engine; + state->onAppCmd = engine_handle_cmd; + + if (lo_dladdr(lo_main, &lo_main_info) != 0) { + lo_main_argv[0] = lo_main_info.dli_fname; + } + + if (sleep_time != 0) + sleep(sleep_time); + + patch_libgnustl_shared(); + + lo_main(lo_main_argc, lo_main_argv); + + exit(0); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/android/makefile.mk b/sal/android/makefile.mk new file mode 100644 index 000000000000..74de4ee1997a --- /dev/null +++ b/sal/android/makefile.mk @@ -0,0 +1,59 @@ +# Version: MPL 1.1 / GPLv3+ / LGPLv3+ +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (the "License"); you may not use this file except in compliance with +# the License or as specified alternatively below. You may obtain a copy of +# the License at http:#www.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# Major Contributor(s): +# Copyright (C) 2011 Tor Lillqvist (initial developer) +# Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer) +# +# Zip parsing code lifted from Mozilla's other-licenses/android/APKOpen.cpp, +# by Michael Wu . +# +# All Rights Reserved. +# +# For minor contributions see the git repository. +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 3 or later (the "GPLv3+"), or +# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), +# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable +# instead of those above. + +PRJ = .. +PRJNAME = sal +TARGET = lo-bootstrap + +# Too many warnings from android_native_app_glue.[ch] +EXTERNAL_WARNINGS_NOT_ERRORS = TRUE + +.INCLUDE : settings.mk + +.IF "$(OS)" != "ANDROID" +ALL: + @: do nothing +.ENDIF + +SHL1TARGET = $(TARGET) + +SHL1OBJS = \ + $(SLO)$/lo-bootstrap.obj + +# We don't want to link liblo-bootstrap.so against +# libgnustl_shared.so. The Android dynamic linker won't find it +# anyway. One very point of liblo-bootstrap is its wrapper for +# dlopen() that searches also in the app's lib folder for needed +# shared libraries. So just re-define STDSHLCUIMT. + +STDSHLCUIMT := -llog -landroid + +# Also don't pointless + +.INCLUDE : target.mk diff --git a/sal/osl/android/.gitignore b/sal/osl/android/.gitignore deleted file mode 100644 index 9034b9895557..000000000000 --- a/sal/osl/android/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -assets -bin -gen -libs -obj -local.properties \ No newline at end of file diff --git a/sal/osl/android/AndroidManifest.xml b/sal/osl/android/AndroidManifest.xml deleted file mode 100644 index 159bbf105d75..000000000000 --- a/sal/osl/android/AndroidManifest.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/sal/osl/android/Makefile b/sal/osl/android/Makefile deleted file mode 100644 index b0368e7d3627..000000000000 --- a/sal/osl/android/Makefile +++ /dev/null @@ -1,126 +0,0 @@ -# The full path the the app's directory needs to be supplied in a -# couple of places, unfortunately. - -APP_DATA_PATH=/data/data/org.libreoffice.android - -NDK_HOME:=$(shell type -p ndk-build) -NDK_HOME:=$(shell dirname $(NDK_HOME)) - -SODEST=libs/armeabi-v7a -OBJLOCAL=obj/local/armeabi-v7a - -define COPY -cp $(1) $(SODEST)$(if $(2),/$(2)) && \ -arm-linux-androideabi-strip --strip-debug $(SODEST)$(if $(2),/$(2),/$(notdir $(1))) && \ -cp $(1) $(OBJLOCAL)$(if $(2),/$(2)) -endef - -# The default target just builds. - -# lo-bootstrap.jar is used from other LO apps than the test ones we -# include in the .apk here. - -all: build-ant lo-bootstrap.jar - -build-ndk: - ndk-build V=1 -# -# Copy shared libraries we need to libs/armeabi-v7a so that ant will -# include them in the .apk. -# -# Copy them to obj/local/armeabi-v7a, too, where gdb will look for -# them. -# -# First the cppunittester "program" from here, which as all "program" -# files we build for Android actually is a shared object. - $(call COPY,../../$(INPATH)/bin/cppunittester,libcppunittester.so) -# -# The cppunit library - $(call COPY,$(OUTDIR)/lib/libcppunit-1.12.so) -# -# cppunit "plug-ins", first ones from sal -# - $(call COPY,../../$(INPATH)/lib/*.so) -# -# and ones from other modules. Note that depending on when you try -# this, these might not have been built yet. -# - for F in $(SRC_ROOT)/cppu/$(INPATH)/lib/qa_*.so; do \ - $(call COPY,$${F},`basename $${F}`); \ - done -# - for F in i18npool_test_breakiterator; do \ - $(call COPY,$(WORKDIR)/LinkTarget/CppunitTest/libtest_$${F}.so); \ - done -# -# Other "programs" - $(call COPY,$(OUTDIR)/bin/uno,libunoexe.so) -# -# Libs and UNO components that the tests from other modules need. -# - for F in $(strip \ - comphelpgcc3 \ - gcc3_uno \ - i18nisolang1gcc3 \ - i18nutilgcc3 \ - icudatalo \ - icui18nlo \ - icuuclo \ - localedata_en \ - localedata_others \ - reg \ - store \ - ucbhelper4gcc3 \ - uno_cppu \ - uno_salhelpergcc3 \ - uno_cppuhelpergcc3 \ - unoexceptionprotector \ - xml2 \ - xmlreader \ - bootstrap.uno \ - i18npool.uno); do \ - $(call COPY,$(OUTDIR)/lib/lib$${F}.so); \ - done -# -# Then the shared GNU C++ library - $(call COPY,$(NDK_HOME)/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/libgnustl_shared.so) -# -# bridgetest components, not in solver - for F in bridgetest constructors cppobj; do \ - $(call COPY,$(SRC_ROOT)/testtools/$(INPATH)/lib/lib$${F}.uno.so); \ - done -# -# Then other "assets". Let the directory structure under assets mimic -# that under solver for now. - mkdir -p assets/bin assets/lib assets/xml/ure assets/ComponentTarget/i18npool/util - cp $(OUTDIR)/bin/udkapi.rdb assets/bin - cp $(OUTDIR)/bin/types.rdb assets/bin - cp $(OUTDIR)/xml/ure/services.rdb assets/xml/ure - cp $(WORKDIR)/ComponentTarget/i18npool/util/i18npool.component assets/ComponentTarget/i18npool/util - for F in uno_services uno_types; do \ - sed -e 's!uri="./!uri="$(APP_DATA_PATH)/lib/!g' <$(SRC_ROOT)/testtools/$(INPATH)/lib/$${F}.rdb >assets/lib/$${F}.rdb; \ - done - -build-ant: build-ndk - unset JAVA_HOME && ant debug - -install: build-ndk - unset JAVA_HOME && ant debug install - @echo - @echo 'Run it with something like what "make run" does (see Makefile)' - @echo - -lo-bootstrap.jar: build-ant - cd bin/classes && jar cvf ../../lo-bootstrap.jar fi/iki/tml org/libreoffice/android/Bootstrap.class - -run: install -# Note: these are just examples. -# - adb shell am start -n org.libreoffice.android/.Bootstrap -e lo-main-library libcppunittester -e lo-main-cmdline "$(APP_DATA_PATH)/lib/libqa_sal_types.so" -# - adb shell am start -n org.libreoffice.android/.Bootstrap -e lo-main-library libcppunittester -e lo-main-cmdline "$(APP_DATA_PATH)/lib/libtest_i18npool_test_breakiterator.so --protector libunoexceptionprotector.so unoexceptionprotector '-env:UNO_TYPES=file:///assets/bin/udkapi.rdb file:///assets/bin/types.rdb' '-env:UNO_SERVICES=file:///assets/xml/ure/services.rdb file:///assets/ComponentTarget/i18npool/util/i18npool.component' -env:LO_LIB_DIR=file://$(APP_DATA_PATH)/lib" -# - adb shell am start -n org.libreoffice.android/.Bootstrap -e lo-main-library libunoexe -e lo-main-cmdline "-ro /assets/lib/uno_services.rdb -ro /assets/lib/uno_types.rdb -s com.sun.star.test.bridge.BridgeTest -- com.sun.star.test.bridge.CppTestObject" -e lo-main-delay 2 - -clean: - rm -rf bin assets diff --git a/sal/osl/android/ant.properties b/sal/osl/android/ant.properties deleted file mode 100644 index ee52d86d94a4..000000000000 --- a/sal/osl/android/ant.properties +++ /dev/null @@ -1,17 +0,0 @@ -# This file is used to override default values used by the Ant build system. -# -# This file must be checked in Version Control Systems, as it is -# integral to the build system of your project. - -# This file is only used by the Ant script. - -# You can use this to override default values such as -# 'source.dir' for the location of your java source folder and -# 'out.dir' for the location of your output folder. - -# You can also use it define how the release builds are signed by declaring -# the following properties: -# 'key.store' for the location of your keystore and -# 'key.alias' for the name of the key to use. -# The password will be asked during the build when you use the 'release' target. - diff --git a/sal/osl/android/build.xml b/sal/osl/android/build.xml deleted file mode 100644 index 6b091c49c2bd..000000000000 --- a/sal/osl/android/build.xml +++ /dev/null @@ -1,114 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/sal/osl/android/jni/android_native_app_glue.c b/sal/osl/android/jni/android_native_app_glue.c deleted file mode 100644 index 8ff9f69d9946..000000000000 --- a/sal/osl/android/jni/android_native_app_glue.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#include - -#include -#include -#include -#include - -#include "android_native_app_glue.h" -#include - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "threaded_app", __VA_ARGS__)) - -static void free_saved_state(struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - if (android_app->savedState != NULL) { - free(android_app->savedState); - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - pthread_mutex_unlock(&android_app->mutex); -} - -int8_t android_app_read_cmd(struct android_app* android_app) { - int8_t cmd; - if (read(android_app->msgread, &cmd, sizeof(cmd)) == sizeof(cmd)) { - switch (cmd) { - case APP_CMD_SAVE_STATE: - free_saved_state(android_app); - break; - } - return cmd; - } else { - LOGI("No data on command pipe!"); - } - return -1; -} - -static void print_cur_config(struct android_app* android_app) { - char lang[2], country[2]; - AConfiguration_getLanguage(android_app->config, lang); - AConfiguration_getCountry(android_app->config, country); - - LOGI("Config: mcc=%d mnc=%d lang=%c%c cnt=%c%c orien=%d touch=%d dens=%d " - "keys=%d nav=%d keysHid=%d navHid=%d sdk=%d size=%d long=%d " - "modetype=%d modenight=%d", - AConfiguration_getMcc(android_app->config), - AConfiguration_getMnc(android_app->config), - lang[0], lang[1], country[0], country[1], - AConfiguration_getOrientation(android_app->config), - AConfiguration_getTouchscreen(android_app->config), - AConfiguration_getDensity(android_app->config), - AConfiguration_getKeyboard(android_app->config), - AConfiguration_getNavigation(android_app->config), - AConfiguration_getKeysHidden(android_app->config), - AConfiguration_getNavHidden(android_app->config), - AConfiguration_getSdkVersion(android_app->config), - AConfiguration_getScreenSize(android_app->config), - AConfiguration_getScreenLong(android_app->config), - AConfiguration_getUiModeType(android_app->config), - AConfiguration_getUiModeNight(android_app->config)); -} - -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case APP_CMD_INPUT_CHANGED: - LOGI("APP_CMD_INPUT_CHANGED\n"); - pthread_mutex_lock(&android_app->mutex); - if (android_app->inputQueue != NULL) { - AInputQueue_detachLooper(android_app->inputQueue); - } - android_app->inputQueue = android_app->pendingInputQueue; - if (android_app->inputQueue != NULL) { - LOGI("Attaching input queue to looper"); - AInputQueue_attachLooper(android_app->inputQueue, - android_app->looper, LOOPER_ID_INPUT, NULL, - &android_app->inputPollSource); - } - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_INIT_WINDOW: - LOGI("APP_CMD_INIT_WINDOW\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = android_app->pendingWindow; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_TERM_WINDOW: - LOGI("APP_CMD_TERM_WINDOW\n"); - pthread_cond_broadcast(&android_app->cond); - break; - - case APP_CMD_RESUME: - case APP_CMD_START: - case APP_CMD_PAUSE: - case APP_CMD_STOP: - LOGI("activityState=%d\n", cmd); - pthread_mutex_lock(&android_app->mutex); - android_app->activityState = cmd; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_CONFIG_CHANGED: - LOGI("APP_CMD_CONFIG_CHANGED\n"); - AConfiguration_fromAssetManager(android_app->config, - android_app->activity->assetManager); - print_cur_config(android_app); - break; - - case APP_CMD_DESTROY: - LOGI("APP_CMD_DESTROY\n"); - android_app->destroyRequested = 1; - break; - } -} - -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd) { - switch (cmd) { - case APP_CMD_TERM_WINDOW: - LOGI("APP_CMD_TERM_WINDOW\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->window = NULL; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_SAVE_STATE: - LOGI("APP_CMD_SAVE_STATE\n"); - pthread_mutex_lock(&android_app->mutex); - android_app->stateSaved = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - break; - - case APP_CMD_RESUME: - free_saved_state(android_app); - break; - } -} - -void app_dummy() { - -} - -static void android_app_destroy(struct android_app* android_app) { - LOGI("android_app_destroy!"); - free_saved_state(android_app); - pthread_mutex_lock(&android_app->mutex); - if (android_app->inputQueue != NULL) { - AInputQueue_detachLooper(android_app->inputQueue); - } - AConfiguration_delete(android_app->config); - android_app->destroyed = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - // Can't touch android_app object after this. -} - -static void process_input(struct android_app* app, struct android_poll_source* source) { - AInputEvent* event = NULL; - (void) source; - if (AInputQueue_getEvent(app->inputQueue, &event) >= 0) { - LOGI("New input event: type=%d\n", AInputEvent_getType(event)); - if (AInputQueue_preDispatchEvent(app->inputQueue, event)) { - return; - } - int32_t handled = 0; - if (app->onInputEvent != NULL) handled = app->onInputEvent(app, event); - AInputQueue_finishEvent(app->inputQueue, event, handled); - } else { - LOGI("Failure reading next input event: %s\n", strerror(errno)); - } -} - -static void process_cmd(struct android_app* app, struct android_poll_source* source) { - int8_t cmd = android_app_read_cmd(app); - (void) source; - android_app_pre_exec_cmd(app, cmd); - if (app->onAppCmd != NULL) app->onAppCmd(app, cmd); - android_app_post_exec_cmd(app, cmd); -} - -static void* android_app_entry(void* param) { - struct android_app* android_app = (struct android_app*)param; - - android_app->config = AConfiguration_new(); - AConfiguration_fromAssetManager(android_app->config, android_app->activity->assetManager); - - print_cur_config(android_app); - - android_app->cmdPollSource.id = LOOPER_ID_MAIN; - android_app->cmdPollSource.app = android_app; - android_app->cmdPollSource.process = process_cmd; - android_app->inputPollSource.id = LOOPER_ID_INPUT; - android_app->inputPollSource.app = android_app; - android_app->inputPollSource.process = process_input; - - ALooper* looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS); - ALooper_addFd(looper, android_app->msgread, LOOPER_ID_MAIN, ALOOPER_EVENT_INPUT, NULL, - &android_app->cmdPollSource); - android_app->looper = looper; - - pthread_mutex_lock(&android_app->mutex); - android_app->running = 1; - pthread_cond_broadcast(&android_app->cond); - pthread_mutex_unlock(&android_app->mutex); - - android_main(android_app); - - android_app_destroy(android_app); - return NULL; -} - -// -------------------------------------------------------------------- -// Native activity interaction (called from main thread) -// -------------------------------------------------------------------- - -static struct android_app* android_app_create(ANativeActivity* activity, - void* savedState, size_t savedStateSize) { - struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app)); - memset(android_app, 0, sizeof(struct android_app)); - android_app->activity = activity; - - pthread_mutex_init(&android_app->mutex, NULL); - pthread_cond_init(&android_app->cond, NULL); - - if (savedState != NULL) { - android_app->savedState = malloc(savedStateSize); - android_app->savedStateSize = savedStateSize; - memcpy(android_app->savedState, savedState, savedStateSize); - } - - int msgpipe[2]; - if (pipe(msgpipe)) { - LOGI("could not create pipe: %s", strerror(errno)); - } - android_app->msgread = msgpipe[0]; - android_app->msgwrite = msgpipe[1]; - - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - pthread_create(&android_app->thread, &attr, android_app_entry, android_app); - - // Wait for thread to start. - pthread_mutex_lock(&android_app->mutex); - while (!android_app->running) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); - - return android_app; -} - -static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { - if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { - LOGI("Failure writing android_app cmd: %s\n", strerror(errno)); - } -} - -static void android_app_set_input(struct android_app* android_app, AInputQueue* inputQueue) { - pthread_mutex_lock(&android_app->mutex); - android_app->pendingInputQueue = inputQueue; - android_app_write_cmd(android_app, APP_CMD_INPUT_CHANGED); - while (android_app->inputQueue != android_app->pendingInputQueue) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_set_window(struct android_app* android_app, ANativeWindow* window) { - pthread_mutex_lock(&android_app->mutex); - if (android_app->pendingWindow != NULL) { - android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); - } - android_app->pendingWindow = window; - if (window != NULL) { - android_app_write_cmd(android_app, APP_CMD_INIT_WINDOW); - } - while (android_app->window != android_app->pendingWindow) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_set_activity_state(struct android_app* android_app, int8_t cmd) { - pthread_mutex_lock(&android_app->mutex); - android_app_write_cmd(android_app, cmd); - while (android_app->activityState != cmd) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); -} - -static void android_app_free(struct android_app* android_app) { - pthread_mutex_lock(&android_app->mutex); - android_app_write_cmd(android_app, APP_CMD_DESTROY); - while (!android_app->destroyed) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - pthread_mutex_unlock(&android_app->mutex); - - close(android_app->msgread); - close(android_app->msgwrite); - pthread_cond_destroy(&android_app->cond); - pthread_mutex_destroy(&android_app->mutex); - free(android_app); -} - -static void onDestroy(ANativeActivity* activity) { - LOGI("Destroy: %p\n", activity); - android_app_free((struct android_app*)activity->instance); -} - -static void onStart(ANativeActivity* activity) { - LOGI("Start: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_START); -} - -static void onResume(ANativeActivity* activity) { - LOGI("Resume: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_RESUME); -} - -static void* onSaveInstanceState(ANativeActivity* activity, size_t* outLen) { - struct android_app* android_app = (struct android_app*)activity->instance; - void* savedState = NULL; - - LOGI("SaveInstanceState: %p\n", activity); - pthread_mutex_lock(&android_app->mutex); - android_app->stateSaved = 0; - android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); - while (!android_app->stateSaved) { - pthread_cond_wait(&android_app->cond, &android_app->mutex); - } - - if (android_app->savedState != NULL) { - savedState = android_app->savedState; - *outLen = android_app->savedStateSize; - android_app->savedState = NULL; - android_app->savedStateSize = 0; - } - - pthread_mutex_unlock(&android_app->mutex); - - return savedState; -} - -static void onPause(ANativeActivity* activity) { - LOGI("Pause: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_PAUSE); -} - -static void onStop(ANativeActivity* activity) { - LOGI("Stop: %p\n", activity); - android_app_set_activity_state((struct android_app*)activity->instance, APP_CMD_STOP); -} - -static void onConfigurationChanged(ANativeActivity* activity) { - struct android_app* android_app = (struct android_app*)activity->instance; - LOGI("ConfigurationChanged: %p\n", activity); - android_app_write_cmd(android_app, APP_CMD_CONFIG_CHANGED); -} - -static void onLowMemory(ANativeActivity* activity) { - struct android_app* android_app = (struct android_app*)activity->instance; - LOGI("LowMemory: %p\n", activity); - android_app_write_cmd(android_app, APP_CMD_LOW_MEMORY); -} - -static void onWindowFocusChanged(ANativeActivity* activity, int focused) { - LOGI("WindowFocusChanged: %p -- %d\n", activity, focused); - android_app_write_cmd((struct android_app*)activity->instance, - focused ? APP_CMD_GAINED_FOCUS : APP_CMD_LOST_FOCUS); -} - -static void onNativeWindowCreated(ANativeActivity* activity, ANativeWindow* window) { - LOGI("NativeWindowCreated: %p -- %p\n", activity, window); - android_app_set_window((struct android_app*)activity->instance, window); -} - -static void onNativeWindowDestroyed(ANativeActivity* activity, ANativeWindow* window) { - LOGI("NativeWindowDestroyed: %p -- %p\n", activity, window); - android_app_set_window((struct android_app*)activity->instance, NULL); -} - -static void onInputQueueCreated(ANativeActivity* activity, AInputQueue* queue) { - LOGI("InputQueueCreated: %p -- %p\n", activity, queue); - android_app_set_input((struct android_app*)activity->instance, queue); -} - -static void onInputQueueDestroyed(ANativeActivity* activity, AInputQueue* queue) { - LOGI("InputQueueDestroyed: %p -- %p\n", activity, queue); - android_app_set_input((struct android_app*)activity->instance, NULL); -} - -void ANativeActivity_onCreate(ANativeActivity* activity, - void* savedState, size_t savedStateSize) { - LOGI("Creating: %p\n", activity); - activity->callbacks->onDestroy = onDestroy; - activity->callbacks->onStart = onStart; - activity->callbacks->onResume = onResume; - activity->callbacks->onSaveInstanceState = onSaveInstanceState; - activity->callbacks->onPause = onPause; - activity->callbacks->onStop = onStop; - activity->callbacks->onConfigurationChanged = onConfigurationChanged; - activity->callbacks->onLowMemory = onLowMemory; - activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; - activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; - activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; - activity->callbacks->onInputQueueCreated = onInputQueueCreated; - activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; - - activity->instance = android_app_create(activity, savedState, savedStateSize); -} diff --git a/sal/osl/android/jni/android_native_app_glue.h b/sal/osl/android/jni/android_native_app_glue.h deleted file mode 100644 index 1b8c1f10725d..000000000000 --- a/sal/osl/android/jni/android_native_app_glue.h +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -#ifndef _ANDROID_NATIVE_APP_GLUE_H -#define _ANDROID_NATIVE_APP_GLUE_H - -#include -#include -#include - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * The native activity interface provided by - * is based on a set of application-provided callbacks that will be called - * by the Activity's main thread when certain events occur. - * - * This means that each one of this callbacks _should_ _not_ block, or they - * risk having the system force-close the application. This programming - * model is direct, lightweight, but constraining. - * - * The 'threaded_native_app' static library is used to provide a different - * execution model where the application can implement its own main event - * loop in a different thread instead. Here's how it works: - * - * 1/ The application must provide a function named "android_main()" that - * will be called when the activity is created, in a new thread that is - * distinct from the activity's main thread. - * - * 2/ android_main() receives a pointer to a valid "android_app" structure - * that contains references to other important objects, e.g. the - * ANativeActivity obejct instance the application is running in. - * - * 3/ the "android_app" object holds an ALooper instance that already - * listens to two important things: - * - * - activity lifecycle events (e.g. "pause", "resume"). See APP_CMD_XXX - * declarations below. - * - * - input events coming from the AInputQueue attached to the activity. - * - * Each of these correspond to an ALooper identifier returned by - * ALooper_pollOnce with values of LOOPER_ID_MAIN and LOOPER_ID_INPUT, - * respectively. - * - * Your application can use the same ALooper to listen to additional - * file-descriptors. They can either be callback based, or with return - * identifiers starting with LOOPER_ID_USER. - * - * 4/ Whenever you receive a LOOPER_ID_MAIN or LOOPER_ID_INPUT event, - * the returned data will point to an android_poll_source structure. You - * can call the process() function on it, and fill in android_app->onAppCmd - * and android_app->onInputEvent to be called for your own processing - * of the event. - * - * Alternatively, you can call the low-level functions to read and process - * the data directly... look at the process_cmd() and process_input() - * implementations in the glue to see how to do this. - * - * See the sample named "native-activity" that comes with the NDK with a - * full usage example. Also look at the JavaDoc of NativeActivity. - */ - -struct android_app; - -/** - * Data associated with an ALooper fd that will be returned as the "outData" - * when that source has data ready. - */ -struct android_poll_source { - // The identifier of this source. May be LOOPER_ID_MAIN or - // LOOPER_ID_INPUT. - int32_t id; - - // The android_app this ident is associated with. - struct android_app* app; - - // Function to call to perform the standard processing of data from - // this source. - void (*process)(struct android_app* app, struct android_poll_source* source); -}; - -/** - * This is the interface for the standard glue code of a threaded - * application. In this model, the application's code is running - * in its own thread separate from the main thread of the process. - * It is not required that this thread be associated with the Java - * VM, although it will need to be in order to make JNI calls any - * Java objects. - */ -struct android_app { - // The application can place a pointer to its own state object - // here if it likes. - void* userData; - - // Fill this in with the function to process main app commands (APP_CMD_*) - void (*onAppCmd)(struct android_app* app, int32_t cmd); - - // Fill this in with the function to process input events. At this point - // the event has already been pre-dispatched, and it will be finished upon - // return. Return 1 if you have handled the event, 0 for any default - // dispatching. - int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event); - - // The ANativeActivity object instance that this app is running in. - ANativeActivity* activity; - - // The current configuration the app is running in. - AConfiguration* config; - - // This is the last instance's saved state, as provided at creation time. - // It is NULL if there was no state. You can use this as you need; the - // memory will remain around until you call android_app_exec_cmd() for - // APP_CMD_RESUME, at which point it will be freed and savedState set to NULL. - // These variables should only be changed when processing a APP_CMD_SAVE_STATE, - // at which point they will be initialized to NULL and you can malloc your - // state and place the information here. In that case the memory will be - // freed for you later. - void* savedState; - size_t savedStateSize; - - // The ALooper associated with the app's thread. - ALooper* looper; - - // When non-NULL, this is the input queue from which the app will - // receive user input events. - AInputQueue* inputQueue; - - // When non-NULL, this is the window surface that the app can draw in. - ANativeWindow* window; - - // Current content rectangle of the window; this is the area where the - // window's content should be placed to be seen by the user. - ARect contentRect; - - // Current state of the app's activity. May be either APP_CMD_START, - // APP_CMD_RESUME, APP_CMD_PAUSE, or APP_CMD_STOP; see below. - int activityState; - - // This is non-zero when the application's NativeActivity is being - // destroyed and waiting for the app thread to complete. - int destroyRequested; - - // ------------------------------------------------- - // Below are "private" implementation of the glue code. - - pthread_mutex_t mutex; - pthread_cond_t cond; - - int msgread; - int msgwrite; - - pthread_t thread; - - struct android_poll_source cmdPollSource; - struct android_poll_source inputPollSource; - - int running; - int stateSaved; - int destroyed; - int redrawNeeded; - AInputQueue* pendingInputQueue; - ANativeWindow* pendingWindow; - ARect pendingContentRect; -}; - -enum { - /** - * Looper data ID of commands coming from the app's main thread, which - * is returned as an identifier from ALooper_pollOnce(). The data for this - * identifier is a pointer to an android_poll_source structure. - * These can be retrieved and processed with android_app_read_cmd() - * and android_app_exec_cmd(). - */ - LOOPER_ID_MAIN = 1, - - /** - * Looper data ID of events coming from the AInputQueue of the - * application's window, which is returned as an identifier from - * ALooper_pollOnce(). The data for this identifier is a pointer to an - * android_poll_source structure. These can be read via the inputQueue - * object of android_app. - */ - LOOPER_ID_INPUT = 2, - - /** - * Start of user-defined ALooper identifiers. - */ - LOOPER_ID_USER = 3, -}; - -enum { - /** - * Command from main thread: the AInputQueue has changed. Upon processing - * this command, android_app->inputQueue will be updated to the new queue - * (or NULL). - */ - APP_CMD_INPUT_CHANGED, - - /** - * Command from main thread: a new ANativeWindow is ready for use. Upon - * receiving this command, android_app->window will contain the new window - * surface. - */ - APP_CMD_INIT_WINDOW, - - /** - * Command from main thread: the existing ANativeWindow needs to be - * terminated. Upon receiving this command, android_app->window still - * contains the existing window; after calling android_app_exec_cmd - * it will be set to NULL. - */ - APP_CMD_TERM_WINDOW, - - /** - * Command from main thread: the current ANativeWindow has been resized. - * Please redraw with its new size. - */ - APP_CMD_WINDOW_RESIZED, - - /** - * Command from main thread: the system needs that the current ANativeWindow - * be redrawn. You should redraw the window before handing this to - * android_app_exec_cmd() in order to avoid transient drawing glitches. - */ - APP_CMD_WINDOW_REDRAW_NEEDED, - - /** - * Command from main thread: the content area of the window has changed, - * such as from the soft input window being shown or hidden. You can - * find the new content rect in android_app::contentRect. - */ - APP_CMD_CONTENT_RECT_CHANGED, - - /** - * Command from main thread: the app's activity window has gained - * input focus. - */ - APP_CMD_GAINED_FOCUS, - - /** - * Command from main thread: the app's activity window has lost - * input focus. - */ - APP_CMD_LOST_FOCUS, - - /** - * Command from main thread: the current device configuration has changed. - */ - APP_CMD_CONFIG_CHANGED, - - /** - * Command from main thread: the system is running low on memory. - * Try to reduce your memory use. - */ - APP_CMD_LOW_MEMORY, - - /** - * Command from main thread: the app's activity has been started. - */ - APP_CMD_START, - - /** - * Command from main thread: the app's activity has been resumed. - */ - APP_CMD_RESUME, - - /** - * Command from main thread: the app should generate a new saved state - * for itself, to restore from later if needed. If you have saved state, - * allocate it with malloc and place it in android_app.savedState with - * the size in android_app.savedStateSize. The will be freed for you - * later. - */ - APP_CMD_SAVE_STATE, - - /** - * Command from main thread: the app's activity has been paused. - */ - APP_CMD_PAUSE, - - /** - * Command from main thread: the app's activity has been stopped. - */ - APP_CMD_STOP, - - /** - * Command from main thread: the app's activity is being destroyed, - * and waiting for the app thread to clean up and exit before proceeding. - */ - APP_CMD_DESTROY, -}; - -/** - * Call when ALooper_pollAll() returns LOOPER_ID_MAIN, reading the next - * app command message. - */ -int8_t android_app_read_cmd(struct android_app* android_app); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * initial pre-processing of the given command. You can perform your own - * actions for the command after calling this function. - */ -void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Call with the command returned by android_app_read_cmd() to do the - * final post-processing of the given command. You must have done your own - * actions for the command before calling this function. - */ -void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd); - -/** - * Dummy function you can call to ensure glue code isn't stripped. - */ -void app_dummy(); - -/** - * This is the function that application code must implement, representing - * the main entry to the app. - */ -extern void android_main(struct android_app* app); - -#ifdef __cplusplus -} -#endif - -#endif /* _ANDROID_NATIVE_APP_GLUE_H */ diff --git a/sal/osl/android/jni/lo-bootstrap.c b/sal/osl/android/jni/lo-bootstrap.c deleted file mode 100644 index d9c1e32958ab..000000000000 --- a/sal/osl/android/jni/lo-bootstrap.c +++ /dev/null @@ -1,1041 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Version: MPL 1.1 / GPLv3+ / LGPLv3+ - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License or as specified alternatively below. You may obtain a copy of - * the License at http: *www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * Major Contributor(s): - * Copyright (C) 2011 Tor Lillqvist (initial developer) - * Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer) - * - * Zip parsing code lifted from Mozilla's other-licenses/android/APKOpen.cpp, - * by Michael Wu . - * - * All Rights Reserved. - * - * For minor contributions see the git repository. - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 3 or later (the "GPLv3+"), or - * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), - * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable - * instead of those above. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#include - -#include "lo-bootstrap.h" - -#include "android_native_app_glue.c" - -#undef LOGI -#undef LOGW - -#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "lo-bootstrap", __VA_ARGS__)) -#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "lo-bootstrap", __VA_ARGS__)) -#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "lo-bootstrap", __VA_ARGS__)) -#define LOGF(...) ((void)__android_log_print(ANDROID_LOG_FATAL, "lo-bootstrap", __VA_ARGS__)) - -#define ROUND_DOWN(ptr,multiple) (void *)(((unsigned) (ptr)) & ~((multiple)-1)) - -struct engine { - int dummy; -}; - -static struct android_app *app; -static const char **library_locations; -static void *apk_file; -static int apk_file_size; -static int (*lo_main)(int, const char **); -static int lo_main_argc; -static const char **lo_main_argv; -static int sleep_time = 0; - -/* Zip data structures */ - -/* compression methods */ -#define STORE 0 -#define DEFLATE 8 -#define LZMA 14 - -struct local_file_header { - uint32_t signature; - uint16_t min_version; - uint16_t general_flag; - uint16_t compression; - uint16_t lastmod_time; - uint16_t lastmod_date; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_size; - uint16_t extra_field_size; - char data[0]; -} __attribute__((__packed__)); - -struct cdir_entry { - uint32_t signature; - uint16_t creator_version; - uint16_t min_version; - uint16_t general_flag; - uint16_t compression; - uint16_t lastmod_time; - uint16_t lastmod_date; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t filename_size; - uint16_t extra_field_size; - uint16_t file_comment_size; - uint16_t disk_num; - uint16_t internal_attr; - uint32_t external_attr; - uint32_t offset; - char data[0]; -} __attribute__((__packed__)); - -#define CDIR_END_SIG 0x06054b50 - -struct cdir_end { - uint32_t signature; - uint16_t disk_num; - uint16_t cdir_disk; - uint16_t disk_entries; - uint16_t cdir_entries; - uint32_t cdir_size; - uint32_t cdir_offset; - uint16_t comment_size; - char comment[0]; -} __attribute__((__packed__)); - -/* End of Zip data structures */ - -static void -engine_handle_cmd(struct android_app* app, - int32_t cmd) -{ - struct engine* engine = (struct engine*)app->userData; - switch (cmd) { - case APP_CMD_SAVE_STATE: - break; - case APP_CMD_INIT_WINDOW: - break; - case APP_CMD_TERM_WINDOW: - break; - case APP_CMD_GAINED_FOCUS: - break; - case APP_CMD_LOST_FOCUS: - break; - } -} - -static char * -read_section(int fd, - Elf32_Shdr *shdr) -{ - char *result; - - result = malloc(shdr->sh_size); - if (lseek(fd, shdr->sh_offset, SEEK_SET) < 0) { - close(fd); - free(result); - return NULL; - } - if (read(fd, result, shdr->sh_size) < shdr->sh_size) { - close(fd); - free(result); - return NULL; - } - - return result; -} - -static void -free_ptrarray(void **pa) -{ - void **rover = pa; - - while (*rover != NULL) - free(*rover++); - - free(pa); -} - -jobjectArray -Java_org_libreoffice_android_Bootstrap_dlneeds(JNIEnv* env, - jobject clazz, - jstring library) -{ - char **needed; - int n_needed; - const jbyte *libName; - jclass String; - jobjectArray result; - - libName = (*env)->GetStringUTFChars(env, library, NULL); - - needed = lo_dlneeds(libName); - - (*env)->ReleaseStringUTFChars(env, library, libName); - - if (needed == NULL) - return NULL; - - n_needed = 0; - while (needed[n_needed] != NULL) - n_needed++; - - /* Allocate return value */ - - String = (*env)->FindClass(env, "java/lang/String"); - if (String == NULL) { - LOGE("Could not find the String class"); - free_ptrarray((void **) needed); - return NULL; - } - - result = (*env)->NewObjectArray(env, n_needed, String, NULL); - if (result == NULL) { - LOGE("Could not create the String array"); - free_ptrarray((void **) needed); - return NULL; - } - - for (n_needed = 0; needed[n_needed] != NULL; n_needed++) - (*env)->SetObjectArrayElement(env, result, n_needed, (*env)->NewStringUTF(env, needed[n_needed])); - - free_ptrarray((void **) needed); - - return result; -} - -jint -Java_org_libreoffice_android_Bootstrap_dlopen(JNIEnv* env, - jobject clazz, - jstring library) -{ - const jbyte *libName = (*env)->GetStringUTFChars(env, library, NULL); - void *p = lo_dlopen (libName); - - (*env)->ReleaseStringUTFChars(env, library, libName); - - return (jint) p; -} - -jint -Java_org_libreoffice_android_Bootstrap_dlsym(JNIEnv* env, - jobject clazz, - jint handle, - jstring symbol) -{ - const jbyte *symName = (*env)->GetStringUTFChars(env, symbol, NULL); - void *p = lo_dlsym ((void *) handle, symName); - - (*env)->ReleaseStringUTFChars(env, symbol, symName); - - return (jint) p; -} - -jint -Java_org_libreoffice_android_Bootstrap_dlcall(JNIEnv* env, - jobject clazz, - jint function, - jobject argument) -{ - jclass StringArray = (*env)->FindClass(env, "[Ljava/lang/String;"); - - if (StringArray == NULL) { - LOGE("Could not find String[] class"); - return 0; - } - - if ((*env)->IsInstanceOf(env, argument, StringArray)) { - int argc = (*env)->GetArrayLength(env, argument); - const char **argv = malloc(sizeof(char *) * (argc+1)); - int i, result; - for (i = 0; i < argc; i++) { - argv[i] = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, argument, i), NULL); - /* LOGI("argv[%d] = %s", i, argv[i]); */ - } - argv[argc] = NULL; - - result = lo_dlcall_argc_argv((void *) function, argc, argv); - - for (i = 0; i < argc; i++) - (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, argument, i), argv[i]); - - free(argv); - return result; - } - - return 0; -} - -// public static native boolean setup(String dataDir, -// String apkFile, -// String[] ld_library_path); - -jboolean -Java_org_libreoffice_android_Bootstrap_setup__Ljava_lang_String_2Ljava_lang_String_2_3Ljava_lang_String_2 - (JNIEnv* env, - jobject clazz, - jstring dataDir, - jstring apkFile, - jobjectArray ld_library_path) -{ - struct stat st; - int i, n, fd; - const jbyte *dataDirPath; - const jbyte *apkFilePath; - char *lib_dir; - - n = (*env)->GetArrayLength(env, ld_library_path); - - library_locations = malloc((n+2) * sizeof(char *)); - - dataDirPath = (*env)->GetStringUTFChars(env, dataDir, NULL); - - lib_dir = malloc(strlen(dataDirPath) + 5); - strcpy(lib_dir, dataDirPath); - strcat(lib_dir, "/lib"); - - (*env)->ReleaseStringUTFChars(env, dataDir, dataDirPath); - - library_locations[0] = lib_dir; - - for (i = 0; i < n; i++) { - const jbyte *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, ld_library_path, i), NULL); - library_locations[i+1] = strdup(s); - (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, ld_library_path, i), s); - } - - library_locations[n+1] = NULL; - - for (n = 0; library_locations[n] != NULL; n++) - LOGI("library_locations[%d] = %s", n, library_locations[n]); - - apkFilePath = (*env)->GetStringUTFChars(env, apkFile, NULL); - - fd = open(apkFilePath, O_RDONLY); - if (fd == -1) { - LOGE("Could not open %s", apkFilePath); - (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); - return JNI_FALSE; - } - if (fstat(fd, &st) == -1) { - LOGE("Could not fstat %s", apkFilePath); - close(fd); - (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); - return JNI_FALSE; - } - apk_file = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); - close(fd); - - if (apk_file == MAP_FAILED) { - LOGE("Could not mmap %s", apkFilePath); - (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); - return JNI_FALSE; - } - apk_file_size = st.st_size; - - (*env)->ReleaseStringUTFChars(env, apkFile, apkFilePath); - - return JNI_TRUE; -} - -// public statuc native boolean setup(int lo_main_ptr, -// Object lo_main_argument, -// int lo_main_delay); - -jboolean -Java_org_libreoffice_android_Bootstrap_setup__ILjava_lang_Object_2I(JNIEnv* env, - jobject clazz, - void *lo_main_ptr, - jobject lo_main_argument, - jint lo_main_delay) -{ - jclass StringArray; - int i; - - lo_main = lo_main_ptr; - - StringArray = (*env)->FindClass(env, "[Ljava/lang/String;"); - if (StringArray == NULL) { - LOGE("Could not find String[] class"); - return JNI_FALSE; - } - - if (!(*env)->IsInstanceOf(env, lo_main_argument, StringArray)) { - LOGE("lo_main_argument is not a String[]?"); - return JNI_FALSE; - } - - lo_main_argc = (*env)->GetArrayLength(env, lo_main_argument); - lo_main_argv = malloc(sizeof(char *) * (lo_main_argc+1)); - - for (i = 0; i < lo_main_argc; i++) { - const jbyte *s = (*env)->GetStringUTFChars(env, (*env)->GetObjectArrayElement(env, lo_main_argument, i), NULL); - lo_main_argv[i] = strdup(s); - (*env)->ReleaseStringUTFChars(env, (*env)->GetObjectArrayElement(env, lo_main_argument, i), s); - /* LOGI("argv[%d] = %s", i, lo_main_argv[i]); */ - } - lo_main_argv[lo_main_argc] = NULL; - - sleep_time = lo_main_delay; - - return JNI_TRUE; -} - -// public static native int getpid(); - -jint -Java_org_libreoffice_android_Bootstrap_getpid(JNIEnv* env, - jobject clazz) -{ - return getpid(); -} - - -// public static native void system(String cmdline); - -jint -Java_org_libreoffice_android_Bootstrap_system(JNIEnv* env, - jobject clazz, - jstring cmdline) -{ - const jbyte *s = (*env)->GetStringUTFChars(env, cmdline, NULL); - - LOGI("system(%s)", s); - - system(s); - - (*env)->ReleaseStringUTFChars(env, cmdline, s); -} - -char ** -lo_dlneeds(const char *library) -{ - int i, fd; - int n_needed; - char **result; - char *shstrtab, *dynstr; - Elf32_Ehdr hdr; - Elf32_Shdr shdr; - Elf32_Dyn dyn; - - /* Open library and read ELF header */ - - fd = open(library, O_RDONLY); - - if (fd == -1) { - LOGE("lo_dlneeds: Could not open library %s: %s", library, strerror(errno)); - return NULL; - } - - if (read(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { - LOGE("lo_dlneeds: Could not read ELF header of %s", library); - close(fd); - return NULL; - } - - /* Read in .shstrtab */ - - if (lseek(fd, hdr.e_shoff + hdr.e_shstrndx * sizeof(shdr), SEEK_SET) < 0) { - LOGE("lo_dlneeds: Could not seek to .shstrtab section header of %s", library); - close(fd); - return NULL; - } - if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { - LOGE("lo_dlneeds: Could not read section header of %s", library); - close(fd); - return NULL; - } - - shstrtab = read_section(fd, &shdr); - if (shstrtab == NULL) - return NULL; - - /* Read section headers, looking for .dynstr section */ - - if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { - LOGE("lo_dlneeds: Could not seek to section headers of %s", library); - close(fd); - return NULL; - } - for (i = 0; i < hdr.e_shnum; i++) { - if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { - LOGE("lo_dlneeds: Could not read section header of %s", library); - close(fd); - return NULL; - } - if (shdr.sh_type == SHT_STRTAB && - strcmp(shstrtab + shdr.sh_name, ".dynstr") == 0) { - dynstr = read_section(fd, &shdr); - if (dynstr == NULL) { - free(shstrtab); - return NULL; - } - break; - } - } - - if (i == hdr.e_shnum) { - LOGE("lo_dlneeds: No .dynstr section in %s", library); - close(fd); - return NULL; - } - - /* Read section headers, looking for .dynamic section */ - - if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { - LOGE("lo_dlneeds: Could not seek to section headers of %s", library); - close(fd); - return NULL; - } - for (i = 0; i < hdr.e_shnum; i++) { - if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { - LOGE("lo_dlneeds: Could not read section header of %s", library); - close(fd); - return NULL; - } - if (shdr.sh_type == SHT_DYNAMIC) { - int dynoff; - int *libnames; - - /* Count number of DT_NEEDED entries */ - n_needed = 0; - if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { - LOGE("lo_dlneeds: Could not seek to .dynamic section of %s", library); - close(fd); - return NULL; - } - for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { - if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { - LOGE("lo_dlneeds: Could not read .dynamic entry of %s", library); - close(fd); - return NULL; - } - if (dyn.d_tag == DT_NEEDED) - n_needed++; - } - - /* LOGI("Found %d DT_NEEDED libs", n_needed); */ - - result = malloc((n_needed+1) * sizeof(char *)); - - n_needed = 0; - if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { - LOGE("lo_dlneeds: Could not seek to .dynamic section of %s", library); - close(fd); - free(result); - return NULL; - } - for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { - if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { - LOGE("lo_dlneeds: Could not read .dynamic entry in %s", library); - close(fd); - free(result); - return NULL; - } - if (dyn.d_tag == DT_NEEDED) { - /* LOGI("needs: %s\n", dynstr + dyn.d_un.d_val); */ - result[n_needed] = strdup(dynstr + dyn.d_un.d_val); - n_needed++; - } - } - - close(fd); - free(dynstr); - free(shstrtab); - result[n_needed] = NULL; - return result; - } - } - - LOGE("lo_dlneeds: Could not find .dynamic section in %s", library); - close(fd); - return NULL; -} - -void * -lo_dlopen(const char *library) -{ - /* - * We should *not* try to just dlopen() the bare library name - * first, as the stupid dynamic linker remembers for each library - * basename if loading it has failed. Thus if you try loading it - * once, and it fails because of missing needed libraries, and - * your load those, and then try again, it fails with an - * infuriating message "failed to load previously" in the log. - * - * We *must* first dlopen() all needed libraries, recursively. It - * shouldn't matter if we dlopen() a library that already is - * loaded, dlopen() just returns the same value then. - */ - - typedef struct loadedLib { - const char *name; - void *handle; - struct loadedLib *next; - } *loadedLib; - static loadedLib loaded_libraries = NULL; - - loadedLib rover; - loadedLib new_loaded_lib; - - struct stat st; - void *p; - char *full_name; - char **needed; - int i; - int found; - - rover = loaded_libraries; - while (rover != NULL && - strcmp(rover->name, library) != 0) - rover = rover->next; - - if (rover != NULL) - return rover->handle; - - /* LOGI("lo_dlopen(%s)", library); */ - - found = 0; - if (library[0] == '/') { - full_name = strdup(library); - - if (stat(full_name, &st) == 0 && - S_ISREG(st.st_mode)) - found = 1; - else - free(full_name); - } else { - for (i = 0; !found && library_locations[i] != NULL; i++) { - full_name = malloc(strlen(library_locations[i]) + 1 + strlen(library) + 1); - strcpy(full_name, library_locations[i]); - strcat(full_name, "/"); - strcat(full_name, library); - - if (stat(full_name, &st) == 0 && - S_ISREG(st.st_mode)) - found = 1; - else - free(full_name); - } - } - - if (!found) { - LOGE("lo_dlopen: Library %s not found", library); - return NULL; - } - - needed = lo_dlneeds(full_name); - if (needed == NULL) { - free(full_name); - return NULL; - } - - for (i = 0; needed[i] != NULL; i++) { - if (lo_dlopen(needed[i]) == NULL) { - free_ptrarray((void **) needed); - free(full_name); - return NULL; - } - } - free_ptrarray((void **) needed); - - p = dlopen(full_name, RTLD_LOCAL); - LOGI("dlopen(%s) = %p", full_name, p); - free(full_name); - if (p == NULL) - LOGE("lo_dlopen: Error from dlopen(%s): %s", library, dlerror()); - - new_loaded_lib = malloc(sizeof(*new_loaded_lib)); - new_loaded_lib->name = strdup(library); - new_loaded_lib->handle = p; - - new_loaded_lib->next = loaded_libraries; - loaded_libraries = new_loaded_lib; - - return p; -} - -void * -lo_dlsym(void *handle, - const char *symbol) -{ - void *p = dlsym(handle, symbol); - /* LOGI("dlsym(%p, %s) = %p", handle, symbol, p); */ - if (p == NULL) - LOGE("lo_dlsym: %s", dlerror()); - return p; -} - -int -lo_dladdr(void *addr, - Dl_info *info) -{ - FILE *maps; - char line[200]; - int result; - int found; - - result = dladdr(addr, info); - if (result == 0) { - /* LOGI("dladdr(%p) = 0", addr); */ - return 0; - } - - maps = fopen("/proc/self/maps", "r"); - if (maps == NULL) { - LOGE("lo_dladdr: Could not open /proc/self/maps: %s", strerror(errno)); - return 0; - } - - found = 0; - while (fgets(line, sizeof(line), maps) != NULL && - line[strlen(line)-1] == '\n') { - void *lo, *hi; - char file[sizeof(line)]; - file[0] = '\0'; - if (sscanf(line, "%x-%x %*s %*x %*x:%*x %*d %[^\n]", &lo, &hi, file) == 3) { - /* LOGI("got %p-%p: %s", lo, hi, file); */ - if (addr >= lo && addr < hi) { - if (info->dli_fbase != lo) { - LOGE("lo_dladdr: Base for %s in /proc/self/maps %p doesn't match what dladdr() said", file, lo); - fclose(maps); - return 0; - } - /* LOGI("dladdr(%p) = { %s:%p, %s:%p }: %s", - addr, - info->dli_fname, info->dli_fbase, - info->dli_sname ? info->dli_sname : "(none)", info->dli_saddr, - file); */ - info->dli_fname = strdup(file); - found = 1; - break; - } - } - } - if (!found) - LOGE("lo_dladdr: Did not find %p in /proc/self/maps", addr); - fclose(maps); - - return result; -} - -static uint32_t cdir_entry_size (struct cdir_entry *entry) -{ - return sizeof(*entry) + - letoh16(entry->filename_size) + - letoh16(entry->extra_field_size) + - letoh16(entry->file_comment_size); -} - -static struct cdir_entry * -find_cdir_entry (struct cdir_entry *entry, int count, const char *name) -{ - size_t name_size = strlen(name); - while (count--) { - if (letoh16(entry->filename_size) == name_size && - !memcmp(entry->data, name, name_size)) - return entry; - entry = (struct cdir_entry *)((char *)entry + cdir_entry_size(entry)); - } - return NULL; -} - -void * -lo_apkentry(const char *filename, - size_t *size) -{ - struct cdir_end *dirend = (struct cdir_end *)((char *) apk_file + apk_file_size - sizeof(*dirend)); - uint32_t cdir_offset; - uint16_t cdir_entries; - struct cdir_entry *cdir_start; - struct cdir_entry *entry; - struct local_file_header *file; - void *data; - - while ((void *)dirend > apk_file && - letoh32(dirend->signature) != CDIR_END_SIG) - dirend = (struct cdir_end *)((char *)dirend - 1); - if (letoh32(dirend->signature) != CDIR_END_SIG) { - LOGE("lo_apkentry: Could not find end of central directory record"); - return; - } - - cdir_offset = letoh32(dirend->cdir_offset); - cdir_entries = letoh16(dirend->cdir_entries); - cdir_start = (struct cdir_entry *)((char *)apk_file + cdir_offset); - - if (*filename == '/') - filename++; - - entry = find_cdir_entry(cdir_start, cdir_entries, filename); - - if (entry == NULL) { - LOGE("lo_apkentry: Could not find %s", filename); - return NULL; - } - file = (struct local_file_header *)((char *)apk_file + letoh32(entry->offset)); - - if (letoh16(file->compression) != STORE) { - LOGE("lo_apkentry: File %s is compressed", filename); - return NULL; - } - - data = ((char *)&file->data) + letoh16(file->filename_size) + letoh16(file->extra_field_size); - *size = file->uncompressed_size; - - /* LOGI("lo_apkentry(%s): %p, %d", filename, data, *size); */ - - return data; -} - -int -lo_dlcall_argc_argv(void *function, - int argc, - const char **argv) -{ - int (*fp)(int, const char **) = function; - int result = fp(argc, argv); - - return result; -} - -/* There is a bug in std::type_info::operator== and - * std::type_info::before() in libgnustl_shared.so in NDK r7 at - * least. They compare the type name pointers instead of comparing the - * type name strings. See - * http://code.google.com/p/android/issues/detail?id=22165 . So patch - * that, poke in jumps to our own code snippets below instead. - */ - -/* Replacement std::type_info::operator== */ - -__asm(" .arm\n" - " .global replacement_operator_equals_arm\n" - "replacement_operator_equals_arm:\n" - " push {lr}\n" - - /* Load name pointers into r0 and r1 */ - " ldr r0, [r0, #4]\n" - " ldr r1, [r1, #4]\n" - - /* First compare pointers */ - " cmp r0, r1\n" - - /* If equal, return true */ - " beq .L.equals.1\n" - - /* Otherwise call strcmp */ - " bl strcmp\n" - - /* And return true or false */ - " cmp r0, #0\n" - " moveq r0, #1\n" - " movne r0, #0\n" - " b .L.equals.9\n" - - ".L.equals.1:\n" - " mov r0, #1\n" - - ".L.equals.9:\n" - " pop {pc}\n" - ); - -extern unsigned int replacement_operator_equals_arm; - -/* The ARM (not Thumb) code of the operator== in NDK r7 */ -static unsigned int expected_operator_equals_r7_code[] = { - 0xe5903004, /* ldr r3, [r0, #4] */ - 0xe5910004, /* ldr r0, [r1, #4] */ - 0xe1530000, /* cmp r3, r0 */ - 0x13a00000, /* movne, #0 */ - 0x03a00001, /* moveq r0, #1 */ - 0xe12fff1e /* bx lr */ -}; - -/* Ditto for std::type_info::before() */ - -__asm(" .arm\n" - " .global replacement_method_before_arm\n" - "replacement_method_before_arm:\n" - " push {lr}\n" - - /* Load name pointers into r0 and r1 */ - " ldr r0, [r0, #4]\n" - " ldr r1, [r1, #4]\n" - - /* First compare pointers */ - " cmp r0, r1\n" - - /* If equal, return false */ - " beq .L.before.1\n" - - /* Otherwise call strcmp */ - " bl strcmp\n" - - /* And return true or false */ - " cmp r0, #0\n" - " movlt r0, #1\n" - " movge r0, #0\n" - " b .L.before.9\n" - - ".L.before.1:\n" - " mov r0, #0\n" - - ".L.before.9:\n" - " pop {pc}\n" - ); - -extern unsigned int replacement_method_before_arm; - -static unsigned int expected_method_before_r7_code[] = { - 0xe5903004, /* ldr r3, [r0, #4] */ - 0xe5910004, /* ldr r0, [r1, #4] */ - 0xe1530000, /* cmp r3, r0 */ - 0x23a00000, /* movcs r0, #0 */ - 0x33a00001, /* movcc r0, #1 */ - 0xe12fff1e /* bx lr */ -}; - -static void -patch(const char *symbol, - const char *plaintext, - unsigned *expected_code, - size_t expected_code_size, - unsigned *replacement_code) -{ - - void *libgnustl_shared; - void *code; - - void *base; - size_t size; - - /* libgnustl_shared.so should be already loaded as we build - * all LO code against it, so as we have loaded the .so - * containing lo_main() already, libgnustl_shared.so will have - * been brought in, too. - */ - libgnustl_shared = dlopen("libgnustl_shared.so", RTLD_LOCAL); - if (libgnustl_shared == NULL) { - LOGF("android_main: libgnustl_shared.so not mapped??"); - exit(0); - } - - code = dlsym(libgnustl_shared, symbol); - if (code == NULL) { - LOGF("android_main: %s not found!?", plaintext); - exit(0); - } - /* LOGI("%s is at %p", plaintext, operator_equals); */ - - if ((((unsigned) code) & 0x03) != 0) { - LOGE("android_main: Address of %s is not at word boundary, huh?", plaintext); - return; - } - - if ((((unsigned) &replacement_code) & 0x03) != 0) { - LOGE("android_main: Address of replacement %s is not at word boundary, huh?", plaintext); - return; - } - - if (memcmp(code, expected_code, expected_code_size) != 0) { - LOGI("android_main: Code for %s does not match that in NDK r7; not patching it", plaintext); - return; - } - - base = ROUND_DOWN(code, getpagesize()); - size = code + sizeof(expected_code_size) - ROUND_DOWN(code, getpagesize()); - if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) == -1) { - LOGE("android_main: mprotect() failed: %s", strerror(errno)); - return; - } - - /* Poke a "b replacement_code" into it instead */ - *((unsigned *) code) = - (0xEA000000 | - ((((int) replacement_code - ((int) code + 8)) / 4) & 0x00FFFFFF)); -} - -static void -patch_libgnustl_shared(void) -{ - patch("_ZNKSt9type_infoeqERKS_", - "std::type_info::operator==", - expected_operator_equals_r7_code, - sizeof(expected_operator_equals_r7_code), - &replacement_operator_equals_arm); - - patch("_ZNKSt9type_info6beforeERKS_", - "std::type_info::before()", - expected_method_before_r7_code, - sizeof(expected_method_before_r7_code), - &replacement_method_before_arm); -} - -void -Java_org_libreoffice_android_Bootstrap_patch_libgnustl_shared(JNIEnv* env, - jobject clazz) -{ - patch_libgnustl_shared(); -} - -JavaVM * -lo_get_javavm(void) -{ - return app->activity->vm; -} - -void -android_main(struct android_app* state) -{ - struct engine engine; - Dl_info lo_main_info; - - app = state; - - memset(&engine, 0, sizeof(engine)); - state->userData = &engine; - state->onAppCmd = engine_handle_cmd; - - if (lo_dladdr(lo_main, &lo_main_info) != 0) { - lo_main_argv[0] = lo_main_info.dli_fname; - } - - if (sleep_time != 0) - sleep(sleep_time); - - patch_libgnustl_shared(); - - lo_main(lo_main_argc, lo_main_argv); - - exit(0); -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/android/jni/lo-bootstrap.h b/sal/osl/android/jni/lo-bootstrap.h deleted file mode 100644 index 258d9d28850c..000000000000 --- a/sal/osl/android/jni/lo-bootstrap.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * Version: MPL 1.1 / GPLv3+ / LGPLv3+ - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License or as specified alternatively below. You may obtain a copy of - * the License at http: *www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * Major Contributor(s): - * Copyright (C) 2011 Tor Lillqvist (initial developer) - * Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer) - * - * All Rights Reserved. - * - * For minor contributions see the git repository. - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 3 or later (the "GPLv3+"), or - * the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), - * in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable - * instead of those above. - */ - -#if defined(ANDROID) - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -char **lo_dlneeds(const char *library); - -void *lo_dlopen(const char *library); - -void *lo_dlsym(void *handle, - const char *symbol); - -int lo_dladdr(void *addr, - Dl_info *info); - -void *lo_apkentry(const char *filename, - size_t *size); - -int lo_dlcall_argc_argv(void *function, - int argc, - const char **argv); - -JavaVM *lo_get_javavm(void); - -#ifdef __cplusplus -} -#endif - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/android/project.properties b/sal/osl/android/project.properties deleted file mode 100644 index 730e911f2f9e..000000000000 --- a/sal/osl/android/project.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "ant.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-14 diff --git a/sal/osl/android/res/layout/main.xml b/sal/osl/android/res/layout/main.xml deleted file mode 100644 index 5839d8cda704..000000000000 --- a/sal/osl/android/res/layout/main.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/sal/osl/android/res/values/strings.xml b/sal/osl/android/res/values/strings.xml deleted file mode 100644 index f296dd4b137d..000000000000 --- a/sal/osl/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - LibreOfficeBootstrap - diff --git a/sal/osl/android/src/fi/iki/tml/CommandLine.java b/sal/osl/android/src/fi/iki/tml/CommandLine.java deleted file mode 100644 index fb5adec2acf5..000000000000 --- a/sal/osl/android/src/fi/iki/tml/CommandLine.java +++ /dev/null @@ -1,176 +0,0 @@ -// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - -// Copyright (C) 2011 Tor Lillqvist -// -// Permission is hereby granted, free of charge, to any person -// obtaining a copy of this software and associated documentation -// files (the "Software"), to deal in the Software without -// restriction, including without limitation the rights to use, -// copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the -// Software is furnished to do so, subject to the following -// conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -// OTHER DEALINGS IN THE SOFTWARE. - -package fi.iki.tml; - -import java.util.*; - -public class CommandLine - implements Iterable -{ - private String commandLine; - - public class Tokenizer - implements Iterator - { - private int index = 0; - private String savedNext = null; - - public Tokenizer() - { - } - - public boolean hasNext() - { - if (savedNext != null) - return true; - try { - savedNext = next(); - return true; - } - catch (NoSuchElementException e) { - } - return false; - } - - public String next() - { - if (savedNext != null) { - String result = savedNext; - savedNext = null; - return result; - } - - StringBuffer sb = new StringBuffer(commandLine.length()); - - while (index < commandLine.length() && - commandLine.charAt(index) == ' ') - index++; - - if (index == commandLine.length()) - throw new NoSuchElementException(); - - while (index < commandLine.length() && - commandLine.charAt(index) != ' ') { - char c = commandLine.charAt(index); - if (c == '\'') { - index++; - while (index < commandLine.length() && - commandLine.charAt(index) != '\'') { - sb.append(commandLine.charAt(index)); - index++; - } - } else if (c == '"') { - index++; - while (index < commandLine.length() && - commandLine.charAt(index) != '\"') { - if (commandLine.charAt(index) == '\\') { - index++; - if (index < commandLine.length()) - sb.append(commandLine.charAt(index)); - } else { - sb.append(commandLine.charAt(index)); - } - index++; - } - } else if (c == '\\') { - index++; - if (index < commandLine.length()) - sb.append(commandLine.charAt(index)); - } else { - sb.append(c); - } - index++; - } - return sb.toString(); - } - - public void remove() - { - throw new UnsupportedOperationException(); - } - } - - public Iterator iterator() - { - return new Tokenizer(); - } - - public CommandLine(String commandLine) - { - this.commandLine = commandLine; - } - - public String[] split() - { - ArrayList argv = new ArrayList(10); - - try { - for (String s : this) - argv.add(s); - } - catch (NoSuchElementException e) { - } - - return argv.toArray(new String[0]); - } - - public static String[] split(String commandLine) - { - return new CommandLine(commandLine).split(); - } - - public static void main(String[] args) - { - class Test - { - Test(String commandLine) - { - String[] argv = split(commandLine); - System.out.println("Split of " + commandLine + ":"); - int n = 0; - for (String s : argv) { - System.out.println("argv[" + n + "}: length " + s.length() + ": \"" + s + "\""); - n++; - } - } - } - - new Test(""); - new Test(" "); - new Test(" "); - new Test(" '' "); - new Test("abc def"); - new Test("abc '' def"); - new Test("abc \"\" def"); - new Test(" abc def "); - new Test(" abc def "); - new Test("abc\" \"def"); - new Test("abc\" \"def \"gh\\i\\\" jkl\""); - new Test("abc' def' '\\ghi jkl'"); - } -} - -// vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sal/osl/android/src/org/libreoffice/android/Bootstrap.java b/sal/osl/android/src/org/libreoffice/android/Bootstrap.java deleted file mode 100644 index 5e377b27b02e..000000000000 --- a/sal/osl/android/src/org/libreoffice/android/Bootstrap.java +++ /dev/null @@ -1,169 +0,0 @@ -// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - -// Version: MPL 1.1 / GPLv3+ / LGPLv3+ -// -// The contents of this file are subject to the Mozilla Public License Version -// 1.1 (the "License"); you may not use this file except in compliance with -// the License or as specified alternatively below. You may obtain a copy of -// the License at http://www.mozilla.org/MPL/ -// -// Software distributed under the License is distributed on an "AS IS" basis, -// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -// for the specific language governing rights and limitations under the -// License. -// -// Major Contributor(s): -// Copyright (C) 2011 Tor Lillqvist (initial developer) -// Copyright (C) 2011 SUSE Linux http://suse.com (initial developer's employer) -// -// All Rights Reserved. -// -// For minor contributions see the git repository. -// -// Alternatively, the contents of this file may be used under the terms of -// either the GNU General Public License Version 3 or later (the "GPLv3+"), or -// the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), -// in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable -// instead of those above. - -package org.libreoffice.android; - -import android.app.Activity; -import android.app.NativeActivity; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.util.Log; - -import fi.iki.tml.CommandLine; - -// We override NativeActivity so that we can get at the intent of the -// activity and its extra parameters, that we use to tell us what -// actual LibreOffice "program" to run. I.e. something that on desktop -// OSes would be a program, but for Androis is actually built as a -// shared object, with an "lo_main" function. - -public class Bootstrap extends NativeActivity -{ - private static String TAG = "lo-bootstrap"; - - private static native boolean setup(String dataDir, - String apkFile, - String[] ld_library_path); - - public static native boolean setup(int lo_main_ptr, - Object lo_main_argument, - int lo_main_delay); - - // This is not just a wrapper for the C library dlopen(), but also - // loads recursively dependent libraries. - public static native int dlopen(String library); - - // This is just a wrapper for the C library dlsym(). - public static native int dlsym(int handle, String symbol); - - // To be called after you are sure libgnustl_shared.so - // has been loaded - public static native void patch_libgnustl_shared(); - - // Wrapper for getpid() - public static native int getpid(); - - // Wrapper for system() - public static native void system(String cmdline); - - public static void setup(Activity activity) - { - String dataDir = null; - - ApplicationInfo ai = activity.getApplicationInfo(); - dataDir = ai.dataDir; - Log.i(TAG, String.format("dataDir=%s\n", dataDir)); - - String llp = System.getenv("LD_LIBRARY_PATH"); - if (llp == null) - llp = "/vendor/lib:/system/lib"; - - String[] llpa = llp.split(":"); - - if (!setup(dataDir, activity.getApplication().getPackageResourcePath(), llpa)) - return; - - } - - @Override - protected void onCreate(Bundle savedInstanceState) - { - setup(this); - - String mainLibrary = getIntent().getStringExtra("lo-main-library"); - - if (mainLibrary == null) - mainLibrary = "libcppunittester"; - - mainLibrary += ".so"; - - Log.i(TAG, String.format("mainLibrary=%s", mainLibrary)); - - // Get "command line" to pass to the LO "program" - String cmdLine = getIntent().getStringExtra("lo-main-cmdline"); - - if (cmdLine == null) - cmdLine = "/data/data/org.libreoffice.android/lib/libqa_sal_types.so"; - - // argv[0] will be replaced by android_main() in lo-bootstrap.c by the - // pathname of the mainLibrary. - cmdLine = "dummy-program-name " + cmdLine; - - Log.i(TAG, String.format("cmdLine=%s", cmdLine)); - - String[] argv = CommandLine.split(cmdLine); - - // Load the LO "program" here and look up lo_main - int loLib = dlopen(mainLibrary); - - if (loLib == 0) { - Log.i(TAG, String.format("Could not load %s", mainLibrary)); - return; - } - - int lo_main = dlsym(loLib, "lo_main"); - if (lo_main == 0) { - Log.i(TAG, String.format("No lo_main in %s", mainLibrary)); - return; - } - - // Tell lo-bootstrap to Start a strace on itself if requested - String strace_args = getIntent().getStringExtra("lo-strace"); - if (strace_args != null) - system("/system/xbin/strace -p " + getpid() + " " + (strace_args != "yes" ? strace_args : "" ) + " &"); - - int delay = 0; - String sdelay = getIntent().getStringExtra("lo-main-delay"); - if (sdelay != null) - delay = Integer.parseInt(sdelay); - - // Tell lo-bootstrap.c the stuff it needs to know - if (!setup(lo_main, argv, delay)) - return; - - // Finally, call our super-class, NativeActivity's onCreate(), - // which eventually calls the ANativeActivity_onCreate() in - // android_native_app_glue.c, which starts a thread in which - // android_main() from lo-bootstrap.c is called. - - // android_main() calls the lo_main() defined in sal/main.h - // through the function pointer passed to setup() above, with - // the argc and argv also saved from the setup() call. - super.onCreate(savedInstanceState); - } - - // This is used to load the 'lo-bootstrap' library on application - // startup. The library has already been unpacked into - // /data/data//lib/liblo-bootstrap.so at installation - // time by the package manager. - static { - System.loadLibrary("lo-bootstrap"); - } -} -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx index ccc87e3ed7b7..1bf76a81f08e 100644 --- a/sal/osl/unx/file.cxx +++ b/sal/osl/unx/file.cxx @@ -56,6 +56,10 @@ #endif /* MACOSX */ +#ifdef ANDROID +#include +#endif + #ifdef DEBUG_OSL_FILE # define OSL_FILE_TRACE osl_trace # define PERROR( a, b ) perror( a ); fprintf( stderr, b ) @@ -917,8 +921,7 @@ SAL_CALL osl_openFile( rtl_uString* ustrFileURL, oslFileHandle* pHandle, sal_uIn { void *address; size_t size; - void *(*lo_apkentry)(const char *, size_t *) = (void *(*)(const char *, size_t *)) dlsym(RTLD_DEFAULT, "lo_apkentry"); - address = (*lo_apkentry)(buffer, &size); + address = lo_apkentry(buffer, &size); return osl_openMemoryAsFile(address, size, pHandle); } #endif diff --git a/sal/osl/unx/module.c b/sal/osl/unx/module.c index 952449db8ed2..38a49b7e4744 100644 --- a/sal/osl/unx/module.c +++ b/sal/osl/unx/module.c @@ -43,6 +43,10 @@ #include #endif +#ifdef ANDROID +#include +#endif + /* implemented in file.c */ extern int UnicodeToText(char *, size_t, const sal_Unicode *, sal_Int32); @@ -96,8 +100,7 @@ static sal_Bool getModulePathFromAddress(void * address, rtl_String ** path) { Dl_info dl_info; #ifdef ANDROID - int (*lo_dladdr)(void *, Dl_info *) = dlsym(RTLD_DEFAULT, "lo_dladdr"); - result = (*lo_dladdr)(address, &dl_info); + result = lo_dladdr(address, &dl_info); #else result = dladdr(address, &dl_info); #endif diff --git a/sal/osl/unx/process_impl.cxx b/sal/osl/unx/process_impl.cxx index 1edbf898f891..83c5c8872dbe 100644 --- a/sal/osl/unx/process_impl.cxx +++ b/sal/osl/unx/process_impl.cxx @@ -59,6 +59,10 @@ #include "uunxapi.h" +#ifdef ANDROID +#include +#endif + /*************************************** osl_bootstrap_getExecutableFile_Impl(). @@ -129,11 +133,25 @@ oslProcessError SAL_CALL osl_bootstrap_getExecutableFile_Impl ( oslProcessError result = osl_Process_E_NotFound; #ifdef ANDROID - /* On Android we want the address of the "lo_main()" function, as - * that is what corresponds to "main()" in LibreOffice programs on - * normal desktop OSes. + /* On Android we in theory want the address of the "lo_main()" + * function, as that is what corresponds to "main()" in + * LibreOffice programs on normal desktop OSes. + * + * But that is true only for apps with a "native activity", using + * and the org.libreoffice.android.Bootstrap + * mechanism. For more normal (?) Android apps that just use + * LibreOffice libraries (components) where the main program is in + * Java, that just use LibreOffice libraries, there is no + * lo_main(). (Note that we don't know for sure yet how + * complicated it might be to write such Android apps...) + * + * Maybe best to just pick some function in liblo-bootstrap.so + * which also such Java apps *must* load as the very first + * LibreOffice native library. We store all LibreOffice native + * shared libraries an app uses in the same folder anyway, so it + * doesn't really matter. */ - void * addr = dlsym (RTLD_DEFAULT, "lo_main"); + void * addr = (void *) &lo_dlopen; #else /* Determine address of "main()" function. */ void * addr = dlsym (RTLD_DEFAULT, "main"); diff --git a/sal/prj/build.lst b/sal/prj/build.lst index 47b8f58ecac9..f7f16ad12796 100644 --- a/sal/prj/build.lst +++ b/sal/prj/build.lst @@ -3,13 +3,14 @@ sa sal usr1 - all sa_mkout NULL sa sal\inc nmake - all sa_inc NULL sa sal\typesconfig nmake - all sa_tc sa_inc NULL sa sal\cpprt nmake - u sa_cpprt sa_util sa_inc NULL +sa sal\android nmake - u sa_android NULL sa sal\rtl\source nmake - all sa_rtl sa_tc.u sa_inc NULL sa sal\textenc nmake - all sa_textenc sa_tc.u sa_inc NULL sa sal\systools\win32\uwinapi nmake - n sa_uwinapi sa_inc NULL sa sal\systools\win32\onlineupdate nmake - n sa_onlineupdate sa_uwinapi.n sa_inc NULL sa sal\osl\w32 nmake - n sa_osln sa_inc NULL sa sal\qa nmake - all sa_qa sa_util NULL -sa sal\osl\unx nmake - u sa_oslu sa_tc.u sa_inc NULL +sa sal\osl\unx nmake - u sa_oslu sa_android.u sa_tc.u sa_inc NULL sa sal\osl\all nmake - all sa_oslall sa_tc.u sa_inc NULL sa sal\util nmake - all sa_util sa_tc.u sa_oslall sa_uwinapi.n sa_onlineupdate.n sa_osln.n sa_oslu.u sa_rtl sa_textenc NULL sa sal\util\sal_textenc nmake - all sa_util_saltextenc sa_util NULL diff --git a/sal/prj/d.lst b/sal/prj/d.lst index c7f93ed06309..1d73422d2fae 100644 --- a/sal/prj/d.lst +++ b/sal/prj/d.lst @@ -31,6 +31,8 @@ symlink: %_DEST%\lib\libuno_sal.dylib.3 %_DEST%\lib\libuno_sal.dylib ..\%__SRC%\lib\*.sl %_DEST%\lib\*.sl ..\%__SRC%\lib\*.a %_DEST%\lib\*.a +..\%__SRC%\lib\liblo-bootstrap.so %_DEST%\lib\liblo-bootstrap.so + ..\%__SRC%\slb\sal.lib %_DEST%\lib\xsal.lib ..\%__SRC%\bin\*.dll %_DEST%\bin\*.dll diff --git a/sal/util/makefile.mk b/sal/util/makefile.mk index 981f3cbbe632..d81780a9bf79 100644 --- a/sal/util/makefile.mk +++ b/sal/util/makefile.mk @@ -113,6 +113,10 @@ SHL1STDLIBS+= -z allextract -staticlib=Crun -z defaultextract SHL1STDLIBS+=-framework CoreFoundation -framework Carbon .ENDIF +.IF "$(OS)" == "ANDROID" +SHL1STDLIBS+=-llo-bootstrap +.ENDIF + SHL1LIBS+=$(SLB)$/$(TARGET).lib .IF "$(linkinc)" != "" -- cgit