[Libreoffice-commits] .: 5 commits - android/Bootstrap android/examples android/experiments configure.in jvmfwk/plugins sal/android sal/osl sal/prj sal/util set_soenv.in solenv/inc

Tor Lillqvist tml at kemper.freedesktop.org
Wed Nov 30 12:02:03 PST 2011


 android/Bootstrap/.gitignore                                                                |    6 
 android/Bootstrap/AndroidManifest.xml                                                       |   24 
 android/Bootstrap/Makefile                                                                  |  129 +
 android/Bootstrap/ant.properties                                                            |   17 
 android/Bootstrap/build.xml                                                                 |  114 +
 android/Bootstrap/project.properties                                                        |   11 
 android/Bootstrap/res/layout/main.xml                                                       |   12 
 android/Bootstrap/res/values/strings.xml                                                    |    4 
 android/Bootstrap/src/fi/iki/tml/CommandLine.java                                           |  176 +
 android/Bootstrap/src/org/libreoffice/android/Bootstrap.java                                |  172 +
 android/examples/DocumentLoader/AndroidManifest.xml                                         |   18 
 android/examples/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java    |   97 
 android/experiments/DocumentLoader/AndroidManifest.xml                                      |   18 
 android/experiments/DocumentLoader/Makefile                                                 |  120 +
 android/experiments/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java |  113 +
 configure.in                                                                                |    5 
 jvmfwk/plugins/sunmajor/pluginlib/makefile.mk                                               |    3 
 jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx                                         |   10 
 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 -
 sal/osl/android/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 
 set_soenv.in                                                                                |   55 
 solenv/inc/unxandr.mk                                                                       |    5 
 solenv/inc/unxandr/lo-bootstrap.h                                                           |   64 
 45 files changed, 2964 insertions(+), 2706 deletions(-)

New commits:
commit 5510127e89d6971a219ce3664e4631d6c6dda2b1
Author: Tor Lillqvist <tlillqvist at suse.com>
Date:   Wed Nov 30 21:39:18 2011 +0200

    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.

