Bug 564327 - Add Java wrapper in embedding/android [1/2]. patch by vlad, blassey, alexp, and me. r=dougt,ted
authorMichael Wu <mwu@mozilla.com>
Wed, 02 Jun 2010 14:55:28 -0700
changeset 43032 67d932ee2153e0fa59825bf7ce6cb6d746a013b3
parent 43031 ef864316356b09c76e11b4369c990a3acea4540d
child 43033 38a1274b603e678592e116c74386648fba76dae7
push idunknown
push userunknown
push dateunknown
reviewersdougt, ted
bugs564327
milestone1.9.3a5pre
Bug 564327 - Add Java wrapper in embedding/android [1/2]. patch by vlad, blassey, alexp, and me. r=dougt,ted
configure.in
embedding/Makefile.in
embedding/android/AndroidManifest.xml.in
embedding/android/App.java.in
embedding/android/GeckoApp.java
embedding/android/GeckoAppShell.java
embedding/android/GeckoEvent.java
embedding/android/GeckoSurfaceView.java
embedding/android/Makefile.in
embedding/android/Restarter.java.in
--- a/configure.in
+++ b/configure.in
@@ -6072,30 +6072,48 @@ case "${target}" in
 esac
 
 MOZ_ARG_ENABLE_BOOL(javaxpcom,
 [  --enable-javaxpcom
                           Enable Java-XPCOM bridge],
     MOZ_JAVAXPCOM=1,
     MOZ_JAVAXPCOM= )
 
+case "$host_os" in
+  cygwin*|msvc*|mks*)
+    if test -n "$JAVA_HOME"; then
+      JAVA_HOME=`cygpath -u \`cygpath -m -s "$JAVA_HOME"\``
+    fi
+    ;;
+  *mingw*)
+    if test -n "$JAVA_HOME"; then
+      JAVA_HOME=`cd "$JAVA_HOME" && pwd`
+    fi
+    ;;
+esac
+
+if test -n "${JAVA_BIN_PATH}"; then
+  dnl Look for javac and jar in the specified path.
+  JAVA_PATH="$JAVA_BIN_PATH"
+else
+  dnl No path specified, so look for javac and jar in $JAVA_HOME & $PATH.
+  JAVA_PATH="$JAVA_HOME/bin:$PATH"
+fi
+
+MOZ_PATH_PROG(JAVA, java, :, [$JAVA_PATH])
+MOZ_PATH_PROG(JAVAC, javac, :, [$JAVA_PATH])
+MOZ_PATH_PROG(JAR, jar, :, [$JAVA_PATH])
+
+if test -n "${JAVA_BIN_PATH}" || test "$OS_TARGET" = Android; then
+  if test -z "$JAVA" || test "$JAVA" = ":" || test -z "$JAVAC" || test "$JAVAC" = ":" || test -z "$JAR" || test "$JAR" = ":"; then
+    AC_MSG_ERROR([The programs java, javac and jar were not found.  Set \$JAVA_HOME to your java sdk directory or use --with-java-bin-path={java-bin-dir}])
+  fi
+fi
+
 if test -n "${MOZ_JAVAXPCOM}"; then
-  case "$host_os" in
-    cygwin*|msvc*|mks*)
-      if test -n "$JAVA_HOME"; then
-        JAVA_HOME=`cygpath -u \`cygpath -m -s "$JAVA_HOME"\``
-      fi
-      ;;
-    *mingw*)
-      if test -n "$JAVA_HOME"; then
-        JAVA_HOME=`cd "$JAVA_HOME" && pwd`
-      fi
-      ;;
-  esac
-
   if test -n "${JAVA_INCLUDE_PATH}"; then
     dnl Make sure jni.h exists in the given include path.
     if test ! -f "$JAVA_INCLUDE_PATH/jni.h"; then
       AC_MSG_ERROR([jni.h was not found in given include path $JAVA_INCLUDE_PATH.])
     fi
   else
     case "$target_os" in
       darwin*)
@@ -6106,31 +6124,16 @@ if test -n "${MOZ_JAVAXPCOM}"; then
         dnl Try $JAVA_HOME
         JAVA_INCLUDE_PATH="$JAVA_HOME/include"
         ;;
     esac
     if test ! -f "$JAVA_INCLUDE_PATH/jni.h"; then
       AC_MSG_ERROR([The header jni.h was not found.  Set \$JAVA_HOME to your java sdk directory, use --with-java-bin-path={java-bin-dir}, or reconfigure with --disable-javaxpcom.])
     fi
   fi
-
-  if test -n "${JAVA_BIN_PATH}"; then
-    dnl Look for javac and jar in the specified path.
-    JAVA_PATH="$JAVA_BIN_PATH"
-  else
-    dnl No path specified, so look for javac and jar in $JAVA_HOME & $PATH.
-    JAVA_PATH="$JAVA_HOME/bin:$PATH"
-  fi
-
-  MOZ_PATH_PROG(JAVA, java, :, [$JAVA_PATH])
-  MOZ_PATH_PROG(JAVAC, javac, :, [$JAVA_PATH])
-  MOZ_PATH_PROG(JAR, jar, :, [$JAVA_PATH])
-  if test -z "$JAVA" || test "$JAVA" = ":" || test -z "$JAVAC" || test "$JAVAC" = ":" || test -z "$JAR" || test "$JAR" = ":"; then
-    AC_MSG_ERROR([The programs java, javac and jar were not found.  Set \$JAVA_HOME to your java sdk directory, use --with-java-bin-path={java-bin-dir}, or reconfigure with --disable-javaxpcom.])
-  fi
 fi
 
 dnl ========================================================
 dnl = Breakpad crash reporting (on by default on supported platforms)
 dnl ========================================================
 
 case $target in
 i?86-*-mingw*)
--- a/embedding/Makefile.in
+++ b/embedding/Makefile.in
@@ -47,9 +47,13 @@ MODULE       = embed
 
 DIRS = base components browser
 
 ifdef ENABLE_TESTS
 XPCSHELL_TESTS = tests/unit
 DIRS += test
 endif
 
