From a9d167ba98672277c1ac57c5fe54965c21c02446 Mon Sep 17 00:00:00 2001 From: Tor Lillqvist Date: Fri, 11 Nov 2011 16:44:18 +0200 Subject: More baby steps for Android --- sal/osl/android/.gitignore | 5 + sal/osl/android/AndroidManifest.xml | 17 ++ sal/osl/android/Makefile | 12 + sal/osl/android/ant.properties | 17 ++ sal/osl/android/build.xml | 85 +++++++ sal/osl/android/jni/Android.mk | 23 ++ sal/osl/android/jni/Application.mk | 2 + sal/osl/android/jni/lo-bootstrap.c | 265 +++++++++++++++++++++ sal/osl/android/project.properties | 11 + sal/osl/android/res/layout/main.xml | 12 + sal/osl/android/res/values/strings.xml | 4 + .../src/org/libreoffice/android/Bootstrap.java | 144 +++++++++++ 12 files changed, 597 insertions(+) create mode 100644 sal/osl/android/.gitignore create mode 100644 sal/osl/android/AndroidManifest.xml create mode 100644 sal/osl/android/Makefile create mode 100644 sal/osl/android/ant.properties create mode 100644 sal/osl/android/build.xml create mode 100644 sal/osl/android/jni/Android.mk create mode 100644 sal/osl/android/jni/Application.mk create mode 100644 sal/osl/android/jni/lo-bootstrap.c create mode 100644 sal/osl/android/project.properties create mode 100644 sal/osl/android/res/layout/main.xml create mode 100644 sal/osl/android/res/values/strings.xml create mode 100644 sal/osl/android/src/org/libreoffice/android/Bootstrap.java (limited to 'sal/osl') diff --git a/sal/osl/android/.gitignore b/sal/osl/android/.gitignore new file mode 100644 index 000000000000..bc4ef79a4b21 --- /dev/null +++ b/sal/osl/android/.gitignore @@ -0,0 +1,5 @@ +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 new file mode 100644 index 000000000000..c88a9d11576c --- /dev/null +++ b/sal/osl/android/AndroidManifest.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + diff --git a/sal/osl/android/Makefile b/sal/osl/android/Makefile new file mode 100644 index 000000000000..d563e4ea870f --- /dev/null +++ b/sal/osl/android/Makefile @@ -0,0 +1,12 @@ +all: jni/LibreOfficeBootstrap-debug.apk + +jni/LibreOfficeBootstrap-debug.apk : jni/*.c src/org/libreoffice/android/*.java + ndk-build V=1 + cp ../../../solver/unxandr.pro/bin/cppunit/cppunittester libs/armeabi-v7a/libcppunittester.so + cp ../../../solver/unxandr.pro/lib/libcppunit-1.12.so libs/armeabi-v7a + cp ../../../solver/unxandr.pro/lib/libuno_sal.so libs/armeabi-v7a + ant debug + @echo Install it on the device with ant debug install + @echo Then run it with something like: + @echo adb shell am start -a org.libreoffice.android -n org.libreoffice.android/.Bootstrap -e lo-main-library libcppunittester + diff --git a/sal/osl/android/ant.properties b/sal/osl/android/ant.properties new file mode 100644 index 000000000000..ee52d86d94a4 --- /dev/null +++ b/sal/osl/android/ant.properties @@ -0,0 +1,17 @@ +# 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 new file mode 100644 index 000000000000..a186d8d62060 --- /dev/null +++ b/sal/osl/android/build.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sal/osl/android/jni/Android.mk b/sal/osl/android/jni/Android.mk new file mode 100644 index 000000000000..ea6845d4b443 --- /dev/null +++ b/sal/osl/android/jni/Android.mk @@ -0,0 +1,23 @@ +# Copyright (C) 2009 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. +# +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := lo-bootstrap +LOCAL_SRC_FILES := lo-bootstrap.c +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/sal/osl/android/jni/Application.mk b/sal/osl/android/jni/Application.mk new file mode 100644 index 000000000000..a89e12df199c --- /dev/null +++ b/sal/osl/android/jni/Application.mk @@ -0,0 +1,2 @@ +APP_ABI := armeabi-v7a +APP_PLATFORM := android-9 diff --git a/sal/osl/android/jni/lo-bootstrap.c b/sal/osl/android/jni/lo-bootstrap.c new file mode 100644 index 000000000000..3905c51db1a9 --- /dev/null +++ b/sal/osl/android/jni/lo-bootstrap.c @@ -0,0 +1,265 @@ +/* -*- 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#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__)) + +static char * +read_section(JNIEnv *env, + 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; +} + +jobjectArray +Java_org_libreoffice_android_Bootstrap_dlneeds( JNIEnv* env, + jobject clazz, + jstring library) +{ + int i, fd; + int n_needed; + const jbyte *libName; + jobjectArray result; + char *shstrtab, *dynstr; + Elf32_Ehdr hdr; + Elf32_Shdr shdr; + Elf32_Dyn dyn; + + /* Open library and read ELF header */ + + libName = (*env)->GetStringUTFChars(env, library, NULL); + + LOGI("dlneeds(%s)\n", libName); + + fd = open (libName, O_RDONLY); + if (fd == -1) { + LOGI("Could not open library"); + return NULL; + } + + if (read(fd, &hdr, sizeof(hdr)) < sizeof(hdr)) { + close(fd); + LOGI("Could not read ELF header"); + return NULL; + } + + /* Read in .shstrtab */ + + if (lseek(fd, hdr.e_shoff + hdr.e_shstrndx * sizeof(shdr), SEEK_SET) < 0) { + close(fd); + LOGI("Could not seek to .shstrtab section header"); + return NULL; + } + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + close(fd); + LOGI("Could not read section header"); + return NULL; + } + + shstrtab = read_section(env, fd, &shdr); + if (shstrtab == NULL) + return NULL; + + /* Read section headers, looking for .dynstr section */ + + if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { + close(fd); + LOGI("Could not seek to section headers"); + return NULL; + } + for (i = 0; i < hdr.e_shnum; i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + close(fd); + LOGI("Could not read section header"); + return NULL; + } + if (shdr.sh_type == SHT_STRTAB && + strcmp(shstrtab + shdr.sh_name, ".dynstr") == 0) { + dynstr = read_section(env, fd, &shdr); + if (dynstr == NULL) { + free(shstrtab); + return NULL; + } + break; + } + } + + if (i == hdr.e_shnum) { + close(fd); + LOGI("No .dynstr section"); + return NULL; + } + + /* Read section headers, looking for .dynamic section */ + + if (lseek(fd, hdr.e_shoff, SEEK_SET) < 0) { + close(fd); + LOGI("Could not seek to section headers"); + return NULL; + } + for (i = 0; i < hdr.e_shnum; i++) { + if (read(fd, &shdr, sizeof(shdr)) < sizeof(shdr)) { + close(fd); + LOGI("Could not read section header"); + return NULL; + } + if (shdr.sh_type == SHT_DYNAMIC) { + int dynoff; + int *libnames; + jclass String; + + /* Count number of DT_NEEDED entries */ + n_needed = 0; + if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { + close(fd); + LOGI("Could not seek to .dynamic section"); + return NULL; + } + for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { + if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { + close(fd); + LOGI("Could not read .dynamic entry"); + return NULL; + } + if (dyn.d_tag == DT_NEEDED) + n_needed++; + } + + LOGI("Found %d DT_NEEDED libs", n_needed); + + /* Allocate return value */ + + String = (*env)->FindClass(env, "java/lang/String"); + if (String == NULL) { + close(fd); + LOGI("Could not find the String class"); + return NULL; + } + + result = (*env)->NewObjectArray(env, n_needed, String, NULL); + if (result == NULL) { + close (fd); + LOGI("Could not create the String array"); + return NULL; + } + + n_needed = 0; + if (lseek(fd, shdr.sh_offset, SEEK_SET) < 0) { + close(fd); + LOGI("Could not seek to .dynamic section"); + return NULL; + } + for (dynoff = 0; dynoff < shdr.sh_size; dynoff += sizeof(dyn)) { + if (read(fd, &dyn, sizeof(dyn)) < sizeof(dyn)) { + close(fd); + LOGI("Could not read .dynamic entry"); + return NULL; + } + if (dyn.d_tag == DT_NEEDED) { + LOGI("needs: %s\n", dynstr + dyn.d_un.d_val); + (*env)->SetObjectArrayElement(env, result, n_needed, (*env)->NewStringUTF(env, dynstr + dyn.d_un.d_val)); + n_needed++; + } + } + + close(fd); + free(dynstr); + free(shstrtab); + return result; + } + } + + return NULL; +} + +jint +Java_org_libreoffice_android_Bootstrap_dlopen(JNIEnv* env, + jobject clazz, + jstring library) +{ + const jbyte *libName = (*env)->GetStringUTFChars(env, library, NULL); + void *p = dlopen (libName, RTLD_LOCAL); + LOGI("dlopen(%s) = %p", libName, p); + if (p == NULL) { + LOGI(dlerror()); + return 0; + } + 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 = dlsym ((void *)handle, symName); + LOGI("dlsym(%p,%s) = %p", handle, symName, p); + if (p == NULL) { + LOGI(dlerror()); + return 0; + } + return (jint) p; +} + +jint +Java_org_libreoffice_android_Bootstrap_dlcall(JNIEnv* env, + jobject clazz, + jint function, + jobject argument) +{ + /* To be implemented */ + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/android/project.properties b/sal/osl/android/project.properties new file mode 100644 index 000000000000..5a7094538fde --- /dev/null +++ b/sal/osl/android/project.properties @@ -0,0 +1,11 @@ +# 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-7 diff --git a/sal/osl/android/res/layout/main.xml b/sal/osl/android/res/layout/main.xml new file mode 100644 index 000000000000..5839d8cda704 --- /dev/null +++ b/sal/osl/android/res/layout/main.xml @@ -0,0 +1,12 @@ + + + + diff --git a/sal/osl/android/res/values/strings.xml b/sal/osl/android/res/values/strings.xml new file mode 100644 index 000000000000..f296dd4b137d --- /dev/null +++ b/sal/osl/android/res/values/strings.xml @@ -0,0 +1,4 @@ + + + LibreOfficeBootstrap + diff --git a/sal/osl/android/src/org/libreoffice/android/Bootstrap.java b/sal/osl/android/src/org/libreoffice/android/Bootstrap.java new file mode 100644 index 000000000000..ba004e190914 --- /dev/null +++ b/sal/osl/android/src/org/libreoffice/android/Bootstrap.java @@ -0,0 +1,144 @@ +// -*- 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.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.Log; + +import java.io.File; +import java.util.HashMap; + +public class Bootstrap extends Activity +{ + private static String TAG = "lo-bootstrap"; + private String dataDir; + + // A native method to list the DT_NEEDED names in a ELF shared object + public static native String[] dlneeds(String library); + + // A native method to call dlopen(library, RTLD_LOCAL) + public static native int dlopen(String library); + + // A native method to call dlsym(handle, symbol) + public static native int dlsym(int handle, String symbol); + + // A native method to call (*function)(argument) + public static native int dlcall(int function, Object argument); + + // Already loaded libraries + private HashMap presentLibs = new HashMap(); + + private int loadLibrary(String 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 infurtating 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. + + Integer handle; + + if ((handle = presentLibs.get(library)) != null) + return handle; + + String fullName = null; + boolean found = false; + String[] libraryLocations = { "/system/lib/", dataDir + "/lib/" }; + for (String dir : libraryLocations ) { + fullName = dir + library; + if (new File(fullName).exists()) { + found = true; + break; + } + } + if (!found) { + Log.i(TAG, String.format("Library not found: %s\n", library)); + return 0; + } + + String[] needs = dlneeds(fullName); + if (needs == null) + return 0; + + for (String neededLibrary : needs) { + if (loadLibrary(neededLibrary) == 0) + return 0; + } + if ((handle = dlopen(fullName)) == 0) + return 0; + + presentLibs.put(library, handle); + return handle; + } + + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + try { + ApplicationInfo ai = this.getPackageManager().getApplicationInfo + ("org.libreoffice.android", + PackageManager.GET_META_DATA); + Log.i(TAG, String.format("sourceDir=%s\n", ai.sourceDir)); + dataDir = ai.dataDir; + Log.i(TAG, String.format("dataDir=%s\n", dataDir)); + } + catch (PackageManager.NameNotFoundException e) { + } + + Object mainLibrary = getIntent().getExtras().get("lo-main-library"); + + if (mainLibrary != null && mainLibrary instanceof String) { + int loLib = loadLibrary((String)mainLibrary + ".so"); + int loLibMain = dlsym(loLib, "lo_main"); + } + } + + /* 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: */ -- cgit