From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Daniel Micay <danielmicay@gmail.com>
Date: Sat, 14 Mar 2015 18:10:20 -0400
Subject: [PATCH] add exec-based spawning support

---
 .../com/android/internal/os/ExecInit.java     | 115 ++++++++++++++++++
 .../com/android/internal/os/WrapperInit.java  |   2 +-
 .../android/internal/os/ZygoteConnection.java |  10 +-
 3 files changed, 125 insertions(+), 2 deletions(-)
 create mode 100644 core/java/com/android/internal/os/ExecInit.java

diff --git a/core/java/com/android/internal/os/ExecInit.java b/core/java/com/android/internal/os/ExecInit.java
new file mode 100644
index 000000000000..2adcab7fdbe6
--- /dev/null
+++ b/core/java/com/android/internal/os/ExecInit.java
@@ -0,0 +1,115 @@
+package com.android.internal.os;
+
+import android.os.Trace;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Slog;
+import android.util.TimingsTraceLog;
+import dalvik.system.VMRuntime;
+
+/**
+ * Startup class for the process.
+ * @hide
+ */
+public class ExecInit {
+    /**
+     * Class not instantiable.
+     */
+    private ExecInit() {
+    }
+
+    /**
+     * The main function called when starting a runtime application.
+     *
+     * The first argument is the target SDK version for the app.
+     *
+     * The remaining arguments are passed to the runtime.
+     *
+     * @param args The command-line arguments.
+     */
+    public static void main(String[] args) {
+        // Parse our mandatory argument.
+        int targetSdkVersion = Integer.parseInt(args[0], 10);
+
+        // Mimic system Zygote preloading.
+        ZygoteInit.preload(new TimingsTraceLog("ExecInitTiming",
+                Trace.TRACE_TAG_DALVIK));
+
+        // Launch the application.
+        String[] runtimeArgs = new String[args.length - 1];
+        System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length);
+        Runnable r = execInit(targetSdkVersion, runtimeArgs);
+
+        r.run();
+    }
+
+    /**
+     * Executes a runtime application with exec-based spawning.
+     * This method never returns.
+     *
+     * @param niceName The nice name for the application, or null if none.
+     * @param targetSdkVersion The target SDK version for the app.
+     * @param args Arguments for {@link RuntimeInit#main}.
+     */
+    public static void execApplication(String niceName, int targetSdkVersion,
+            String instructionSet, String[] args) {
+        int niceArgs = niceName == null ? 0 : 1;
+        int baseArgs = 5 + niceArgs;
+        String[] argv = new String[baseArgs + args.length];
+        if (VMRuntime.is64BitInstructionSet(instructionSet)) {
+            argv[0] = "/system/bin/app_process64";
+        } else {
+            argv[0] = "/system/bin/app_process32";
+        }
+        argv[1] = "/system/bin";
+        argv[2] = "--application";
+        if (niceName != null) {
+            argv[3] = "--nice-name=" + niceName;
+        }
+        argv[3 + niceArgs] = "com.android.internal.os.ExecInit";
+        argv[4 + niceArgs] = Integer.toString(targetSdkVersion);
+        System.arraycopy(args, 0, argv, baseArgs, args.length);
+
+        WrapperInit.preserveCapabilities();
+        try {
+            Os.execv(argv[0], argv);
+        } catch (ErrnoException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * The main function called when an application is started with exec-based spawning.
+     *
+     * When the app starts, the runtime starts {@link RuntimeInit#main}
+     * which calls {@link main} which then calls this method.
+     * So we don't need to call commonInit() here.
+     *
+     * @param targetSdkVersion target SDK version
+     * @param argv arg strings
+     */
+    private static Runnable execInit(int targetSdkVersion, String[] argv) {
+        if (RuntimeInit.DEBUG) {
+            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from exec");
+        }
+
+        // Check whether the first argument is a "-cp" in argv, and assume the next argument is the
+        // classpath. If found, create a PathClassLoader and use it for applicationInit.
+        ClassLoader classLoader = null;
+        if (argv != null && argv.length > 2 && argv[0].equals("-cp")) {
+            classLoader = ZygoteInit.createPathClassLoader(argv[1], targetSdkVersion);
+
+            // Install this classloader as the context classloader, too.
+            Thread.currentThread().setContextClassLoader(classLoader);
+
+            // Remove the classpath from the arguments.
+            String removedArgs[] = new String[argv.length - 2];
+            System.arraycopy(argv, 2, removedArgs, 0, argv.length - 2);
+            argv = removedArgs;
+        }
+
+        // Perform the same initialization that would happen after the Zygote forks.
+        Zygote.nativePreApplicationInit();
+        return RuntimeInit.applicationInit(targetSdkVersion, /*disabledCompatChanges*/ null, argv, classLoader);
+    }
+}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 6860759eea8a..a2eef62f80be 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -186,7 +186,7 @@ public class WrapperInit {
      *       This is acceptable here as failure will leave the wrapped app with strictly less
      *       capabilities, which may make it crash, but not exceed its allowances.
      */
-    private static void preserveCapabilities() {
+    public static void preserveCapabilities() {
         StructCapUserHeader header = new StructCapUserHeader(
                 OsConstants._LINUX_CAPABILITY_VERSION_3, 0);
         StructCapUserData[] data;
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 993e4e7b4b3d..c3e6e0453b50 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -29,6 +29,7 @@ import android.net.Credentials;
 import android.net.LocalSocket;
 import android.os.Parcel;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.os.Trace;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -247,7 +248,7 @@ class ZygoteConnection {
                     fdsToClose[1] = zygoteFd.getInt$();
                 }
 
-                if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
+                if (parsedArgs.mInvokeWith != null || SystemProperties.getBoolean("sys.spawn.exec", false) || parsedArgs.mStartChildZygote
                         || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
                     // Continue using old code for now. TODO: Handle these cases in the other path.
                     pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
@@ -535,6 +536,13 @@ class ZygoteConnection {
             throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
         } else {
             if (!isZygote) {
+                if (SystemProperties.getBoolean("sys.spawn.exec", false)) {
+                    ExecInit.execApplication(parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
+                            VMRuntime.getCurrentInstructionSet(), parsedArgs.mRemainingArgs);
+
+                    // Should not get here.
+                    throw new IllegalStateException("ExecInit.execApplication unexpectedly returned");
+                }
                 return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                         parsedArgs.mDisabledCompatChanges,
                         parsedArgs.mRemainingArgs, null /* classLoader */);