+ifeq ($(OS_TARGET),Android)
+TOOL_DIRS = android
+endif
+
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/embedding/android/AndroidManifest.xml.in
@@ -0,0 +1,35 @@
+#filter substitution
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+      package="org.mozilla.@MOZ_APP_NAME@"
+      android:versionCode="1"
+      android:versionName="1.9.3">
+    <uses-sdk android:minSdkVersion="5"
+              android:targetSdkVersion="5"/>
+
+    <uses-permission android:name="android.permission.INTERNET"/> 
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 
+
+    <application android:label="@MOZ_APP_DISPLAYNAME@"
+		 android:icon="@drawable/icon">
+        <activity android:name="App"
+                  android:label="@MOZ_APP_DISPLAYNAME@"
+                  android:configChanges="keyboard|keyboardHidden|orientation|mcc|mnc"
+                  android:windowSoftInputMode="stateUnspecified|adjustResize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+
+            <intent-filter>
+                <action android:name="org.mozilla.gecko.DEBUG" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+	<receiver android:enabled="true" android:name="Restarter">
+	  <intent-filter>
+            <action android:name="org.mozilla.gecko.restart@MOZ_APP_NAME@" />
+	  </intent-filter>
+	</receiver>
+    </application>
+</manifest> 
new file mode 100644
--- /dev/null
+++ b/embedding/android/App.java.in
@@ -0,0 +1,48 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brad Lassey <blassey@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#filter substitution
+package org.mozilla.@MOZ_APP_NAME@;
+
+import org.mozilla.gecko.GeckoApp;
+
+public class App extends GeckoApp {
+    public String getAppName() {
+	return "@MOZ_APP_NAME@";
+    }
+};
+
new file mode 100644
--- /dev/null
+++ b/embedding/android/GeckoApp.java
@@ -0,0 +1,366 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+import java.nio.*;
+
+import android.os.*;
+import android.app.*;
+import android.text.*;
+import android.view.*;
+import android.view.inputmethod.*;
+import android.content.*;
+import android.graphics.*;
+import android.widget.*;
+import android.hardware.*;
+
+import android.util.*;
+
+abstract public class GeckoApp
+    extends Activity
+{
+    public static FrameLayout mainLayout;
+    public static GeckoSurfaceView surfaceView;
+    public static GeckoApp mAppContext;
+
+    public static boolean useSoftwareDrawing;
+
+    void launch()
+    {
+        // unpack files in the components directory
+        unpackComponents();
+        // and then fire us up
+        Intent i = getIntent();
+        String env = i.getStringExtra("env0");
+        Log.i("GeckoApp", "env0: "+ env);
+        for (int c = 1; env != null; c++) {
+            GeckoAppShell.putenv(env);
+            env = i.getStringExtra("env" + c);
+            Log.i("GeckoApp", "env"+ c +": "+ env);
+        }
+        GeckoAppShell.runGecko(getApplication().getPackageResourcePath(),
+                               i.getStringExtra("args"),
+                               i.getDataString());
+    }
+
+    /** Called when the activity is first created. */
+    @Override
+    public void onCreate(Bundle savedInstanceState)
+    {
+        super.onCreate(savedInstanceState);
+
+        mAppContext = this;
+
+        // hide our window's title, we don't want it
+        requestWindowFeature(Window.FEATURE_NO_TITLE);
+
+        surfaceView = new GeckoSurfaceView(this);
+        
+        mainLayout = new FrameLayout(this);
+        mainLayout.addView(surfaceView,
+                           new FrameLayout.LayoutParams(FrameLayout.LayoutParams.FILL_PARENT,
+                                                        FrameLayout.LayoutParams.FILL_PARENT));
+
+        boolean useLaunchButton = false;
+
+        String intentAction = getIntent().getAction();
+        if (intentAction != null && intentAction.equals("org.mozilla.gecko.DEBUG"))
+            useLaunchButton = true;
+
+        setContentView(mainLayout,
+                       new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+                                                  ViewGroup.LayoutParams.FILL_PARENT));
+
+        useSoftwareDrawing = true; //isInEmulator() == 1;
+
+        if (!GeckoAppShell.sGeckoRunning) {
+            // Load our JNI libs; we need to do this before launch() because
+            // setInitialSize will be called even before Gecko is actually up
+            // and running.
+            GeckoAppShell.loadGeckoLibs();
+
+            if (useLaunchButton) {
+                final Button b = new Button(this);
+                b.setText("Launch");
+                b.setOnClickListener(new Button.OnClickListener() {
+                        public void onClick (View v) {
+                            // hide the button so we can't be launched again
+                            mainLayout.removeView(b);
+                            launch();
+                        }
+                    });
+                mainLayout.addView(b, 300, 200);
+            } else {
+                launch();
+            }
+        }
+
+        super.onCreate(savedInstanceState);
+    }
+
+    @Override
+    public void onPause()
+    {
+        // The user is navigating away from this activity, but nothing
+        // has come to the foreground yet; for Gecko, we may want to
+        // stop repainting, for example.
+
+        // Whatever we do here should be fast, because we're blocking
+        // the next activity from showing up until we finish.
+
+        // onPause will be followed by either onResume or onStop.
+        super.onPause();
+    }
+
+    @Override
+    public void onResume()
+    {
+        // After an onPause, the activity is back in the foreground.
+        // Undo whatever we did in onPause.
+        super.onResume();
+    }
+
+    @Override
+    public void onStop()
+    {
+        // We're about to be stopped, potentially in preparation for
+        // being destroyed.  We're killable after this point -- as I
+        // understand it, in extreme cases the process can be terminated
+        // without going through onDestroy.
+        //
+        // We might also get an onRestart after this; not sure what
+        // that would mean for Gecko if we were to kill it here.
+        // Instead, what we should do here is save prefs, session,
+        // etc., and generally mark the profile as 'clean', and then
+        // dirty it again if we get an onResume.
+
+        // XXX do the above.
+
+        super.onStop();
+    }
+
+    @Override
+    public void onDestroy()
+    {
+        // Tell Gecko to shutting down; we'll end up calling System.exit()
+        // in onXreExit.
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(GeckoEvent.ACTIVITY_STOPPING));
+
+        super.onDestroy();
+    }
+
+    @Override
+    public void onConfigurationChanged(android.content.res.Configuration newConfig)
+    {
+        // nothing, just ignore
+        super.onConfigurationChanged(newConfig);
+    }
+
+    @Override
+    public void onLowMemory()
+    {
+        // XXX TODO
+        super.onLowMemory();
+    }
+
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        return true;
+    }
+
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        return true;
+    }
+
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        return true;
+    }
+
+    abstract public String getAppName();
+
+    protected void unpackComponents()
+    {
+        ZipFile zip;
+        InputStream listStream;
+
+        try {
+            File componentsDir = new File("/data/data/org.mozilla." + getAppName() +"/components");
+            componentsDir.mkdir();
+            zip = new ZipFile(getApplication().getPackageResourcePath());
+
+            ZipEntry componentsList = zip.getEntry("components/components.list");
+            if (componentsList == null) {
+                Log.i("GeckoAppJava", "Can't find components.list !");
+                return;
+            }
+
+            listStream = new BufferedInputStream(zip.getInputStream(componentsList));
+        } catch (Exception e) {
+            Log.i("GeckoAppJava", e.toString());
+            return;
+        }
+
+        byte[] buf = new byte[8192];
+
+        StreamTokenizer tkn = new StreamTokenizer(new InputStreamReader(listStream));
+        String line = "components/";
+        int status;
+        tkn.eolIsSignificant(true);
+        do {
+            try {
+                status = tkn.nextToken();
+            } catch (IOException e) {
+                Log.i("GeckoAppJava", e.toString());
+                return;
+            }
+            switch (status) {
+            case StreamTokenizer.TT_WORD:
+                line += tkn.sval;
+                break;
+            case StreamTokenizer.TT_NUMBER:
+                line += tkn.nval;
+                break;
+            case StreamTokenizer.TT_EOF:
+            case StreamTokenizer.TT_EOL:
+                if (!line.endsWith(".js"))
+                    unpackFile(zip, buf, null, line);
+                line = "components/";
+                break;
+            }
+        } while (status != StreamTokenizer.TT_EOF);
+
+        unpackFile(zip, buf, null, "application.ini");
+    }
+
+    private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry, String name)
+    {
+        if (fileEntry == null)
+            fileEntry = zip.getEntry(name);
+        if (fileEntry == null) {
+            Log.i("GeckoAppJava", "Can't find " + name + " in " + zip.getName());
+            return;
+        }
+
+        File outFile = new File("/data/data/org.mozilla." + getAppName() + "/" + name);
+        if (outFile.exists() &&
+            outFile.lastModified() >= fileEntry.getTime() &&
+            outFile.length() == fileEntry.getSize())
+            return;
+
+        try {
+            File dir = outFile.getParentFile();
+            if (!outFile.exists())
+                dir.mkdirs();
+        } catch (Exception e) {
+            Log.i("GeckoAppJava", e.toString());
+            return;
+        }
+
+        InputStream fileStream;
+        try {
+            fileStream = zip.getInputStream(fileEntry);
+        } catch (Exception e) {
+            Log.i("GeckoAppJava", e.toString());
+            return;
+        }
+
+        OutputStream outStream;
+        try {
+            outStream = new FileOutputStream(outFile);
+
+            while (fileStream.available() > 0) {
+                int read = fileStream.read(buf, 0, buf.length);
+                outStream.write(buf, 0, read);
+            }
+
+            fileStream.close();
+            outStream.close();
+        } catch (Exception e) {
+            Log.i("GeckoAppJava", e.toString());
+            return;
+        }
+    }
+    
+    public String getEnvString() {
+        Map<String,String> envMap = System.getenv();
+        Set<Map.Entry<String,String>> envSet = envMap.entrySet();
+        Iterator<Map.Entry<String,String>> envIter = envSet.iterator();
+        StringBuffer envstr = new StringBuffer();
+        int c = 0;
+        while (envIter.hasNext()) {
+            Map.Entry<String,String> entry = envIter.next();
+            // No need to pass env vars that we know the system provides
+            // Unnecessary vars need to be trimmed since amount of data
+            // we can pass this way is limited
+            if (!entry.getKey().equals("BOOTCLASSPATH") &&
+                !entry.getKey().equals("ANDROID_SOCKET_zygote") && 
+                !entry.getKey().equals("TMPDIR") &&
+                !entry.getKey().equals("ANDROID_BOOTLOGO") &&
+                !entry.getKey().equals("EXTERNAL_STORAGE") &&
+                !entry.getKey().equals("ANDROID_ASSETS") &&
+                !entry.getKey().equals("PATH") &&
+                !entry.getKey().equals("TERMINFO") &&
+                !entry.getKey().equals("LD_LIBRARY_PATH") &&
+                !entry.getKey().equals("ANDROID_DATA") &&
+                !entry.getKey().equals("ANDROID_PROPERTY_WORKSPACE") &&
+                !entry.getKey().equals("ANDROID_ROOT")) {
+                envstr.append(" --es env" + c + " " + entry.getKey() + "=" 
+                              + entry.getValue());
+                c++;
+            }
+        }
+        return envstr.toString();        
+    }
+
+    public void doRestart() {
+        try {
+            String action = "org.mozilla.gecko.restart" + getAppName();
+            String amCmd = "/system/bin/am broadcast -a " + action + getEnvString() + " -n org.mozilla." + getAppName() + "/org.mozilla." + getAppName() + ".Restarter";
+            Log.i("GeckoAppJava", amCmd);
+            Runtime.getRuntime().exec(amCmd);
+        } catch (Exception e) {
+            Log.i("GeckoAppJava", e.toString());
+        }
+        System.exit(0);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/embedding/android/GeckoAppShell.java
@@ -0,0 +1,219 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko;
+
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+import java.nio.*;
+
+import android.os.*;
+import android.app.*;
+import android.text.*;
+import android.view.*;
+import android.view.inputmethod.*;
+import android.content.*;
+import android.graphics.*;
+import android.widget.*;
+import android.hardware.*;
+
+import android.util.*;
+import android.content.DialogInterface; 
+
+class GeckoAppShell
+{
+    static {
+        sGeckoRunning = false;
+    }
+
+    // static members only
+    private GeckoAppShell() { }
+
+    static boolean sGeckoRunning;
+
+    static private boolean gRestartScheduled = false;
+
+    /* The Android-side API: API methods that Android calls */
+
+    // Initialization methods
+    public static native void nativeInit();
+    public static native void nativeRun(String args);
+
+    // helper methods
+    public static native void setInitialSize(int width, int height);
+    public static native void setSurfaceView(GeckoSurfaceView sv);
+    public static native void putenv(String map);
+
+    // java-side stuff
+    public static void loadGeckoLibs() {
+        // The package data lib directory isn't placed in ld.so's
+        // search path, so we have to manually load libraries that
+        // libxul will depend on.  Not ideal.
+
+        // MozAlloc
+        System.loadLibrary("mozalloc");
+
+        // NSPR
+        System.loadLibrary("nspr4");
+        System.loadLibrary("plc4");
+        System.loadLibrary("plds4");
+
+        // SQLite
+        System.loadLibrary("mozsqlite3");
+
+        // NSS
+        System.loadLibrary("nssutil3");
+        System.loadLibrary("nss3");
+        System.loadLibrary("ssl3");
+        System.loadLibrary("smime3");
+
+        // JS
+        System.loadLibrary("mozjs");
+
+        // XUL
+        System.loadLibrary("xul");
+
+        // xpcom glue -- needed to load binary components
+        System.loadLibrary("xpcom");                                          
+
+        // Root certs. someday we may teach security/manager/ssl/src/nsNSSComponent.cpp to find ckbi itself
+        System.loadLibrary("nssckbi");
+    }
+
+    public static void runGecko(String apkPath, String args, String url) {
+        // run gecko -- it will spawn its own thread
+        GeckoAppShell.nativeInit();
+
+        // Tell Gecko where the target surface view is for rendering
+        GeckoAppShell.setSurfaceView(GeckoApp.surfaceView);
+
+        sGeckoRunning = true;
+
+        // First argument is the .apk path
+        String combinedArgs = apkPath;
+        if (args != null)
+            combinedArgs += " " + args;
+        if (url != null)
+            combinedArgs += " " + url;
+        // and go
+        GeckoAppShell.nativeRun(combinedArgs);
+    }
+
+    private static GeckoEvent mLastDrawEvent;
+
+    public static void sendEventToGecko(GeckoEvent e) {
+        if (sGeckoRunning)
+            notifyGeckoOfEvent(e);
+    }
+
+    // Tell the Gecko event loop that an event is available.
+    public static native void notifyGeckoOfEvent(GeckoEvent event);
+
+    /*
+     *  The Gecko-side API: API methods that Gecko calls
+     */
+    public static void scheduleRedraw() {
+        // Redraw everything
+        scheduleRedraw(0, -1, -1, -1, -1);
+    }
+
+    public static void scheduleRedraw(int nativeWindow, int x, int y, int w, int h) {
+        GeckoEvent e;
+
+        if (x == -1) {
+            e = new GeckoEvent(GeckoEvent.DRAW, null);
+        } else {
+            e = new GeckoEvent(GeckoEvent.DRAW, new Rect(x, y, w, h));
+        }
+
+        e.mNativeWindow = nativeWindow;
+
+        sendEventToGecko(e);
+    }
+
+    public static void showIME(int state) {
+        InputMethodManager imm = (InputMethodManager) 
+            GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        GeckoApp.surfaceView.mIMEState = state;
+        if (state != 0)
+            imm.showSoftInput(GeckoApp.surfaceView, 0);
+        else
+            imm.hideSoftInputFromWindow(GeckoApp.surfaceView.getWindowToken(), 0);
+    }
+
+    public static void enableAccelerometer(boolean enable) {
+        SensorManager sm = (SensorManager) 
+            GeckoApp.surfaceView.getContext().getSystemService(Context.SENSOR_SERVICE);
+
+        if (enable) {
+            Sensor accelSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+            if (accelSensor == null)
+                return;
+
+            sm.registerListener(GeckoApp.surfaceView, accelSensor, SensorManager.SENSOR_DELAY_GAME);
+        } else {
+            sm.unregisterListener(GeckoApp.surfaceView);
+        }
+    }
+
+    public static void returnIMEQueryResult(String result, int selectionStart, int selectionEnd) {
+        GeckoApp.surfaceView.inputConnection.mSelectionStart = selectionStart;
+        GeckoApp.surfaceView.inputConnection.mSelectionEnd = selectionEnd;
+        try {
+            GeckoApp.surfaceView.inputConnection.mQueryResult.put(result);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    static void onXreExit() {
+        sGeckoRunning = false;
+        Log.i("GeckoAppJava", "XRE exited");
+        if (gRestartScheduled) {
+            GeckoApp.mAppContext.doRestart();
+        } else {
+            Log.i("GeckoAppJava", "we're done, good bye");
+            System.exit(0);
+        }
+
+    }
+    static void scheduleRestart() {
+        Log.i("GeckoAppJava", "scheduling restart");
+        gRestartScheduled = true;        
+    }
+}
new file mode 100644
--- /dev/null
+++ b/embedding/android/GeckoEvent.java
@@ -0,0 +1,165 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2009-2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko;
+
+import android.os.*;
+import android.app.*;
+import android.view.*;
+import android.content.*;
+import android.graphics.*;
+import android.widget.*;
+import android.hardware.*;
+
+import android.util.Log;
+
+/* We're not allowed to hold on to most events given to us
+ * so we save the parts of the events we want to use in GeckoEvent.
+ * Fields have different meanings depending on the event type.
+ */
+
+public class GeckoEvent {
+    public static final int INVALID = -1;
+    public static final int NATIVE_POKE = 0;
+    public static final int KEY_EVENT = 1;
+    public static final int MOTION_EVENT = 2;
+    public static final int SENSOR_EVENT = 3;
+    public static final int IME_EVENT = 4;
+    public static final int DRAW = 5;
+    public static final int SIZE_CHANGED = 6;
+    public static final int ACTIVITY_STOPPING = 7;
+
+    public static final int IME_BATCH_END = 0;
+    public static final int IME_BATCH_BEGIN = 1;
+    public static final int IME_SET_TEXT = 2;
+    public static final int IME_GET_TEXT = 3;
+    public static final int IME_DELETE_TEXT = 4;
+
+    public int mType;
+    public int mAction;
+    public long mTime;
+    public Point mP0, mP1;
+    public Rect mRect;
+    public float mX, mY, mZ;
+
+    public int mMetaState, mFlags;
+    public int mKeyCode, mUnicodeChar;
+    public int mCount, mCount2;
+    public String mCharacters;
+
+    public int mNativeWindow;
+
+    public GeckoEvent() {
+        mType = NATIVE_POKE;
+    }
+
+    public GeckoEvent(int evType) {
+        mType = evType;
+    }
+
+    public GeckoEvent(KeyEvent k) {
+        mType = KEY_EVENT;
+        mAction = k.getAction();
+        mTime = k.getEventTime();
+        mMetaState = k.getMetaState();
+        mFlags = k.getFlags();
+        mKeyCode = k.getKeyCode();
+        mUnicodeChar = k.getUnicodeChar();
+        mCharacters = k.getCharacters();
+    }
+
+    public GeckoEvent(MotionEvent m) {
+        mType = MOTION_EVENT;
+        mAction = m.getAction();
+        mTime = m.getEventTime();
+        mP0 = new Point((int)m.getX(), (int)m.getY());
+    }
+
+    public GeckoEvent(SensorEvent s) {
+        mType = SENSOR_EVENT;
+        mX = s.values[0] / SensorManager.GRAVITY_EARTH;
+        mY = s.values[1] / SensorManager.GRAVITY_EARTH;
+        mZ = s.values[2] / SensorManager.GRAVITY_EARTH;
+    }
+
+    public GeckoEvent(boolean batchEdit, String text) {
+        mType = IME_EVENT;
+        if (text != null)
+            mAction = IME_SET_TEXT;
+        else
+            mAction = batchEdit ? IME_BATCH_BEGIN : IME_BATCH_END;
+        mCharacters = text;
+    }
+
+    public GeckoEvent(boolean forward, int count) {
+        mType = IME_EVENT;
+        mAction = IME_GET_TEXT;
+        if (forward)
+            mCount = count;
+        else
+            mCount2 = count;
+    }
+
+    public GeckoEvent(int leftLen, int rightLen) {
+        mType = IME_EVENT;
+        mAction = IME_DELETE_TEXT;
+        mCount = leftLen;
+        mCount2 = rightLen;
+    }
+
+    public GeckoEvent(int etype, Rect dirty) {
+        if (etype != DRAW) {
+            mType = INVALID;
+            return;
+        }
+
+        mType = etype;
+        mRect = dirty;
+    }
+
+    public GeckoEvent(int etype, int w, int h, int oldw, int oldh) {
+        if (etype != SIZE_CHANGED) {
+            mType = INVALID;
+            return;
+        }
+
+        mType = etype;
+
+        mP0 = new Point(w, h);
+        mP1 = new Point(oldw, oldh);
+    }
+}
new file mode 100644
--- /dev/null
+++ b/embedding/android/GeckoSurfaceView.java
@@ -0,0 +1,838 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package org.mozilla.gecko;
+
+import java.io.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+import java.util.concurrent.atomic.*;
+import java.util.zip.*;
+import java.nio.*;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.os.*;
+import android.app.*;
+import android.text.*;
+import android.view.*;
+import android.view.inputmethod.*;
+import android.content.*;
+import android.graphics.*;
+import android.widget.*;
+import android.hardware.*;
+
+import android.util.*;
+
+/*
+ * GeckoSurfaceView implements a GL surface view,
+ * similar to GLSurfaceView.  However, since we
+ * already have a thread for Gecko, we don't really want
+ * a separate renderer thread that GLSurfaceView provides.
+ */
+class GeckoSurfaceView
+    extends SurfaceView
+    implements SurfaceHolder.Callback, SensorEventListener
+{
+    public GeckoSurfaceView(Context context) {
+        super(context);
+
+        getHolder().addCallback(this);
+        inputConnection = new GeckoInputConnection(this);
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+
+        if (!GeckoApp.useSoftwareDrawing)
+            startEgl();
+
+        mWidth = 0;
+        mHeight = 0;
+        mBufferWidth = 0;
+        mBufferHeight = 0;
+
+        mSurfaceLock = new ReentrantLock();
+    }
+
+    protected void finalize() throws Throwable {
+        super.finalize();
+        if (!GeckoApp.useSoftwareDrawing)
+            finishEgl();
+    }
+
+    private static final int EGL_OPENGL_ES2_BIT = 4;
+    private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+    private void printConfig(EGL10 egl, EGLDisplay display,
+                             EGLConfig config) {
+        int[] attributes = {
+            EGL10.EGL_BUFFER_SIZE,
+            EGL10.EGL_ALPHA_SIZE,
+            EGL10.EGL_BLUE_SIZE,
+            EGL10.EGL_GREEN_SIZE,
+            EGL10.EGL_RED_SIZE,
+            EGL10.EGL_DEPTH_SIZE,
+            EGL10.EGL_STENCIL_SIZE,
+            EGL10.EGL_CONFIG_CAVEAT,
+            EGL10.EGL_CONFIG_ID,
+            EGL10.EGL_LEVEL,
+            EGL10.EGL_MAX_PBUFFER_HEIGHT,
+            EGL10.EGL_MAX_PBUFFER_PIXELS,
+            EGL10.EGL_MAX_PBUFFER_WIDTH,
+            EGL10.EGL_NATIVE_RENDERABLE,
+            EGL10.EGL_NATIVE_VISUAL_ID,
+            EGL10.EGL_NATIVE_VISUAL_TYPE,
+            0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
+            EGL10.EGL_SAMPLES,
+            EGL10.EGL_SAMPLE_BUFFERS,
+            EGL10.EGL_SURFACE_TYPE,
+            EGL10.EGL_TRANSPARENT_TYPE,
+            EGL10.EGL_TRANSPARENT_RED_VALUE,
+            EGL10.EGL_TRANSPARENT_GREEN_VALUE,
+            EGL10.EGL_TRANSPARENT_BLUE_VALUE,
+            0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
+            0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
+            0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
+            0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
+            EGL10.EGL_LUMINANCE_SIZE,
+            EGL10.EGL_ALPHA_MASK_SIZE,
+            EGL10.EGL_COLOR_BUFFER_TYPE,
+            EGL10.EGL_RENDERABLE_TYPE,
+            0x3042 // EGL10.EGL_CONFORMANT
+        };
+        String[] names = {
+            "EGL_BUFFER_SIZE",
+            "EGL_ALPHA_SIZE",
+            "EGL_BLUE_SIZE",
+            "EGL_GREEN_SIZE",
+            "EGL_RED_SIZE",
+            "EGL_DEPTH_SIZE",
+            "EGL_STENCIL_SIZE",
+            "EGL_CONFIG_CAVEAT",
+            "EGL_CONFIG_ID",
+            "EGL_LEVEL",
+            "EGL_MAX_PBUFFER_HEIGHT",
+            "EGL_MAX_PBUFFER_PIXELS",
+            "EGL_MAX_PBUFFER_WIDTH",
+            "EGL_NATIVE_RENDERABLE",
+            "EGL_NATIVE_VISUAL_ID",
+            "EGL_NATIVE_VISUAL_TYPE",
+            "EGL_PRESERVED_RESOURCES",
+            "EGL_SAMPLES",
+            "EGL_SAMPLE_BUFFERS",
+            "EGL_SURFACE_TYPE",
+            "EGL_TRANSPARENT_TYPE",
+            "EGL_TRANSPARENT_RED_VALUE",
+            "EGL_TRANSPARENT_GREEN_VALUE",
+            "EGL_TRANSPARENT_BLUE_VALUE",
+            "EGL_BIND_TO_TEXTURE_RGB",
+            "EGL_BIND_TO_TEXTURE_RGBA",
+            "EGL_MIN_SWAP_INTERVAL",
+            "EGL_MAX_SWAP_INTERVAL",
+            "EGL_LUMINANCE_SIZE",
+            "EGL_ALPHA_MASK_SIZE",
+            "EGL_COLOR_BUFFER_TYPE",
+            "EGL_RENDERABLE_TYPE",
+            "EGL_CONFORMANT"
+        };
+        int[] value = new int[1];
+        for (int i = 0; i < attributes.length; i++) {
+            int attribute = attributes[i];
+            String name = names[i];
+            if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
+                Log.w("GeckoAppJava", String.format("  %s: %d\n", name, value[0]));
+            } else {
+                Log.w("GeckoAppJava", String.format("  %s: failed\n", name));
+                // while (egl.eglGetError() != EGL10.EGL_SUCCESS);
+            }
+        }
+    }
+
+    public void startEgl() {
+        if (mEgl != null)
+            return;
+
+        mEgl = (EGL10) EGLContext.getEGL();
+        mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+        // initialize egl
+        int[] version = new int[2];
+        mEgl.eglInitialize(mEglDisplay, version);
+
+        // flip this to true to dump all the EGL configs
+        if (false) {
+            int[] cs = { EGL10.EGL_NONE };
+            int[] ptrnum = new int[1];
+
+            mEgl.eglChooseConfig(mEglDisplay, cs, null, 0, ptrnum);
+
+            int num = ptrnum[0];
+            EGLConfig[] confs = new EGLConfig[num];
+
+            mEgl.eglChooseConfig(mEglDisplay, cs, confs, num, ptrnum);
+
+            for (int i = 0; i < num; ++i) {
+                Log.w("GeckoAppJava", "===  EGL config " + i + " ===");
+                printConfig(mEgl, mEglDisplay, confs[i]);
+            }
+        }
+    }
+
+    public void finishEgl() {
+        if (mEglDisplay != null) {
+            mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+                                EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+        }
+
+        if (mEglContext != null) {
+            mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+            mEglContext = null;
+        }
+
+        if (mEglSurface != null) {
+            mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+            mEglSurface = null;
+        }
+
+        if (mEglDisplay != null) {
+            mEgl.eglTerminate(mEglDisplay);
+            mEglDisplay = null;
+        }
+
+        mEglConfig = null;
+        mEgl = null;
+
+        mSurfaceChanged = false;
+    }
+
+    public void chooseEglConfig() {
+        int redBits, greenBits, blueBits, alphaBits;
+
+        // There are some shenanigans here.
+        // We're required to choose an exact EGL config to match
+        // the surface format, or we get an error.  However,
+        // on Droid at least, the format is -1, which is PixelFormat.OPAQUE.
+        // That's not a valid format to be reported; that should just be a
+        // valid value for *setFormat*.  We have a catch-all case that
+        // assumes 565 for those cases, but it's pretty ugly.
+
+        Log.i("GeckoAppJava", "GeckoView PixelFormat format is " + mFormat);
+        if (mFormat == PixelFormat.RGB_565) {
+            redBits = 5;
+            greenBits = 6;
+            blueBits = 5;
+            alphaBits = 0;
+        } else if (mFormat == PixelFormat.RGB_888 ||
+                   mFormat == PixelFormat.RGBX_8888)
+        {
+            redBits = 8;
+            greenBits = 8;
+            blueBits = 8;
+            alphaBits = 0;
+        } else if (mFormat == PixelFormat.RGBA_8888) {
+            redBits = 8;
+            greenBits = 8;
+            blueBits = 8;
+            alphaBits = 8;
+        } else {
+            Log.w("GeckoAppJava", "Unknown PixelFormat for surface (format is " + mFormat + "), assuming 5650!");
+            redBits = 5;
+            greenBits = 6;
+            blueBits = 5;
+            alphaBits = 0;
+        }
+
+        // PowerVR SGX (Droid) seems to really want depth == 24 for
+        // performance, even 0 is slower.  However, other platforms,
+        // like Tegra, don't -have- a 24 bit depth config (have 16).
+        // So that's not good.  We'll try with 24 first, and if
+        // nothing, then we'll go to 0.  I'm not sure what the nexus
+        // one chip wants.
+
+        int[] confSpec = new int[] {
+            // DEPTH_SIZE must be the first pair
+            EGL10.EGL_DEPTH_SIZE, 24,
+            EGL10.EGL_RED_SIZE, redBits,
+            EGL10.EGL_GREEN_SIZE, greenBits,
+            EGL10.EGL_BLUE_SIZE, blueBits,
+            EGL10.EGL_ALPHA_SIZE, alphaBits,
+            EGL10.EGL_STENCIL_SIZE, 0,
+            EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE,
+            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL10.EGL_NONE };
+
+        // so tortured to pass an int as an out param...
+        int[] ptrNumConfigs = new int[1];
+        mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
+
+        int numConfigs = ptrNumConfigs[0];
+
+        if (numConfigs == 0) {
+            // twiddle the DEPTH_SIZE value
+            confSpec[1] = 0;
+            Log.i("GeckoAppJava", "Couldn't find any valid EGL configs with 24 bit depth, trying 0.");
+
+            mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
+            numConfigs = ptrNumConfigs[0];
+        }
+
+        if (numConfigs <= 0) {
+            // couldn't find a config?
+            Log.w("GeckoAppJava", "Couldn't find any valid EGL configs, blindly trying them all!");
+
+            int[] fallbackConfSpec = new int[] {
+                EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+                EGL10.EGL_NONE };
+            confSpec = fallbackConfSpec;
+
+            mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
+            numConfigs = ptrNumConfigs[0];
+
+            if (numConfigs == 0) {
+                Log.e("GeckoAppJava", "There aren't any EGL configs available on this system.");
+                finishEgl();
+                mSurfaceValid = false;
+                return;
+            }
+        }
+
+        EGLConfig[] configs = new EGLConfig[numConfigs];
+        mEgl.eglChooseConfig(mEglDisplay, confSpec, configs, numConfigs, ptrNumConfigs);
+
+        // Find the first config that has the exact RGB sizes that we
+        // need for our window surface.
+        int[] ptrVal = new int[1];
+        for (int i = 0; i < configs.length; ++i) {
+            int confRed = -1, confGreen = -1, confBlue = -1, confAlpha = -1;
+
+            if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_RED_SIZE, ptrVal))
+                confRed = ptrVal[0];
+            if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_GREEN_SIZE, ptrVal))
+                confGreen = ptrVal[0];
+            if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_BLUE_SIZE, ptrVal))
+                confBlue = ptrVal[0];
+            if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_ALPHA_SIZE, ptrVal))
+                confAlpha = ptrVal[0];
+
+            if (confRed == redBits &&
+                confGreen == greenBits &&
+                confBlue == blueBits &&
+                confAlpha == alphaBits)
+            {
+                mEglConfig = configs[i];
+                break;
+            }
+        }
+
+        if (mEglConfig == null) {
+            Log.w("GeckoAppJava", "Couldn't find EGL config matching colors; using first, hope it works!");
+            mEglConfig = configs[0];
+        }
+
+        Log.i("GeckoAppJava", "====== Chosen config: ======");
+        printConfig(mEgl, mEglDisplay, mEglConfig);
+
+        mEglContext = null;
+        mEglSurface = null;
+    }
+
+    /*
+     * Called on main thread
+     */
+
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        mSurfaceLock.lock();
+
+        try {
+            if (mInDrawing) {
+                Log.w("GeckoAppJava", "surfaceChanged while mInDrawing is true!");
+            }
+
+            mFormat = format;
+            mWidth = width;
+            mHeight = height;
+            mSurfaceValid = true;
+
+            Log.i("GeckoAppJava", "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
+
+            if (!GeckoApp.useSoftwareDrawing) {
+                chooseEglConfig();
+            }
+
+            // XXX This code doesn't seem to actually get hit
+            if (!GeckoAppShell.sGeckoRunning) {
+                GeckoAppShell.setInitialSize(width, height);
+                return;
+            }
+
+            GeckoEvent e = new GeckoEvent(GeckoEvent.SIZE_CHANGED, width, height, -1, -1);
+            GeckoAppShell.sendEventToGecko(e);
+
+            if (mSurfaceNeedsRedraw) {
+                GeckoAppShell.scheduleRedraw();
+                mSurfaceNeedsRedraw = false;
+            }
+
+            mSurfaceChanged = true;
+
+            //Log.i("GeckoAppJava", "<< surfaceChanged");
+        } finally {
+            mSurfaceLock.unlock();
+        }
+    }
+ 
+    public void surfaceCreated(SurfaceHolder holder) {
+        if (GeckoAppShell.sGeckoRunning)
+            mSurfaceNeedsRedraw = true;
+    }
+ 
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.i("GeckoAppJava", "surface destroyed");
+        mSurfaceValid = false;
+    }
+
+    public ByteBuffer getSoftwareDrawBuffer() {
+        //#ifdef DEBUG
+        if (!mSurfaceLock.isHeldByCurrentThread())
+            Log.e("GeckoAppJava", "getSoftwareDrawBuffer called outside of mSurfaceLock!");
+        //#endif
+
+        return mSoftwareBuffer;
+    }
+
+    /*
+     * Called on Gecko thread
+     */
+
+    public static final int DRAW_ERROR = 0;
+    public static final int DRAW_GLES_2 = 1;
+    public static final int DRAW_SOFTWARE = 2;
+
+    int innerBeginDrawing() {
+        /*
+         * Software (non-GL) rendering
+         */
+        if (GeckoApp.useSoftwareDrawing) {
+            if (mWidth != mBufferWidth ||
+                mHeight != mBufferHeight ||
+                mSurfaceChanged)
+            {
+                if (mWidth*mHeight != mBufferWidth*mBufferHeight)
+                    mSoftwareBuffer = ByteBuffer.allocateDirect(mWidth*mHeight*4);
+
+                mSoftwareBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
+
+                mBufferWidth = mWidth;
+                mBufferHeight = mHeight;
+                mSurfaceChanged = false;
+            }
+
+            mSoftwareCanvas = getHolder().lockCanvas(null);
+            if (mSoftwareCanvas == null) {
+                Log.e("GeckoAppJava", "lockCanvas failed! << beginDrawing");
+                return DRAW_ERROR;
+            }
+
+            return DRAW_SOFTWARE;
+        }
+
+        /*
+         * GL rendering
+         */
+
+        if (mEgl == null || mEglDisplay == null || mEglConfig == null) {
+            Log.e("GeckoAppJava", "beginDrawing called, but EGL was never initialized!");
+
+            mSurfaceLock.unlock();
+            return DRAW_ERROR;
+        }
+
+        /*
+         * If the surface doesn't exist, or if its dimensions or something else changed,
+         * recreate it.
+         */
+        if (mEglSurface == null ||
+            mWidth != mBufferWidth ||
+            mHeight != mBufferHeight ||
+            mSurfaceChanged)
+        {
+            if (mEglContext != null) {
+                mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+                mEglContext = null;
+            }
+
+            if (mEglSurface != null)
+                mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+
+            mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, getHolder(), null);
+            if (mEglSurface == EGL10.EGL_NO_SURFACE)
+            {
+                Log.e("GeckoAppJava", "eglCreateWindowSurface failed!");
+                mSurfaceValid = false;
+                return DRAW_ERROR;
+            }
+
+            mBufferWidth = mWidth;
+            mBufferHeight = mHeight;
+
+            mSurfaceChanged = false;
+        }
+
+        if (mEglContext == null) {
+            int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
+                                  EGL10.EGL_NONE };
+            mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
+            if (mEglContext == EGL10.EGL_NO_CONTEXT)
+            {
+                Log.e("GeckoAppJava", "eglCreateContext failed! " + mEgl.eglGetError());
+                mSurfaceValid = false;
+                return DRAW_ERROR;
+            }
+
+            Log.i("GeckoAppJava", "EglContext created");
+        }
+
+        // Hmm, should we issue this makecurrent for each frame?
+        // We'll likely have to, as other bits might switch the context (e.g.
+        // WebGL, video, etc.).
+
+        if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+            int err = mEgl.eglGetError();
+            Log.e("GeckoAppJava", "eglMakeCurrent failed! " + err);
+            return DRAW_ERROR;
+        }
+
+        return DRAW_GLES_2;
+    }
+
+    public int beginDrawing() {
+        //Log.i("GeckoAppJava", ">> beginDrawing");
+
+        if (mInDrawing) {
+            Log.e("GeckoAppJava", "Recursive beginDrawing call!");
+            return DRAW_ERROR;
+        }
+
+        /* Grab the lock, which we'll hold while we're drawing.
+         * It gets released in endDrawing(), and is also used in surfaceChanged
+         * to make sure that we don't change our surface details while
+         * we're in the middle of drawing (and especially in the middle of
+         * executing beginDrawing/endDrawing).
+         *
+         * We might not need to hold this lock in between
+         * beginDrawing/endDrawing, and might just be able to make
+         * surfaceChanged, beginDrawing, and endDrawing synchronized,
+         * but this way is safer for now.
+         */
+        mSurfaceLock.lock();
+
+        if (!mSurfaceValid) {
+            Log.e("GeckoAppJava", "Surface not valid");
+            mSurfaceLock.unlock();
+            return DRAW_ERROR;
+        }
+
+        // call the inner function to do the work, so we can sanely unlock on error
+        int result = innerBeginDrawing();
+
+        if (result == DRAW_ERROR) {
+            mSurfaceLock.unlock();
+            return DRAW_ERROR;
+        }
+
+        mInDrawing = true;
+        return result;
+    }
+
+    public void endDrawing() {
+        //Log.w("GeckoAppJava", ">> endDrawing");
+
+        if (!mInDrawing) {
+            Log.e("GeckoAppJava", "endDrawing without beginDrawing!");
+            return;
+        }
+
+        try {
+            if (!mSurfaceValid) {
+                Log.e("GeckoAppJava", "endDrawing with false mSurfaceValid");
+                return;
+            }
+
+            if (GeckoApp.useSoftwareDrawing) {
+                if (!mSurfaceChanged) {
+                    mSoftwareBitmap.copyPixelsFromBuffer(mSoftwareBuffer);
+                    mSoftwareCanvas.drawBitmap(mSoftwareBitmap, 0, 0, null);
+
+                    getHolder().unlockCanvasAndPost(mSoftwareCanvas);
+                    mSoftwareCanvas = null;
+                }
+            } else {
+                // If the surface was changed while we were drawing;
+                // don't even bother trying to swap, it won't work,
+                // and may cause further problems.  Note that
+                // eglSwapBuffers might also throw an
+                // IllegalArgumentException; we catch that as well.
+                if (!mSurfaceChanged) {
+                    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+                    if (mEgl.eglGetError() == EGL11.EGL_CONTEXT_LOST)
+                        mSurfaceChanged = true;
+                }
+            }
+        } catch (java.lang.IllegalArgumentException ex) {
+            mSurfaceChanged = true;
+        } finally {
+            mInDrawing = false;
+
+            //#ifdef DEBUG
+            if (!mSurfaceLock.isHeldByCurrentThread())
+                Log.e("GeckoAppJava", "endDrawing while mSurfaceLock not held by current thread!");
+            //#endif
+
+            mSurfaceLock.unlock();
+        }
+    }
+
+    @Override
+    public boolean onCheckIsTextEditor () {
+        return false;
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        outAttrs.inputType = InputType.TYPE_CLASS_TEXT |
+                             InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
+        return inputConnection;
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy)
+    {
+    }
+
+    public void onSensorChanged(SensorEvent event)
+    {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+    }
+
+    // event stuff
+    public boolean onTouchEvent(MotionEvent event) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(event));
+        return true;
+    }
+
+    // Is this surface valid for drawing into?
+    boolean mSurfaceValid;
+
+    // Do we need to force a redraw on surfaceChanged?
+    boolean mSurfaceNeedsRedraw;
+
+    // Has this surface been changed?  (That is,
+    // do we need to recreate buffers?)
+    boolean mSurfaceChanged;
+
+    // Are we actively between beginDrawing/endDrawing?
+    boolean mInDrawing;
+
+    // let's not change stuff around while we're in the middle of
+    // starting drawing, ending drawing, or changing surface
+    // characteristics
+    ReentrantLock mSurfaceLock;
+
+    // Surface format, from surfaceChanged.  Largely
+    // useless.
+    int mFormat;
+
+    // the dimensions of the surface
+    int mWidth;
+    int mHeight;
+
+    // the dimensions of the buffer we're using for drawing,
+    // that is the software buffer or the EGLSurface
+    int mBufferWidth;
+    int mBufferHeight;
+
+    // IME stuff
+    GeckoInputConnection inputConnection;
+    int mIMEState;
+
+    // Software rendering
+    ByteBuffer mSoftwareBuffer;
+    Bitmap mSoftwareBitmap;
+    Canvas mSoftwareCanvas;
+
+    // GL rendering
+    EGL10 mEgl;
+    EGLDisplay mEglDisplay;
+    EGLSurface mEglSurface;
+    EGLConfig mEglConfig;
+    EGLContext mEglContext;
+}
+
+class GeckoInputConnection
+    extends BaseInputConnection
+{
+    public GeckoInputConnection (View targetView) {
+        super(targetView, true);
+        mQueryResult = new SynchronousQueue<String>();
+        mExtractedText.partialStartOffset = -1;
+        mExtractedText.partialEndOffset = -1;
+    }
+
+    @Override
+    public Editable getEditable() {
+        Log.i("GeckoAppJava", "getEditable");
+        return null;
+    }
+
+    @Override
+    public boolean beginBatchEdit() {
+        if (mComposing)
+            return true;
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(true, null));
+        mComposing = true;
+        return true;
+    }
+    @Override
+    public boolean commitCompletion(CompletionInfo text) {
+        Log.i("GeckoAppJava", "Stub: commitCompletion");
+        return true;
+    }
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(true, text.toString()));
+        endBatchEdit();
+        return true;
+    }
+    @Override
+    public boolean deleteSurroundingText(int leftLength, int rightLength) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(leftLength, rightLength));
+        updateExtractedText();
+        return true;
+    }
+    @Override
+    public boolean endBatchEdit() {
+        updateExtractedText();
+        if (!mComposing)
+            return true;
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(false, null));
+        mComposing = false;
+        return true;
+    }
+    @Override
+    public boolean finishComposingText() {
+        endBatchEdit();
+        return true;
+    }
+    @Override
+    public int getCursorCapsMode(int reqModes) {
+        return 0;
+    }
+    @Override
+    public ExtractedText getExtractedText(ExtractedTextRequest req, int flags) {
+        mExtractToken = req.token;
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(false, 0));
+        try {
+            mExtractedText.text = mQueryResult.take();
+            mExtractedText.selectionStart = mSelectionStart;
+            mExtractedText.selectionEnd = mSelectionEnd;
+        } catch (InterruptedException e) {
+            Log.i("GeckoAppJava", "getExtractedText: Interrupted!");
+        }
+        return mExtractedText;
+    }
+    @Override
+    public CharSequence getTextAfterCursor(int length, int flags) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(true, length));
+        try {
+            String result = mQueryResult.take();
+            return result;
+        } catch (InterruptedException e) {
+            Log.i("GeckoAppJava", "getTextAfterCursor: Interrupted!");
+        }
+        return null;
+    }
+    @Override
+    public CharSequence getTextBeforeCursor(int length, int flags) {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(false, length));
+        try {
+            String result = mQueryResult.take();
+            return result;
+        } catch (InterruptedException e) {
+            Log.i("GeckoAppJava", "getTextBeforeCursor: Interrupted!");
+        }
+        return null;
+    }
+    @Override
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        beginBatchEdit();
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(true, text.toString()));
+        return true;
+    }
+    @Override
+    public boolean setSelection(int start, int end) {
+        Log.i("GeckoAppJava", "Stub: setSelection " + start + " " + end);
+        return true;
+    }
+
+    private void updateExtractedText() {
+        GeckoAppShell.sendEventToGecko(new GeckoEvent(false, 0));
+        try {
+            mExtractedText.text = mQueryResult.take();
+            mExtractedText.selectionStart = mSelectionStart;
+            mExtractedText.selectionEnd = mSelectionEnd;
+        } catch (InterruptedException e) {
+            Log.i("GeckoAppJava", "getExtractedText: Interrupted!");
+        }
+
+        InputMethodManager imm = (InputMethodManager)
+            GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        imm.updateExtractedText(GeckoApp.surfaceView, mExtractToken, mExtractedText);
+    }
+
+    boolean mComposing;
+    int mExtractToken;
+    final ExtractedText mExtractedText = new ExtractedText();
+
+    int mSelectionStart, mSelectionEnd;
+    SynchronousQueue<String> mQueryResult;
+}
new file mode 100644
--- /dev/null
+++ b/embedding/android/Makefile.in
@@ -0,0 +1,182 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# 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. 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.
+#
+# The Original Code is the Mozilla browser.
+#
+# The Initial Developer of the Original Code is
+#   Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2009-2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Vladimir Vukicevic <vladimir@pobox.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+DX=$(ANDROID_SDK)/tools/dx
+AAPT=$(ANDROID_SDK)/tools/aapt
+APKBUILDER=$(ANDROID_SDK)/../../tools/apkbuilder
+ZIPALIGN=$(ANDROID_SDK)/../../tools/zipalign
+
+JAVAFILES = \
+  GeckoApp.java \
+  GeckoAppShell.java \
+  GeckoEvent.java \
+  GeckoSurfaceView.java \
+  $(NULL)
+
+DEFINES += \
+  -DMOZ_APP_DISPLAYNAME=$(MOZ_APP_DISPLAYNAME) \
+  -DMOZ_APP_NAME=$(MOZ_APP_NAME)
+
+GARBAGE += \
+  AndroidManifest.xml  \
+  classes.dex  \
+  $(MOZ_APP_NAME).apk  \
+  App.java \
+  Restarter.java \
+  gecko.ap_  \
+  gecko-unaligned.apk  \
+  gecko-unsigned-unaligned.apk \
+  $(NULL)
+
+GARBAGE_DIRS += res libs dist classes
+
+ifdef JARSIGNER
+  APKBUILDER_FLAGS += -u
+endif
+
+# Bug 567884 - Need a way to find appropriate icons during packaging
+ifeq ($(MOZ_APP_NAME),fennec)
+ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_48x48.png
+ICON_PATH_HI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/fennec_72x72.png
+else
+ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon48.png
+ICON_PATH_HI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
+endif
+
+NATIVE_LIBS = $(shell cat $(DIST)/bin/dependentlibs.list) libxpcom.so libnssckbi.so libfreebl3.so
+FULL_LIBS = $(addprefix libs/armeabi/,$(NATIVE_LIBS))
+
+# We'll strip all the libs by default, due to size, but we might
+# want to have a non-stripped version for debugging.  We should
+# really pull out debuginfo and stuff.
+ifdef NO_STRIP
+DO_STRIP=echo not stripping
+else
+DO_STRIP=$(STRIP)
+endif
+
+# The set of files/directories from the dist/bin dir that we'll make available in the apk
+# some directories not listed due to special handling
+DIST_LINK_FILES = \
+  modules \
+  res \
+  application.ini \
+  platform.ini \
+  greprefs.js \
+  browserconfig.properties \
+  blocklist.xml \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+# rules.mk has some java stuff, but we're going to ignore it for now
+JAVAC_FLAGS = \
+  -target 1.5 \
+  -classpath $(ANDROID_SDK)/android.jar \
+  -bootclasspath $(ANDROID_SDK)/android.jar \
+  -encoding ascii \
+  -g \
+  $(NULL)
+
+tools:: $(MOZ_APP_NAME).apk
+
+# Note that we're going to set up a dependency directly between embed_android.dex and the java files
+# Instead of on the .class files, since more than one .class file might be produced per .java file
+classes.dex: $(JAVAFILES) App.java Restarter.java
+	$(NSINSTALL) -D classes
+	$(JAVAC) $(JAVAC_FLAGS) -d classes  $(addprefix $(srcdir)/,$(JAVAFILES)) App.java Restarter.java
+	$(DX) --dex --output=$@ classes
+
+AndroidManifest.xml App.java Restarter.java : % : %.in
+	$(PYTHON) $(topsrcdir)/config/Preprocessor.py \
+             $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@
+
+res/drawable/icon.png: $(MOZ_APP_ICON)
+	$(NSINSTALL) -D res/drawable
+	cp $(ICON_PATH) $@
+
+res/drawable-hdpi/icon.png: $(MOZ_APP_ICON)
+	$(NSINSTALL) -D res/drawable-hdpi
+	cp $(ICON_PATH_HI) $@
+
+gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png
+	$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar  -S res -F $@
+
+libs/armeabi/%: $(DIST)/lib/%
+	@$(NSINSTALL) -D libs/armeabi
+	@cp -L -v $< $@
+	@$(DO_STRIP) $@
+
+# Bug 567873 - Android packaging should use standard packaging code
+dist: FORCE
+	$(NSINSTALL) -D dist/components
+	rm -f dist/components/*
+	@(for f in $(DIST)/bin/components/* ; do ln -sf ../../$$f dist/components ; done)
+	$(DIST)/host/bin/host_xpt_link gecko.xpt dist/components/*.xpt
+	rm dist/components/*.xpt
+	mv gecko.xpt dist/components
+	$(NSINSTALL) -D dist/chrome
+	rm -f dist/chrome/*
+	@(for f in $(DIST)/bin/chrome/* ; do ln -sf ../../$$f dist/chrome ; done)
+	@(for MANIFEST in dist/chrome/*.manifest ; do cat "$$MANIFEST" >> chrome.manifest ; echo >> chrome.manifest; rm "$$MANIFEST" ; done )
+	mv chrome.manifest dist/chrome/
+	$(NSINSTALL) -D dist/defaults
+	rm -f dist/defaults/*
+	@(for f in $(DIST)/bin/defaults/* ; do ln -sf ../../$$f dist/defaults ; done )
+	@(for PREF in $(DIST)/bin/defaults/pref/*.js ; do cat "$$PREF" >> dist/defaults/prefs.js ; echo >> dist/defaults/prefs.js ; done )
+	rm dist/defaults/pref
+	@(for f in $(DIST_LINK_FILES) ; do if [ -e $(DIST)/bin/$$f ] ; then echo $$f ; ln -sf ../$(DIST)/bin/$$f dist ; fi ; done)
+
+gecko-unsigned-unaligned.apk: gecko.ap_ classes.dex dist $(FULL_LIBS)
+	$(APKBUILDER) $@ -v $(APKBUILDER_FLAGS) -z gecko.ap_ -f classes.dex -nf `pwd`/libs -rf dist
+
+gecko-unaligned.apk: gecko-unsigned-unaligned.apk
+	cp gecko-unsigned-unaligned.apk $@
+ifdef JARSIGNER
+	$(JARSIGNER) $@
+endif
+
+$(MOZ_APP_NAME).apk: gecko-unaligned.apk
+	$(ZIPALIGN) -f -v 4 gecko-unaligned.apk $@
+
new file mode 100644
--- /dev/null
+++ b/embedding/android/Restarter.java.in
@@ -0,0 +1,91 @@
+/* -*- Mode: Java; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * 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. 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.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Brad Lassey <blassey@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#filter substitution
+package org.mozilla.@MOZ_APP_NAME@;
+
+import android.content.*;
+import android.util.Log;
+import java.io.*;
+
+public class Restarter extends BroadcastReceiver {
+    @Override 
+    public void onReceive(Context context, Intent intent) {
+        Log.i("Restarter", "trying to restart @MOZ_APP_NAME@");
+        try { 
+            boolean stillRunning;
+            do {
+                stillRunning = false;
+                Process p = Runtime.getRuntime().exec("/system/bin/ps");
+                BufferedReader psOut = new BufferedReader(new InputStreamReader(p.getInputStream()));
+                String line;
+                Log.i("Restarter", "pid: " + new Integer(android.os.Process.myPid()).toString());
+                while((line = psOut.readLine()) != null) {
+                    Log.i("Restarter", "ps: " + line);
+                    if (line.contains("org.mozilla.@MOZ_APP_NAME@.App")){
+                        if (!line.contains(new Integer(android.os.Process.myPid()).toString())){
+                            Log.i("Restarter", "app still running, wait a bit");
+                            stillRunning = true;
+                            Thread.sleep(500);
+                            break; // don't need to look at the rest
+                        } else {
+                            Log.i("Restarter", "found ourselves");
+                        }
+                    }
+                }
+            } while(stillRunning);
+        } catch (Exception e) {
+                Log.i("Restarter", e.toString());
+        }
+        try {
+            String action = "android.intent.action.MAIN";
+            String env;
+            StringBuffer envstr = new StringBuffer();
+            for (int c = 0; (env = intent.getStringExtra("env" + c)) != null; c++) {
+                envstr.append(" --es env" + c + " " + env);
+            }
+            String amCmd = "/system/bin/am start -a " + action + envstr.toString() + 
+                " -n org.mozilla.@MOZ_APP_NAME@/org.mozilla.@MOZ_APP_NAME@.App";
+            Log.i("Restarter", amCmd);
+
+            Runtime.getRuntime().exec(amCmd);
+        } catch (Exception e) {
+            Log.i("Restarter", e.toString());
+        }
+        System.exit(0);    
+    }
+};