diff --git a/android/Bootstrap/.gitignore b/android/Bootstrap/.gitignore
new file mode 100644
index 0000000..9034b98
--- /dev/null
+++ b/android/Bootstrap/.gitignore
@@ -0,0 +1,6 @@
+assets
+bin
+gen
+libs
+obj
+local.properties
\ No newline at end of file
diff --git a/android/Bootstrap/AndroidManifest.xml b/android/Bootstrap/AndroidManifest.xml
new file mode 100644
index 0000000..159bbf1
--- /dev/null
+++ b/android/Bootstrap/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.libreoffice.android"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="9" />
+    <!-- This .apk uses a subclass of NativeActivity, so it *has* Java code. -->
+    <!-- android:hasCode="false" -->
+    <application android:label="@string/app_name"
+                 android:debuggable="true">
+        <!-- <activity android:name="android.app.NativeActivity" -->
+        <activity android:name=".Bootstrap"
+                  android:label="LibreOffice Bootstrap"
+                  android:configChanges="orientation|keyboardHidden">
+            <!-- Tell NativeActivity the name of our .so -->
+            <meta-data android:name="android.app.lib_name"
+                    android:value="lo-bootstrap" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/android/Bootstrap/Makefile b/android/Bootstrap/Makefile
new file mode 100644
index 0000000..d8a7004
--- /dev/null
+++ b/android/Bootstrap/Makefile
@@ -0,0 +1,129 @@
+# 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.
+
+all: build-ant lo-bootstrap.jar
+
+copy-stuff:
+#
+# 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.
+#
+	mkdir -p $(SODEST) $(OBJLOCAL)
+#
+# First the most important one, liblo-bootstrap.so
+	$(call COPY,$(OUTDIR)/lib/liblo-bootstrap.so)
+#
+# Then the cppunittester "program". As all "program"
+# files we build for Android actually is a shared object.
+	$(call COPY,$(OUTDIR)/bin/cppunit/cppunittester,libcppunittester.so)
+#
+# The cppunit library
+	$(call COPY,$(OUTDIR)/lib/libcppunit-1.12.so)
+#
+# cppunit "plug-ins", first ones from sal
+#
+	$(call COPY,../../sal/$(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: copy-stuff
+	unset JAVA_HOME && ant debug
+
+lo-bootstrap.jar: build-ant
+	cd bin/classes && jar cvf ../../lo-bootstrap.jar fi/iki/tml org/libreoffice/android/Bootstrap.class
+
+install: copy-stuff lo-bootstrap.jar
+	cp lo-bootstrap.jar $(OUTDIR)bin
+	unset JAVA_HOME && ant debug install
+	@echo
+	@echo 'Run it with something like what "make run" does (see Makefile)'
+	@echo
+
+run:
+# 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"
+#
+	sleep 10
+	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/android/Bootstrap/ant.properties b/android/Bootstrap/ant.properties
new file mode 100644
index 0000000..ee52d86
--- /dev/null
+++ b/android/Bootstrap/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/android/Bootstrap/build.xml b/android/Bootstrap/build.xml
new file mode 100644
index 0000000..6b091c4
--- /dev/null
+++ b/android/Bootstrap/build.xml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="LibreOfficeBootstrap" default="help">
+
+    <!-- The local.properties file is created and updated by the 'android' tool.
+         It contains the path to the SDK. It should *NOT* be checked into
+         Version Control Systems. -->
+    <loadproperties srcFile="local.properties" />
+
+    <!-- The ant.properties file can be created by you. It is only edited by the
+         'android' tool to add properties to it.
+         This is the place to change some Ant specific build properties.
+         Here are some properties you may want to change/update:
+
+         source.dir
+             The name of the source directory. Default is 'src'.
+         out.dir
+             The name of the output directory. Default is 'bin'.
+
+         For other overridable properties, look at the beginning of the rules
+         files in the SDK, at tools/ant/build.xml
+
+         Properties related to the SDK location or the project target should
+         be updated using the 'android' tool with the 'update' action.
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems.
+
+         -->
+    <property file="ant.properties" />
+
+    <!-- The project.properties file is created and updated by the 'android'
+         tool, as well as ADT.
+
+         This contains project specific properties such as project target, and library
+         dependencies. Lower level build properties are stored in ant.properties
+         (or in .classpath for Eclipse projects).
+
+         This file is an integral part of the build system for your
+         application and should be checked into Version Control Systems. -->
+    <loadproperties srcFile="project.properties" />
+
+    <!-- quick check on sdk.dir -->
+    <fail
+            message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
+            unless="sdk.dir"
+    />
+
+
+<!-- extension targets. Uncomment the ones where you want to do custom work
+     in between standard targets -->
+<!--
+    <target name="-pre-build">
+    </target>
+    <target name="-pre-compile">
+    </target>
+
+    /* This is typically used for code obfuscation.
+       Compiled code location: ${out.classes.absolute.dir}
+       If this is not done in place, override ${out.dex.input.absolute.dir} */
+    <target name="-post-compile">
+    </target>
+-->
+
+    <!-- Import the actual build file.
+
+         To customize existing targets, there are two options:
+         - Customize only one target:
+             - copy/paste the target into this file, *before* the
+               <import> task.
+             - customize it to your needs.
+         - Customize the whole content of build.xml
+             - copy/paste the content of the rules files (minus the top node)
+               into this file, replacing the <import> task.
+             - customize to your needs.
+
+         ***********************
+         ****** IMPORTANT ******
+         ***********************
+         In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
+         in order to avoid having your file be overridden by tools such as "android update project"
+    -->
+    <!-- version-tag: 1 -->
+    <import file="${sdk.dir}/tools/ant/build.xml" />
+
+    <!-- Re-define the "-package-resources" target to not compress resources -->
+
+    <target name="-package-resources" depends="-crunch">
+        <!-- only package resources if *not* a library project -->
+        <do-only-if-not-library elseText="Library project: do not package resources..." >
+            <aapt executable="${aapt}"
+                    command="package"
+                    versioncode="${version.code}"
+                    versionname="${version.name}"
+                    debug="${build.is.packaging.debug}"
+                    manifest="AndroidManifest.xml"
+                    assets="${asset.absolute.dir}"
+                    androidjar="${android.jar}"
+                    apkfolder="${out.absolute.dir}"
+                    nocrunch="${build.packaging.nocrunch}"
+                    resourcefilename="${resource.package.file.name}"
+                    resourcefilter="${aapt.resource.filter}"
+                    projectLibrariesResName="project.libraries.res"
+                    projectLibrariesPackageName="project.libraries.package"
+                    previousBuildType="${build.last.target}"
+                    buildType="${build.target}">
+                <res path="${out.res.absolute.dir}" />
+                <res path="${resource.absolute.dir}" />
+                <nocompress /> <!-- forces no compression on any files in assets or res/raw -->
+                <!-- <nocompress extension="xml" /> forces no compression on specific file extensions in assets and res/raw -->
+            </aapt>
+        </do-only-if-not-library>
+    </target>
+
+</project>
diff --git a/android/Bootstrap/project.properties b/android/Bootstrap/project.properties
new file mode 100644
index 0000000..730e911
--- /dev/null
+++ b/android/Bootstrap/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-14
diff --git a/android/Bootstrap/res/layout/main.xml b/android/Bootstrap/res/layout/main.xml
new file mode 100644
index 0000000..5839d8c
--- /dev/null
+++ b/android/Bootstrap/res/layout/main.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    >
+<TextView
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:text="Hello World"
+    />
+</LinearLayout>
diff --git a/android/Bootstrap/res/values/strings.xml b/android/Bootstrap/res/values/strings.xml
new file mode 100644
index 0000000..f296dd4
--- /dev/null
+++ b/android/Bootstrap/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">LibreOfficeBootstrap</string>
+</resources>
diff --git a/android/Bootstrap/src/fi/iki/tml/CommandLine.java b/android/Bootstrap/src/fi/iki/tml/CommandLine.java
new file mode 100644
index 0000000..fb5adec
--- /dev/null
+++ b/android/Bootstrap/src/fi/iki/tml/CommandLine.java
@@ -0,0 +1,176 @@
+// -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+
+// Copyright (C) 2011 Tor Lillqvist <tml at iki.fi>
+//
+// 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<String>
+{
+    private String commandLine;
+
+    public class Tokenizer
+        implements Iterator<String>
+    {
+        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<String> iterator()
+    {
+        return new Tokenizer();
+    }
+
+    public CommandLine(String commandLine)
+    {
+        this.commandLine = commandLine;
+    }
+
+    public String[] split()
+    {
+        ArrayList<String> argv = new ArrayList<String>(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/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java b/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java
new file mode 100644
index 0000000..46ce235
--- /dev/null
+++ b/android/Bootstrap/src/org/libreoffice/android/Bootstrap.java
@@ -0,0 +1,172 @@
+// -*- 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 <tml at iki.fi> (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);
+
+    // Wrapper for putenv()
+    public static native void putenv(String string);
+
+    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/<app name>/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/android/examples/DocumentLoader/AndroidManifest.xml b/android/examples/DocumentLoader/AndroidManifest.xml
deleted file mode 100644
index c137311..0000000
--- a/android/examples/DocumentLoader/AndroidManifest.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="org.libreoffice.android.examples"
-      android:versionCode="1"
-      android:versionName="1.0">
-    <uses-sdk android:minSdkVersion="9" />
-    <application android:label="@string/app_name"
-                 android:debuggable="true">
-        <activity android:name=".DocumentLoader"
-                  android:label="LO DocumentLoader"
-                  android:configChanges="orientation|keyboardHidden">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest>
diff --git a/android/examples/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java b/android/examples/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java
deleted file mode 100644
index 41f69dd..0000000
--- a/android/examples/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java
+++ /dev/null
@@ -1,97 +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 <tml at iki.fi> (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.examples;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-
-import com.sun.star.uno.UnoRuntime;
-
-import org.libreoffice.android.Bootstrap;
-
-public class DocumentLoader
-    extends Activity {
-
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-
-        try {
-
-            Thread.sleep(20000);
-
-            Bootstrap.setup(this);
-
-            Bootstrap.dlopen("libjuh.so");
-
-            com.sun.star.uno.XComponentContext xContext = null;
-
-            xContext = com.sun.star.comp.helper.Bootstrap.defaultBootstrap_InitialComponentContext();
-
-            com.sun.star.lang.XMultiComponentFactory xMCF =
-                xContext.getServiceManager();
-
-            Object oDesktop = xMCF.createInstanceWithContext(
-                "com.sun.star.frame.Desktop", xContext);
-
-            com.sun.star.frame.XComponentLoader xCompLoader =
-                (com.sun.star.frame.XComponentLoader)
-                     UnoRuntime.queryInterface(
-                         com.sun.star.frame.XComponentLoader.class, oDesktop);
-
-            // Getting the given starting directory
-            String sUrl = "file:///assets/inputfile.doc";
-
-            // Loading the wanted document
-            com.sun.star.beans.PropertyValue propertyValues[] =
-                new com.sun.star.beans.PropertyValue[1];
-            propertyValues[0] = new com.sun.star.beans.PropertyValue();
-            propertyValues[0].Name = "Hidden";
-            propertyValues[0].Value = new Boolean(true);
-
-            Object oDoc =
-                xCompLoader.loadComponentFromURL
-                (sUrl, "_blank", 0, propertyValues);
-        }
-        catch (Exception e) {
-            e.printStackTrace(System.err);
-            System.exit(1);
-        }
-    }
-}
-
-// vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/android/experiments/DocumentLoader/AndroidManifest.xml b/android/experiments/DocumentLoader/AndroidManifest.xml
new file mode 100644
index 0000000..c137311
--- /dev/null
+++ b/android/experiments/DocumentLoader/AndroidManifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.libreoffice.android.examples"
+      android:versionCode="1"
+      android:versionName="1.0">
+    <uses-sdk android:minSdkVersion="9" />
+    <application android:label="@string/app_name"
+                 android:debuggable="true">
+        <activity android:name=".DocumentLoader"
+                  android:label="LO DocumentLoader"
+                  android:configChanges="orientation|keyboardHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/android/experiments/DocumentLoader/Makefile b/android/experiments/DocumentLoader/Makefile
new file mode 100644
index 0000000..7db82ae
--- /dev/null
+++ b/android/experiments/DocumentLoader/Makefile
@@ -0,0 +1,120 @@
+NDK_HOME:=$(shell type -p ndk-build)
+NDK_HOME:=$(shell dirname $(NDK_HOME))
+
+SODEST=libs/armeabi-v7a
+OBJLOCAL=obj/local/armeabi-v7a
+
+define COPYSO
+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
+
+define COPYJAR
+cp $(1) libs
+endef
+
+# The default target just builds.
+
+all: build-ant
+
+copy-stuff:
+# First always clean 
+	rm -rf libs $(OBJLOCAL)
+	mkdir -p $(SODEST) $(OBJLOCAL)
+#
+# Copy jar files we need, and even construct one.
+#
+	for F in $(strip \
+		   java_uno \
+		   juh \
+		   jurt \
+		   ridl \
+		   unoloader \
+		  ); do \
+	    $(call COPYJAR,$(OUTDIR)/bin/$${F}.jar); \
+	done
+#
+# lo-bootstrap.jar from ../../Bootstrap
+#
+	cp ../../Bootstrap/lo-bootstrap.jar libs
+#
+# com.sun.star.frame.XComponentLoader is not in any jar
+#
+	cd libs && \
+	    LD_LIBRARY_PATH=$(OUTDIR_FOR_BUILD)/lib \
+	    DYLD_LIBRARY_PATH=$(OUTDIR_FOR_BUILD)/lib \
+	    $(OUTDIR_FOR_BUILD)/bin/javamaker -BUCR -nD \
+		$(OUTDIR)/bin/udkapi.rdb $(OUTDIR)/bin/offapi.rdb \
+		-Tcom.sun.star.frame.XComponentLoader && \
+	    jar cvf more.jar com
+#
+# Copy shared libraries (including UNO components) 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.
+#
+	for F in $(strip \
+		   comphelpgcc3 \
+		   gcc3_uno \
+		   i18nisolang1gcc3 \
+		   i18nutilgcc3 \
+		   icudatalo \
+		   icui18nlo \
+		   icuuclo \
+		   java_uno \
+		   juh \
+		   juhx \
+		   jvmaccessgcc3 \
+		   lo-bootstrap \
+		   localedata_en \
+		   localedata_others \
+		   reg \
+		   sal_textenc \
+		   store \
+		   ucbhelper4gcc3 \
+		   uno_cppu \
+		   uno_sal \
+		   uno_salhelpergcc3 \
+		   uno_cppuhelpergcc3 \
+		   xml2 \
+		   xmlreader \
+		   bootstrap.uno \
+		   i18npool.uno \
+		  ); do \
+	    $(call COPYSO,$(OUTDIR)/lib/lib$${F}.so); \
+	done
+#
+# Then the shared GNU C++ library
+	$(call COPYSO,$(NDK_HOME)/sources/cxx-stl/gnu-libstdc++/libs/armeabi-v7a/libgnustl_shared.so)
+#
+# 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)/bin/uno.ini assets
+	cp $(OUTDIR)/xml/ure/services.rdb assets/xml/ure
+	cp $(SRC_ROOT)/odk/examples/java/DocumentHandling/test/test1.odt assets
+	cp $(WORKDIR)/ComponentTarget/i18npool/util/i18npool.component assets/ComponentTarget/i18npool/util
+#
+# Then gdbserver and gdb.setup so that we can debug with ndk-gdb.
+#
+	cp $(NDK_HOME)/toolchains/arm-linux-androideabi-4.4.3/prebuilt/gdbserver $(SODEST)
+	echo set solib-search-path ./obj/local/armeabi-v7a >$(SODEST)/gdb.setup
+
+build-ant: copy-stuff
+	unset JAVA_HOME && ant debug
+
+install: copy-stuff
+	unset JAVA_HOME && ant debug install
+	@echo
+	@echo 'Run it with something like what "make run" does (see Makefile)'
+	@echo
+
+run: install
+	adb shell am start -n org.libreoffice.android.examples/.DocumentLoader -e input /assets/test1.odt
+
+clean:
+	rm -rf bin assets
diff --git a/android/experiments/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java b/android/experiments/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java
new file mode 100644
index 0000000..819cf82
--- /dev/null
+++ b/android/experiments/DocumentLoader/src/org/libreoffice/android/examples/DocumentLoader.java
@@ -0,0 +1,113 @@
+// -*- 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 <tml at iki.fi> (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.examples;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.sun.star.uno.UnoRuntime;
+
+import org.libreoffice.android.Bootstrap;
+
+public class DocumentLoader
+    extends Activity {
+
+    private static String TAG = "DocumentLoader";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+        try {
+
+            Bootstrap.setup(this);
+
+            Bootstrap.dlopen("libjuh.so");
+
+            // Load more shlibs here explicitly in advance because
+            // that makes debugging work better, sigh
+            Bootstrap.dlopen("libuno_cppu.so");
+            Bootstrap.dlopen("libuno_salhelpergcc3.so");
+            Bootstrap.dlopen("libuno_cppuhelpergcc3.so");
+            Bootstrap.dlopen("libbootstrap.uno.so");
+            Bootstrap.dlopen("libgcc3_uno.so");
+            Bootstrap.dlopen("libjava_uno.so");
+            
+            Bootstrap.putenv("UNO_TYPES=file:///assets/bin/udkapi.rdb file:///assets/bin/types.rdb");
+            Bootstrap.putenv("UNO_SERVICES=file:///assets/xml/ure/services.rdb");
+            // Bootstrap.putenv("INIFILENAME=vnd.sun.star.pathname:/assets/uno.ini");
+
+            com.sun.star.uno.XComponentContext xContext = null;
+
+            xContext = com.sun.star.comp.helper.Bootstrap.defaultBootstrap_InitialComponentContext();
+
+            Log.i(TAG, "xContext is" + (xContext!=null ? " not" : "") + " null");
+
+            com.sun.star.lang.XMultiComponentFactory xMCF =
+                xContext.getServiceManager();
+
+            Object oDesktop = xMCF.createInstanceWithContext(
+                "com.sun.star.frame.Desktop", xContext);
+
+            Log.i(TAG, "oDesktop is" + (oDesktop!=null ? " not" : "") + " null");
+
+            com.sun.star.frame.XComponentLoader xCompLoader =
+                (com.sun.star.frame.XComponentLoader)
+                     UnoRuntime.queryInterface(
+                         com.sun.star.frame.XComponentLoader.class, oDesktop);
+
+            Log.i(TAG, "xCompLoader is" + (xCompLoader!=null ? " not" : "") + " null");
+
+            String input = getIntent().getStringExtra("input");
+            if (input == null)
+                input = "/assets/test1.odt";
+
+            String sUrl = "file://" + input;
+
+            // Loading the wanted document
+            com.sun.star.beans.PropertyValue propertyValues[] =
+                new com.sun.star.beans.PropertyValue[1];
+            propertyValues[0] = new com.sun.star.beans.PropertyValue();
+            propertyValues[0].Name = "Hidden";
+            propertyValues[0].Value = new Boolean(true);
+
+            Object oDoc =
+                xCompLoader.loadComponentFromURL
+                (sUrl, "_blank", 0, propertyValues);
+        }
+        catch (Exception e) {
+            e.printStackTrace(System.err);
+            System.exit(1);
+        }
+    }
+}
+
+// vim:set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/jvmfwk/plugins/sunmajor/pluginlib/makefile.mk b/jvmfwk/plugins/sunmajor/pluginlib/makefile.mk
index 78f1457..03c4f58 100644
--- a/jvmfwk/plugins/sunmajor/pluginlib/makefile.mk
+++ b/jvmfwk/plugins/sunmajor/pluginlib/makefile.mk
@@ -69,6 +69,9 @@ SHL1STDLIBS= \
         $(SALHELPERLIB) \
         $(SALLIB)
         
+.IF "$(OS)" == "ANDROID"
+SHL1STDLIBS+=-llo-bootstrap
+.ENDIF
 
 .IF "$(GUI)" == "WNT"
 .IF "$(COM)"!="GCC"
diff --git a/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx
index 7a54b44..0af05d9 100644
--- a/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx
+++ b/jvmfwk/plugins/sunmajor/pluginlib/sunjavaplugin.cxx
@@ -64,6 +64,10 @@
 #include "vendorlist.hxx"
 #include "diagnostics.h"
 
+#ifdef ANDROID
+#include <lo-bootstrap.h>
+#endif
+
 #if defined HAVE_VALGRIND_H
 #include <valgrind.h>
 #else
@@ -758,10 +762,8 @@ javaPluginError jfw_plugin_startJavaVirtualMachine(
     (void) cOptions;
     // On Android we always have a Java VM as we only expect this code
     // to be run in an Android app anyway.
-    struct JNIInvokeInterface* * (*lo_get_javavm)(void) = (struct JNIInvokeInterface* * (*)(void)) dlsym(RTLD_DEFAULT, "lo_get_javavm");
-    fprintf(stderr, "Got lo_get_javavm = %p", lo_get_javavm);
-    *ppVm = (JavaVM *) (*lo_get_javavm)();
-    fprintf(stderr, "lo_get_javavm returns %p", (*lo_get_javavm)());
+    *ppVm = lo_get_javavm();
+    fprintf(stderr, "lo_get_javavm returns %p", *ppVm);
 #endif
 
    return errcode;
diff --git a/sal/android/android_native_app_glue.c b/sal/android/android_native_app_glue.c
new file mode 100644
index 0000000..8ff9f69
--- /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 <jni.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#include "android_native_app_glue.h"
+#include <android/log.h>
+
+#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 0000000..1b8c1f1
--- /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 <poll.h>
+#include <pthread.h>
+#include <sched.h>
+
+#include <android/configuration.h>
+#include <android/looper.h>
+#include <android/native_activity.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * The native activity interface provided by <android/native_activity.h>
+ * 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 0000000..6ac285d
--- /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 <tml at iki.fi> (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 <mwu at mozilla.com>.
+ *
+ * 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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <sys/mman.h>
+
+#include <jni.h>
+
+#include <linux/elf.h>
+
+#include <android/log.h>
+
+#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 */

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list