diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-05-16 17:08:42 -0700 |
---|---|---|
committer | Jeff Brown <jeffbrown@google.com> | 2011-05-24 12:01:25 -0700 |
commit | ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3 (patch) | |
tree | ae346479a6c069a3a57acecb18a0b0a563325e3f | |
parent | cbc38e74f028d9e337eba0f3ed7c435310cc10c2 (diff) | |
download | frameworks_base-ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3.zip frameworks_base-ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3.tar.gz frameworks_base-ebed7d6e35f7f960e6e6add2b8ab7c7a31a511c3.tar.bz2 |
Support wrapping app processes to inject debug instrumentation.
Bug: 4437846
Change-Id: I4552501c693716b14714afb5c5248edaca9547ab
-rw-r--r-- | cmds/app_process/app_main.cpp | 71 | ||||
-rw-r--r-- | cmds/runtime/main_runtime.cpp | 2 | ||||
-rw-r--r-- | core/java/android/os/Process.java | 14 | ||||
-rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 143 | ||||
-rw-r--r-- | core/java/com/android/internal/os/WrapperInit.java | 117 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 251 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 48 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 24 | ||||
-rw-r--r-- | core/jni/com_android_internal_os_ZygoteInit.cpp | 24 | ||||
-rw-r--r-- | include/android_runtime/AndroidRuntime.h | 10 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 20 |
11 files changed, 515 insertions, 209 deletions
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 371268f..152a7cb 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -149,10 +149,7 @@ int main(int argc, const char* const argv[]) mArgLen--; AppRuntime runtime; - const char *arg; - const char *argv0; - - argv0 = argv[0]; + const char* argv0 = argv[0]; // Process command line arguments // ignore argv[0] @@ -163,39 +160,53 @@ int main(int argc, const char* const argv[]) int i = runtime.addVmArguments(argc, argv); - // Next arg is parent directory - if (i < argc) { - runtime.mParentDir = argv[i++]; - } - - // Next arg is startup classname or "--zygote" - if (i < argc) { - arg = argv[i++]; - if (0 == strcmp("--zygote", arg)) { - bool startSystemServer = (i < argc) ? - strcmp(argv[i], "--start-system-server") == 0 : false; - setArgv0(argv0, "zygote"); - set_process_name("zygote"); - runtime.start("com.android.internal.os.ZygoteInit", - startSystemServer); + // Parse runtime arguments. Stop at first unrecognized option. + bool zygote = false; + bool startSystemServer = false; + bool application = false; + const char* parentDir = NULL; + const char* niceName = NULL; + const char* className = NULL; + while (i < argc) { + const char* arg = argv[i++]; + if (!parentDir) { + parentDir = arg; + } else if (strcmp(arg, "--zygote") == 0) { + zygote = true; + niceName = "zygote"; + } else if (strcmp(arg, "--start-system-server") == 0) { + startSystemServer = true; + } else if (strcmp(arg, "--application") == 0) { + application = true; + } else if (strncmp(arg, "--nice-name=", 12) == 0) { + niceName = arg + 12; } else { - set_process_name(argv0); - - runtime.mClassName = arg; + className = arg; + break; + } + } - // Remainder of args get passed to startup class main() - runtime.mArgC = argc-i; - runtime.mArgV = argv+i; + if (niceName && *niceName) { + setArgv0(argv0, niceName); + set_process_name(niceName); + } - LOGV("App process is starting with pid=%d, class=%s.\n", - getpid(), runtime.getClassName()); - runtime.start(); - } + runtime.mParentDir = parentDir; + + if (zygote) { + runtime.start("com.android.internal.os.ZygoteInit", + startSystemServer ? "start-system-server" : ""); + } else if (className) { + // Remainder of args get passed to startup class main() + runtime.mClassName = className; + runtime.mArgC = argc - i; + runtime.mArgV = argv + i; + runtime.start("com.android.internal.os.RuntimeInit", + application ? "application" : "tool"); } else { fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } - } diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp index 785e4cc..dbff095 100644 --- a/cmds/runtime/main_runtime.cpp +++ b/cmds/runtime/main_runtime.cpp @@ -497,7 +497,7 @@ int main(int argc, char* const argv[]) #ifndef HAVE_ANDROID_OS QuickRuntime* runt = new QuickRuntime(); runt->start("com/android/server/SystemServer", - false /* spontaneously fork system server from zygote */); + "" /* spontaneously fork system server from zygote */); #endif } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f85df6c..d475f36 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -634,6 +634,20 @@ public class Process { } /** + * Returns the parent process id for a currently running process. + * @param pid the process id + * @return the parent process id of the process, or -1 if the process is not running. + * @hide + */ + public static final int getParentPid(int pid) { + String[] procStatusLabels = { "PPid:" }; + long[] procStatusValues = new long[1]; + procStatusValues[0] = -1; + Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues); + return (int) procStatusValues[0]; + } + + /** * Set the priority of a thread, based on Linux priorities. * * @param tid The identifier of the thread/process to change. diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 0f086f6..5e9cd23 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -32,7 +32,6 @@ import dalvik.system.VMRuntime; import java.lang.reflect.Method; import java.lang.reflect.Modifier; -import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.LogManager; import java.util.TimeZone; @@ -45,6 +44,7 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter; */ public class RuntimeInit { private final static String TAG = "AndroidRuntime"; + private final static boolean DEBUG = false; /** true if commonInit() has been called */ private static boolean initialized; @@ -89,14 +89,14 @@ public class RuntimeInit { } private static final void commonInit() { - if (false) Slog.d(TAG, "Entered RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); int hasQwerty = getQwertyKeyboard(); - if (false) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); + if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (hasQwerty == 1) { System.setProperty("qwerty", "1"); } @@ -183,11 +183,6 @@ public class RuntimeInit { */ private static void invokeStaticMain(String className, String[] argv) throws ZygoteInit.MethodAndArgsCaller { - - // We want to be fairly aggressive about heap utilization, to avoid - // holding on to a lot of memory that isn't needed. - VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); - Class<?> cl; try { @@ -225,6 +220,13 @@ public class RuntimeInit { } public static final void main(String[] argv) { + if (argv.length == 2 && argv[1].equals("application")) { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application"); + redirectLogStreams(); + } else { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool"); + } + commonInit(); /* @@ -233,7 +235,7 @@ public class RuntimeInit { */ finishInit(); - if (false) Slog.d(TAG, "Leaving RuntimeInit!"); + if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } public static final native void finishInit(); @@ -245,7 +247,6 @@ public class RuntimeInit { * * Current recognized args: * <ul> - * <li> --nice-name=<i>nice name to appear in ps</i> * <li> <code> [--] <start class name> <args> * </ul> * @@ -253,45 +254,60 @@ public class RuntimeInit { */ public static final void zygoteInit(String[] argv) throws ZygoteInit.MethodAndArgsCaller { - // TODO: Doing this here works, but it seems kind of arbitrary. Find - // a better place. The goal is to set it up for applications, but not - // tools like am. - System.out.close(); - System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); - System.err.close(); - System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); + + redirectLogStreams(); commonInit(); zygoteInitNative(); - int curArg = 0; - for ( /* curArg */ ; curArg < argv.length; curArg++) { - String arg = argv[curArg]; - - if (arg.equals("--")) { - curArg++; - break; - } else if (!arg.startsWith("--")) { - break; - } else if (arg.startsWith("--nice-name=")) { - String niceName = arg.substring(arg.indexOf('=') + 1); - Process.setArgV0(niceName); - } - } + applicationInit(argv); + } - if (curArg == argv.length) { - Slog.e(TAG, "Missing classname argument to RuntimeInit!"); + /** + * The main function called when an application is started through a + * wrapper process. + * + * When the wrapper starts, the runtime starts {@link RuntimeInit#main} + * which calls {@link WrapperInit#main} which then calls this method. + * So we don't need to call commonInit() here. + * + * @param argv arg strings + */ + public static void wrapperInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); + + applicationInit(argv); + } + + private static void applicationInit(String[] argv) + throws ZygoteInit.MethodAndArgsCaller { + // We want to be fairly aggressive about heap utilization, to avoid + // holding on to a lot of memory that isn't needed. + VMRuntime.getRuntime().setTargetHeapUtilization(0.75f); + + final Arguments args; + try { + args = new Arguments(argv); + } catch (IllegalArgumentException ex) { + Slog.e(TAG, ex.getMessage()); // let the process exit return; } // Remaining arguments are passed to the start class's static main + invokeStaticMain(args.startClass, args.startArgs); + } - String startClass = argv[curArg++]; - String[] startArgs = new String[argv.length - curArg]; - - System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); - invokeStaticMain(startClass, startArgs); + /** + * Redirect System.out and System.err to the Android log. + */ + public static void redirectLogStreams() { + System.out.close(); + System.setOut(new AndroidPrintStream(Log.INFO, "System.out")); + System.err.close(); + System.setErr(new AndroidPrintStream(Log.WARN, "System.err")); } public static final native void zygoteInitNative(); @@ -351,4 +367,55 @@ public class RuntimeInit { // Register handlers for DDM messages. android.ddm.DdmRegister.registerHandlers(); } + + /** + * Handles argument parsing for args related to the runtime. + * + * Current recognized args: + * <ul> + * <li> <code> [--] <start class name> <args> + * </ul> + */ + static class Arguments { + /** first non-option argument */ + String startClass; + + /** all following arguments */ + String[] startArgs; + + /** + * Constructs instance and parses args + * @param args runtime command-line args + * @throws IllegalArgumentException + */ + Arguments(String args[]) throws IllegalArgumentException { + parseArgs(args); + } + + /** + * Parses the commandline arguments intended for the Runtime. + */ + private void parseArgs(String args[]) + throws IllegalArgumentException { + int curArg = 0; + for (; curArg < args.length; curArg++) { + String arg = args[curArg]; + + if (arg.equals("--")) { + curArg++; + break; + } else if (!arg.startsWith("--")) { + break; + } + } + + if (curArg == args.length) { + throw new IllegalArgumentException("Missing classname argument to RuntimeInit!"); + } + + startClass = args[curArg++]; + startArgs = new String[args.length - curArg]; + System.arraycopy(args, curArg, startArgs, 0, startArgs.length); + } + } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java new file mode 100644 index 0000000..18d6caa --- /dev/null +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2011 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. + */ + +package com.android.internal.os; + +import android.os.Process; +import android.util.Slog; + +import java.io.DataOutputStream; +import java.io.FileDescriptor; +import java.io.FileOutputStream; +import java.io.IOException; + +import libcore.io.IoUtils; +import libcore.io.Libcore; + +import dalvik.system.Zygote; + +/** + * Startup class for the wrapper process. + * @hide + */ +public class WrapperInit { + private final static String TAG = "AndroidRuntime"; + + /** + * Class not instantiable. + */ + private WrapperInit() { + } + + /** + * The main function called when starting a runtime application through a + * wrapper process instead of by forking Zygote. + * + * The first argument specifies the file descriptor for a pipe that should receive + * the pid of this process, or 0 if none. The remaining arguments are passed to + * the runtime. + * + * @param args The command-line arguments. + */ + public static void main(String[] args) { + try { + int fdNum = Integer.parseInt(args[0], 10); + if (fdNum != 0) { + try { + FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum); + DataOutputStream os = new DataOutputStream(new FileOutputStream(fd)); + os.writeInt(Process.myPid()); + os.close(); + IoUtils.closeQuietly(fd); + } catch (IOException ex) { + Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex); + } + } + + String[] runtimeArgs = new String[args.length - 1]; + System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length); + RuntimeInit.wrapperInit(runtimeArgs); + } catch (ZygoteInit.MethodAndArgsCaller caller) { + caller.run(); + } + } + + /** + * Executes a runtime application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param niceName The nice name for the application, or null if none. + * @param pipeFd The pipe to which the application's pid should be written, or null if none. + * @param args Arguments for {@link RuntimeInit.main}. + */ + public static void execApplication(String invokeWith, String niceName, + FileDescriptor pipeFd, String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/app_process /system/bin --application"); + if (niceName != null) { + command.append(" '--nice-name=").append(niceName).append("'"); + } + command.append(" com.android.internal.os.WrapperInit "); + command.append(pipeFd != null ? pipeFd.getInt$() : 0); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } + + /** + * Executes a standalone application with a wrapper command. + * This method never returns. + * + * @param invokeWith The wrapper command. + * @param classPath The class path. + * @param className The class name to invoke. + * @param args Arguments for the main() method of the specified class. + */ + public static void execStandalone(String invokeWith, String classPath, String className, + String[] args) { + StringBuilder command = new StringBuilder(invokeWith); + command.append(" /system/bin/dalvikvm -classpath '").append(classPath); + command.append("' ").append(className); + Zygote.appendQuotedShellArgs(command, args); + Zygote.execShell(command.toString()); + } +} diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index c473fd2..b872e22 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -26,14 +26,20 @@ import dalvik.system.PathClassLoader; import dalvik.system.Zygote; import java.io.BufferedReader; +import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileDescriptor; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.util.ArrayList; +import libcore.io.ErrnoException; +import libcore.io.IoUtils; +import libcore.io.Libcore; + /** * A connection that can make spawn requests. */ @@ -193,15 +199,20 @@ class ZygoteConnection { new FileOutputStream(descriptors[2])); } - int pid; + int pid = -1; + FileDescriptor childPipeFd = null; + FileDescriptor serverPipeFd = null; try { parsedArgs = new Arguments(args); applyUidSecurityPolicy(parsedArgs, peer); - applyDebuggerSecurityPolicy(parsedArgs); applyRlimitSecurityPolicy(parsedArgs, peer); applyCapabilitiesSecurityPolicy(parsedArgs, peer); + applyInvokeWithSecurityPolicy(parsedArgs, peer); + + applyDebuggerSystemProperty(parsedArgs); + applyInvokeWithSystemProperty(parsedArgs); int[][] rlimits = null; @@ -209,25 +220,45 @@ class ZygoteConnection { rlimits = parsedArgs.rlimits.toArray(intArray2d); } + if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) { + FileDescriptor[] pipeFds = Libcore.os.pipe(); + childPipeFd = pipeFds[1]; + serverPipeFd = pipeFds[0]; + ZygoteInit.setCloseOnExec(serverPipeFd, true); + } + pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids, parsedArgs.debugFlags, rlimits); + } catch (IOException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); + } catch (ErrnoException ex) { + logAndPrintError(newStderr, "Exception creating pipe", ex); } catch (IllegalArgumentException ex) { - logAndPrintError (newStderr, "Invalid zygote arguments", ex); - pid = -1; + logAndPrintError(newStderr, "Invalid zygote arguments", ex); } catch (ZygoteSecurityException ex) { logAndPrintError(newStderr, "Zygote security policy prevents request: ", ex); - pid = -1; } - if (pid == 0) { - // in child - handleChildProc(parsedArgs, descriptors, newStderr); - // should never happen - return true; - } else { /* pid != 0 */ - // in parent...pid of < 0 means failure - return handleParentProc(pid, descriptors, parsedArgs); + try { + if (pid == 0) { + // in child + IoUtils.closeQuietly(serverPipeFd); + serverPipeFd = null; + handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); + + // should never get here, the child is expected to either + // throw ZygoteInit.MethodAndArgsCaller or exec(). + return true; + } else { + // in parent...pid of < 0 means failure + IoUtils.closeQuietly(childPipeFd); + childPipeFd = null; + return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); + } + } finally { + IoUtils.closeQuietly(childPipeFd); + IoUtils.closeQuietly(serverPipeFd); } } @@ -244,8 +275,8 @@ class ZygoteConnection { } /** - * Handles argument parsing for args related to the zygote spawner.<p> - + * Handles argument parsing for args related to the zygote spawner. + * * Current recognized args: * <ul> * <li> --setuid=<i>uid of child process, defaults to 0</i> @@ -274,6 +305,7 @@ class ZygoteConnection { * be handed off to com.android.internal.os.RuntimeInit, rather than * processed directly * Android runtime startup (eg, Binder initialization) is also eschewed. + * <li> --nice-name=<i>nice name to appear in ps</i> * <li> If <code>--runtime-init</code> is present: * [--] <args for RuntimeInit > * <li> If <code>--runtime-init</code> is absent: @@ -307,6 +339,9 @@ class ZygoteConnection { /** from --runtime-init */ boolean runtimeInit; + /** from --nice-name */ + String niceName; + /** from --capabilities */ boolean capabilitiesSpecified; long permittedCapabilities; @@ -315,6 +350,9 @@ class ZygoteConnection { /** from all --rlimit=r,c,m */ ArrayList<int[]> rlimits; + /** from --invoke-with */ + String invokeWith; + /** * Any args after and including the first non-option arg * (or after a '--') @@ -438,6 +476,23 @@ class ZygoteConnection { for (int i = params.length - 1; i >= 0 ; i--) { gids[i] = Integer.parseInt(params[i]); } + } else if (arg.equals("--invoke-with")) { + if (invokeWith != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + try { + invokeWith = args[++curArg]; + } catch (IndexOutOfBoundsException ex) { + throw new IllegalArgumentException( + "--invoke-with requires argument"); + } + } else if (arg.startsWith("--nice-name=")) { + if (niceName != null) { + throw new IllegalArgumentException( + "Duplicate arg specified"); + } + niceName = arg.substring(arg.indexOf('=') + 1); } else { break; } @@ -567,14 +622,15 @@ class ZygoteConnection { /** - * Applies debugger security policy. + * Applies debugger system properties to the zygote arguments. + * * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, * the debugger state is specified via the "--enable-debugger" flag * in the spawn request. * * @param args non-null; zygote spawner args */ - private static void applyDebuggerSecurityPolicy(Arguments args) { + public static void applyDebuggerSystemProperty(Arguments args) { if ("1".equals(SystemProperties.get("ro.debuggable"))) { args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; } @@ -664,12 +720,56 @@ class ZygoteConnection { } /** + * Applies zygote security policy. + * Based on the credentials of the process issuing a zygote command: + * <ol> + * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a + * wrapper command. + * <li> Any other uid may not specify any invoke-with argument. + * </ul> + * + * @param args non-null; zygote spawner arguments + * @param peer non-null; peer credentials + * @throws ZygoteSecurityException + */ + private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer) + throws ZygoteSecurityException { + int peerUid = peer.getUid(); + + if (args.invokeWith != null && peerUid != 0) { + throw new ZygoteSecurityException("Peer is not permitted to specify " + + "an explicit invoke-with wrapper command"); + } + } + + /** + * Applies invoke-with system properties to the zygote arguments. + * + * @param parsedArgs non-null; zygote args + */ + public static void applyInvokeWithSystemProperty(Arguments args) { + if (args.invokeWith == null && args.niceName != null) { + if (args.niceName != null) { + String property = "wrap." + args.niceName; + if (property.length() > 31) { + property = property.substring(0, 31); + } + args.invokeWith = SystemProperties.get(property); + if (args.invokeWith != null && args.invokeWith.length() == 0) { + args.invokeWith = null; + } + } + } + } + + /** * Handles post-fork setup of child proc, closing sockets as appropriate, * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * if successful or returning if failed. * * @param parsedArgs non-null; zygote args * @param descriptors null-ok; new file descriptors for stdio if available. + * @param pipeFd null-ok; pipe for communication back to Zygote. * @param newStderr null-ok; stream to use for stderr until stdio * is reopened. * @@ -677,7 +777,7 @@ class ZygoteConnection { * trampoline to code that invokes static main. */ private void handleChildProc(Arguments parsedArgs, - FileDescriptor[] descriptors, PrintStream newStderr) + FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr) throws ZygoteInit.MethodAndArgsCaller { /* @@ -704,7 +804,7 @@ class ZygoteConnection { descriptors[1], descriptors[2]); for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + IoUtils.closeQuietly(fd); } newStderr = System.err; } catch (IOException ex) { @@ -712,37 +812,48 @@ class ZygoteConnection { } } - if (parsedArgs.runtimeInit) { - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); - } else { - ClassLoader cloader; + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } - if (parsedArgs.classpath != null) { - cloader - = new PathClassLoader(parsedArgs.classpath, - ClassLoader.getSystemClassLoader()); + if (parsedArgs.runtimeInit) { + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs); } else { - cloader = ClassLoader.getSystemClassLoader(); + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); } - + } else { String className; try { className = parsedArgs.remainingArgs[0]; } catch (ArrayIndexOutOfBoundsException ex) { - logAndPrintError (newStderr, + logAndPrintError(newStderr, "Missing required class name argument", null); return; } - String[] mainArgs - = new String[parsedArgs.remainingArgs.length - 1]; + String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1]; System.arraycopy(parsedArgs.remainingArgs, 1, mainArgs, 0, mainArgs.length); - try { - ZygoteInit.invokeStaticMain(cloader, className, mainArgs); - } catch (RuntimeException ex) { - logAndPrintError (newStderr, "Error starting. ", ex); + if (parsedArgs.invokeWith != null) { + WrapperInit.execStandalone(parsedArgs.invokeWith, + parsedArgs.classpath, className, mainArgs); + } else { + ClassLoader cloader; + if (parsedArgs.classpath != null) { + cloader = new PathClassLoader(parsedArgs.classpath, + ClassLoader.getSystemClassLoader()); + } else { + cloader = ClassLoader.getSystemClassLoader(); + } + + try { + ZygoteInit.invokeStaticMain(cloader, className, mainArgs); + } catch (RuntimeException ex) { + logAndPrintError(newStderr, "Error starting.", ex); + } } } } @@ -754,36 +865,54 @@ class ZygoteConnection { * if < 0; * @param descriptors null-ok; file descriptors for child's new stdio if * specified. + * @param pipeFd null-ok; pipe for communication with child. * @param parsedArgs non-null; zygote args * @return true for "exit command loop" and false for "continue command * loop" */ private boolean handleParentProc(int pid, - FileDescriptor[] descriptors, Arguments parsedArgs) { + FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) { + + if (pid > 0) { + setChildPgid(pid); + } + + if (descriptors != null) { + for (FileDescriptor fd: descriptors) { + IoUtils.closeQuietly(fd); + } + } - if(pid > 0) { - // Try to move the new child into the peer's process group. + if (pipeFd != null && pid > 0) { + DataInputStream is = new DataInputStream(new FileInputStream(pipeFd)); + int innerPid = -1; try { - ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + innerPid = is.readInt(); } catch (IOException ex) { - // This exception is expected in the case where - // the peer is not in our session - // TODO get rid of this log message in the case where - // getsid(0) != getsid(peer.getPid()) - Log.i(TAG, "Zygote: setpgid failed. This is " - + "normal if peer is not in our session"); + Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex); + } finally { + try { + is.close(); + } catch (IOException ex) { + } } - } - try { - if (descriptors != null) { - for (FileDescriptor fd: descriptors) { - ZygoteInit.closeDescriptor(fd); + // Ensure that the pid reported by the wrapped process is either the + // child process that we forked, or a descendant of it. + if (innerPid > 0) { + int parentPid = innerPid; + while (parentPid > 0 && parentPid != pid) { + parentPid = Process.getParentPid(parentPid); + } + if (parentPid > 0) { + Log.i(TAG, "Wrapped process has pid " + innerPid); + pid = innerPid; + } else { + Log.w(TAG, "Wrapped process reported a pid that is not a child of " + + "the process that we forked: childPid=" + pid + + " innerPid=" + innerPid); } } - } catch (IOException ex) { - Log.e(TAG, "Error closing passed descriptors in " - + "parent process", ex); } try { @@ -808,6 +937,20 @@ class ZygoteConnection { return false; } + private void setChildPgid(int pid) { + // Try to move the new child into the peer's process group. + try { + ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid())); + } catch (IOException ex) { + // This exception is expected in the case where + // the peer is not in our session + // TODO get rid of this log message in the case where + // getsid(0) != getsid(peer.getPid()) + Log.i(TAG, "Zygote: setpgid failed. This is " + + "normal if peer is not in our session"); + } + } + /** * Logs an error message and prints it to the specified stream, if * provided diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index fbe66e5..157c0bf 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -23,6 +23,7 @@ import android.graphics.drawable.Drawable; import android.net.LocalServerSocket; import android.os.Debug; import android.os.FileUtils; +import android.os.Process; import android.os.SystemClock; import android.os.SystemProperties; import android.util.EventLog; @@ -68,7 +69,7 @@ public class ZygoteInit { private static final int PRELOAD_GC_THRESHOLD = 50000; public static final String USAGE_STRING = - " <\"true\"|\"false\" for startSystemServer>"; + " <\"start-system-server\"|\"\" for startSystemServer>"; private static LocalServerSocket sServerSocket; @@ -441,11 +442,20 @@ public class ZygoteInit { // set umask to 0077 so new files and directories will default to owner-only permissions. FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO); - /* - * Pass the remaining arguments to SystemServer. - * "--nice-name=system_server com.android.server.SystemServer" - */ - RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + if (parsedArgs.niceName != null) { + Process.setArgV0(parsedArgs.niceName); + } + + if (parsedArgs.invokeWith != null) { + WrapperInit.execApplication(parsedArgs.invokeWith, + parsedArgs.niceName, null, parsedArgs.remainingArgs); + } else { + /* + * Pass the remaining arguments to SystemServer. + */ + RuntimeInit.zygoteInit(parsedArgs.remainingArgs); + } + /* should never reach here */ } @@ -470,20 +480,13 @@ public class ZygoteInit { try { parsedArgs = new ZygoteConnection.Arguments(args); - - /* - * Enable debugging of the system process if *either* the command line flags - * indicate it should be debuggable or the ro.debuggable system property - * is set to "1" - */ - int debugFlags = parsedArgs.debugFlags; - if ("1".equals(SystemProperties.get("ro.debuggable"))) - debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + ZygoteConnection.applyDebuggerSystemProperty(parsedArgs); + ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs); /* Request to fork the system server process */ pid = Zygote.forkSystemServer( parsedArgs.uid, parsedArgs.gid, - parsedArgs.gids, debugFlags, null, + parsedArgs.gids, parsedArgs.debugFlags, null, parsedArgs.permittedCapabilities, parsedArgs.effectiveCapabilities); } catch (IllegalArgumentException ex) { @@ -522,9 +525,9 @@ public class ZygoteInit { throw new RuntimeException(argv[0] + USAGE_STRING); } - if (argv[1].equals("true")) { + if (argv[1].equals("start-system-server")) { startSystemServer(); - } else if (!argv[1].equals("false")) { + } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } @@ -696,15 +699,6 @@ public class ZygoteInit { FileDescriptor out, FileDescriptor err) throws IOException; /** - * Calls close() on a file descriptor - * - * @param fd descriptor to close - * @throws IOException - */ - static native void closeDescriptor(FileDescriptor fd) - throws IOException; - - /** * Toggles the close-on-exec flag for the specified file descriptor. * * @param fd non-null; file descriptor diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index b787e9f..e610640 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -808,8 +808,11 @@ char* AndroidRuntime::toSlashClassName(const char* className) * Start the Android runtime. This involves starting the virtual machine * and calling the "static void main(String[] args)" method in the class * named by "className". + * + * Passes the main function two arguments, the class name and the specified + * options string. */ -void AndroidRuntime::start(const char* className, const bool startSystemServer) +void AndroidRuntime::start(const char* className, const char* options) { LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); @@ -820,7 +823,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ - if (startSystemServer) { + if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, @@ -857,13 +860,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) /* * We want to call main() with a String array with arguments in it. - * At present we only have one argument, the class name. Create an - * array to hold it. + * At present we have two arguments, the class name and an option string. + * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; - jstring startSystemServerStr; + jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); @@ -872,9 +875,8 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); - startSystemServerStr = env->NewStringUTF(startSystemServer ? - "true" : "false"); - env->SetObjectArrayElement(strArray, 1, startSystemServerStr); + optionsStr = env->NewStringUTF(options); + env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will @@ -909,12 +911,6 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer) LOGW("Warning: VM did not shut down cleanly\n"); } -void AndroidRuntime::start() -{ - start("com.android.internal.os.RuntimeInit", - false /* Don't start the system server */); -} - void AndroidRuntime::onExit(int code) { LOGV("AndroidRuntime onExit calling exit(%d)", code); diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp index e627e4a..86fd9cb 100644 --- a/core/jni/com_android_internal_os_ZygoteInit.cpp +++ b/core/jni/com_android_internal_os_ZygoteInit.cpp @@ -131,28 +131,6 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env, } while (err < 0 && errno == EINTR); } -static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env, - jobject clazz, jobject descriptor) -{ - int fd; - int err; - - fd = jniGetFDFromFileDescriptor(env, descriptor); - - if (env->ExceptionOccurred() != NULL) { - return; - } - - do { - err = close(fd); - } while (err < 0 && errno == EINTR); - - if (err < 0) { - jniThrowIOException(env, errno); - return; - } -} - static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env, jobject clazz, jobject descriptor, jboolean flag) { @@ -332,8 +310,6 @@ static JNINativeMethod gMethods[] = { "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;" "Ljava/io/FileDescriptor;)V", (void *) com_android_internal_os_ZygoteInit_reopenStdio}, - { "closeDescriptor", "(Ljava/io/FileDescriptor;)V", - (void *) com_android_internal_os_ZygoteInit_closeDescriptor}, { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V", (void *) com_android_internal_os_ZygoteInit_setCloseOnExec}, { "setCapabilities", "(JJ)V", diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h index b02a057..32cd4f5 100644 --- a/include/android_runtime/AndroidRuntime.h +++ b/include/android_runtime/AndroidRuntime.h @@ -39,6 +39,13 @@ public: AndroidRuntime(); virtual ~AndroidRuntime(); + enum StartMode { + Zygote, + SystemServer, + Application, + Tool, + }; + /** * Register a set of methods in the specified class. */ @@ -59,8 +66,7 @@ public: int addVmArguments(int argc, const char* const argv[]); - void start(const char *classname, const bool startSystemServer); - void start(); // start in android.util.RuntimeInit + void start(const char *classname, const char* options); static AndroidRuntime* getRuntime(); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 5aae539..fca2418 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -854,14 +854,6 @@ public final class ActivityManagerService extends ActivityManagerNative * Current sequence id for process LRU updating. */ int mLruSeq = 0; - - /** - * Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar - * is set, indicating the user wants processes started in such a way - * that they can use ANDROID_PROCESS_WRAPPER and know what will be - * running in each process (thus no pre-initialized process, etc). - */ - boolean mSimpleProcessManagement = false; /** * System monitoring: number of processes that died since the last @@ -1455,15 +1447,6 @@ public final class ActivityManagerService extends ActivityManagerNative } private ActivityManagerService() { - String v = System.getenv("ANDROID_SIMPLE_PROCESS_MANAGEMENT"); - if (v != null && Integer.getInteger(v) != 0) { - mSimpleProcessManagement = true; - } - v = System.getenv("ANDROID_DEBUG_APP"); - if (v != null) { - mSimpleProcessManagement = true; - } - Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass()); File dataDir = Environment.getDataDirectory(); @@ -1937,8 +1920,7 @@ public final class ActivityManagerService extends ActivityManagerNative debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; } int pid = Process.start("android.app.ActivityThread", - mSimpleProcessManagement ? app.processName : null, uid, uid, - gids, debugFlags, null); + app.processName, uid, uid, gids, debugFlags, null); BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); synchronized (bs) { if (bs.isOnBattery()) { |