am 7e79b9aa: am dbff9107: Merge "Support wrapping app processes to inject debug instrumentation. Bug: 4437846"

* commit '7e79b9aad85db332d334f8bc1625bf46f030830f':
  Support wrapping app processes to inject debug instrumentation. Bug: 4437846
This commit is contained in:
Jeff Brown
2011-08-10 13:59:41 -07:00
committed by Android Git Automerger
13 changed files with 637 additions and 226 deletions

View File

@@ -128,10 +128,7 @@ int main(int argc, const char* const argv[])
mArgLen--; mArgLen--;
AppRuntime runtime; AppRuntime runtime;
const char *arg; const char* argv0 = argv[0];
const char *argv0;
argv0 = argv[0];
// Process command line arguments // Process command line arguments
// ignore argv[0] // ignore argv[0]
@@ -142,39 +139,53 @@ int main(int argc, const char* const argv[])
int i = runtime.addVmArguments(argc, argv); int i = runtime.addVmArguments(argc, argv);
// Next arg is parent directory // Parse runtime arguments. Stop at first unrecognized option.
if (i < argc) { bool zygote = false;
runtime.mParentDir = argv[i++]; 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 {
className = arg;
break;
}
} }
// Next arg is startup classname or "--zygote" if (niceName && *niceName) {
if (i < argc) { setArgv0(argv0, niceName);
arg = argv[i++]; set_process_name(niceName);
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);
} else {
set_process_name(argv0);
runtime.mClassName = arg; runtime.mParentDir = parentDir;
// Remainder of args get passed to startup class main() if (zygote) {
runtime.mArgC = argc-i; runtime.start("com.android.internal.os.ZygoteInit",
runtime.mArgV = argv+i; startSystemServer ? "start-system-server" : "");
} else if (className) {
LOGV("App process is starting with pid=%d, class=%s.\n", // Remainder of args get passed to startup class main()
getpid(), runtime.getClassName()); runtime.mClassName = className;
runtime.start(); runtime.mArgC = argc - i;
} runtime.mArgV = argv + i;
runtime.start("com.android.internal.os.RuntimeInit",
application ? "application" : "tool");
} else { } else {
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
fprintf(stderr, "Error: no class name or --zygote supplied.\n"); fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage(); app_usage();
return 10; return 10;
} }
} }

View File

@@ -497,8 +497,8 @@ int main(int argc, char* const argv[])
} else { } else {
#ifndef HAVE_ANDROID_OS #ifndef HAVE_ANDROID_OS
QuickRuntime* runt = new QuickRuntime(); QuickRuntime* runt = new QuickRuntime();
runt->start("com/android/server/SystemServer", runt->start("com/android/server/SystemServer",
false /* spontaneously fork system server from zygote */); "" /* spontaneously fork system server from zygote */);
#endif #endif
} }

View File

@@ -257,13 +257,12 @@ public class Process {
* @param enableDebugger True if debugging should be enabled for this process. * @param enableDebugger True if debugging should be enabled for this process.
* @param zygoteArgs Additional arguments to supply to the zygote process. * @param zygoteArgs Additional arguments to supply to the zygote process.
* *
* @return int If > 0 the pid of the new process; if 0 the process is * @return An object that describes the result of the attempt to start the process.
* being emulated by a thread
* @throws RuntimeException on fatal start failure * @throws RuntimeException on fatal start failure
* *
* {@hide} * {@hide}
*/ */
public static final int start(final String processClass, public static final ProcessStartResult start(final String processClass,
final String niceName, final String niceName,
int uid, int gid, int[] gids, int uid, int gid, int[] gids,
int debugFlags, int debugFlags,
@@ -294,8 +293,7 @@ public class Process {
} else { } else {
new Thread(runnable).start(); new Thread(runnable).start();
} }
return new ProcessStartResult();
return 0;
} }
} }
@@ -303,7 +301,7 @@ public class Process {
* Start a new process. Don't supply a custom nice name. * Start a new process. Don't supply a custom nice name.
* {@hide} * {@hide}
*/ */
public static final int start(String processClass, int uid, int gid, public static final ProcessStartResult start(String processClass, int uid, int gid,
int[] gids, int debugFlags, String[] zygoteArgs) { int[] gids, int debugFlags, String[] zygoteArgs) {
return start(processClass, "", uid, gid, gids, return start(processClass, "", uid, gid, gids,
debugFlags, zygoteArgs); debugFlags, zygoteArgs);
@@ -418,14 +416,11 @@ public class Process {
* and returns the child's pid. Please note: the present implementation * and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces. * replaces newlines in the argument list with spaces.
* @param args argument list * @param args argument list
* @return PID of new child process * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason * @throws ZygoteStartFailedEx if process start failed for any reason
*/ */
private static int zygoteSendArgsAndGetPid(ArrayList<String> args) private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx { throws ZygoteStartFailedEx {
int pid;
openZygoteSocketIfNeeded(); openZygoteSocketIfNeeded();
try { try {
@@ -436,7 +431,8 @@ public class Process {
* b) a number of newline-separated argument strings equal to count * b) a number of newline-separated argument strings equal to count
* *
* After the zygote process reads these it will write the pid of * After the zygote process reads these it will write the pid of
* the child or -1 on failure. * the child or -1 on failure, followed by boolean to
* indicate whether a wrapper process was used.
*/ */
sZygoteWriter.write(Integer.toString(args.size())); sZygoteWriter.write(Integer.toString(args.size()));
@@ -456,11 +452,13 @@ public class Process {
sZygoteWriter.flush(); sZygoteWriter.flush();
// Should there be a timeout on this? // Should there be a timeout on this?
pid = sZygoteInputStream.readInt(); ProcessStartResult result = new ProcessStartResult();
result.pid = sZygoteInputStream.readInt();
if (pid < 0) { if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed"); throw new ZygoteStartFailedEx("fork() failed");
} }
result.usingWrapper = sZygoteInputStream.readBoolean();
return result;
} catch (IOException ex) { } catch (IOException ex) {
try { try {
if (sZygoteSocket != null) { if (sZygoteSocket != null) {
@@ -475,8 +473,6 @@ public class Process {
throw new ZygoteStartFailedEx(ex); throw new ZygoteStartFailedEx(ex);
} }
return pid;
} }
/** /**
@@ -490,18 +486,16 @@ public class Process {
* new process should setgroup() to. * new process should setgroup() to.
* @param enableDebugger True if debugging should be enabled for this process. * @param enableDebugger True if debugging should be enabled for this process.
* @param extraArgs Additional arguments to supply to the zygote process. * @param extraArgs Additional arguments to supply to the zygote process.
* @return PID * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason * @throws ZygoteStartFailedEx if process start failed for any reason
*/ */
private static int startViaZygote(final String processClass, private static ProcessStartResult startViaZygote(final String processClass,
final String niceName, final String niceName,
final int uid, final int gid, final int uid, final int gid,
final int[] gids, final int[] gids,
int debugFlags, int debugFlags,
String[] extraArgs) String[] extraArgs)
throws ZygoteStartFailedEx { throws ZygoteStartFailedEx {
int pid;
synchronized(Process.class) { synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>(); ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -553,15 +547,9 @@ public class Process {
argsForZygote.add(arg); argsForZygote.add(arg);
} }
} }
pid = zygoteSendArgsAndGetPid(argsForZygote);
}
if (pid <= 0) { return zygoteSendArgsAndGetResult(argsForZygote);
throw new ZygoteStartFailedEx("zygote start failed:" + pid);
} }
return pid;
} }
/** /**
@@ -615,6 +603,20 @@ public class Process {
return (int) procStatusValues[0]; return (int) procStatusValues[0];
} }
/**
* 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. * Set the priority of a thread, based on Linux priorities.
* *
@@ -826,4 +828,21 @@ public class Process {
* @hide * @hide
*/ */
public static final native long getPss(int pid); public static final native long getPss(int pid);
/**
* Specifies the outcome of having started a process.
* @hide
*/
public static final class ProcessStartResult {
/**
* The PID of the newly started process.
* Always >= 0. (If the start failed, an exception will have been thrown instead.)
*/
public int pid;
/**
* True if the process was started with a wrapper attached.
*/
public boolean usingWrapper;
}
} }

View File

@@ -23,7 +23,6 @@ import android.os.Debug;
import android.os.IBinder; import android.os.IBinder;
import android.os.Process; import android.os.Process;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Config;
import android.util.Log; import android.util.Log;
import android.util.Slog; import android.util.Slog;
@@ -46,6 +45,7 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter;
*/ */
public class RuntimeInit { public class RuntimeInit {
private final static String TAG = "AndroidRuntime"; private final static String TAG = "AndroidRuntime";
private final static boolean DEBUG = false;
/** true if commonInit() has been called */ /** true if commonInit() has been called */
private static boolean initialized; private static boolean initialized;
@@ -90,14 +90,14 @@ public class RuntimeInit {
} }
private static final void commonInit() { private static final void commonInit() {
if (Config.LOGV) Slog.d(TAG, "Entered RuntimeInit!"); if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/* set default handler; this applies to all threads in the VM */ /* set default handler; this applies to all threads in the VM */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
int hasQwerty = getQwertyKeyboard(); int hasQwerty = getQwertyKeyboard();
if (Config.LOGV) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty); if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
if (hasQwerty == 1) { if (hasQwerty == 1) {
System.setProperty("qwerty", "1"); System.setProperty("qwerty", "1");
} }
@@ -184,11 +184,6 @@ public class RuntimeInit {
*/ */
private static void invokeStaticMain(String className, String[] argv) private static void invokeStaticMain(String className, String[] argv)
throws ZygoteInit.MethodAndArgsCaller { 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; Class<?> cl;
try { try {
@@ -226,6 +221,13 @@ public class RuntimeInit {
} }
public static final void main(String[] argv) { 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(); commonInit();
/* /*
@@ -234,7 +236,7 @@ public class RuntimeInit {
*/ */
finishInit(); finishInit();
if (Config.LOGV) Slog.d(TAG, "Leaving RuntimeInit!"); if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
} }
public static final native void finishInit(); public static final native void finishInit();
@@ -246,7 +248,6 @@ public class RuntimeInit {
* *
* Current recognized args: * Current recognized args:
* <ul> * <ul>
* <li> --nice-name=<i>nice name to appear in ps</i>
* <li> <code> [--] &lt;start class name&gt; &lt;args&gt; * <li> <code> [--] &lt;start class name&gt; &lt;args&gt;
* </ul> * </ul>
* *
@@ -254,43 +255,60 @@ public class RuntimeInit {
*/ */
public static final void zygoteInit(String[] argv) public static final void zygoteInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller { throws ZygoteInit.MethodAndArgsCaller {
// TODO: Doing this here works, but it seems kind of arbitrary. Find if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
// a better place. The goal is to set it up for applications, but not
// tools like am. redirectLogStreams();
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
commonInit(); commonInit();
zygoteInitNative(); zygoteInitNative();
int curArg = 0; applicationInit(argv);
for ( /* curArg */ ; curArg < argv.length; curArg++) { }
String arg = argv[curArg];
if (arg.equals("--")) { /**
curArg++; * The main function called when an application is started through a
break; * wrapper process.
} else if (!arg.startsWith("--")) { *
break; * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
} else if (arg.startsWith("--nice-name=")) { * which calls {@link WrapperInit#main} which then calls this method.
String niceName = arg.substring(arg.indexOf('=') + 1); * So we don't need to call commonInit() here.
Process.setArgV0(niceName); *
} * @param argv arg strings
} */
public static void wrapperInit(String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
if (curArg == argv.length) { applicationInit(argv);
Slog.e(TAG, "Missing classname argument to RuntimeInit!"); }
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 // let the process exit
return; return;
} }
// Remaining arguments are passed to the start class's static main // 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]; * Redirect System.out and System.err to the Android log.
*/
System.arraycopy(argv, curArg, startArgs, 0, startArgs.length); public static void redirectLogStreams() {
invokeStaticMain(startClass, startArgs); 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(); public static final native void zygoteInitNative();
@@ -353,4 +371,55 @@ public class RuntimeInit {
// Register handlers for DDM messages. // Register handlers for DDM messages.
android.ddm.DdmRegister.registerHandlers(); android.ddm.DdmRegister.registerHandlers();
} }
/**
* Handles argument parsing for args related to the runtime.
*
* Current recognized args:
* <ul>
* <li> <code> [--] &lt;start class name&gt; &lt;args&gt;
* </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);
}
}
} }

View File

@@ -0,0 +1,120 @@
/*
* 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 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 {
// Tell the Zygote what our actual PID is (since it only knows about the
// wrapper that it directly forked).
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();
ZygoteInit.closeDescriptorQuietly(fd);
} catch (IOException ex) {
Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
}
}
// Mimic Zygote preloading.
ZygoteInit.preload();
// Launch the application.
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 ? ZygoteInit.getFDFromFileDescriptor(pipeFd) : 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());
}
}

View File

@@ -26,8 +26,10 @@ import dalvik.system.PathClassLoader;
import dalvik.system.Zygote; import dalvik.system.Zygote;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
@@ -193,15 +195,20 @@ class ZygoteConnection {
new FileOutputStream(descriptors[2])); new FileOutputStream(descriptors[2]));
} }
int pid; int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try { try {
parsedArgs = new Arguments(args); parsedArgs = new Arguments(args);
applyUidSecurityPolicy(parsedArgs, peer); applyUidSecurityPolicy(parsedArgs, peer);
applyDebuggerSecurityPolicy(parsedArgs);
applyRlimitSecurityPolicy(parsedArgs, peer); applyRlimitSecurityPolicy(parsedArgs, peer);
applyCapabilitiesSecurityPolicy(parsedArgs, peer); applyCapabilitiesSecurityPolicy(parsedArgs, peer);
applyInvokeWithSecurityPolicy(parsedArgs, peer);
applyDebuggerSystemProperty(parsedArgs);
applyInvokeWithSystemProperty(parsedArgs);
int[][] rlimits = null; int[][] rlimits = null;
@@ -209,25 +216,44 @@ class ZygoteConnection {
rlimits = parsedArgs.rlimits.toArray(intArray2d); rlimits = parsedArgs.rlimits.toArray(intArray2d);
} }
if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
FileDescriptor[] pipeFds = new FileDescriptor[2];
ZygoteInit.pipe(pipeFds);
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
ZygoteInit.setCloseOnExec(serverPipeFd, true);
}
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, parsedArgs.debugFlags, rlimits); parsedArgs.gids, parsedArgs.debugFlags, rlimits);
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
logAndPrintError (newStderr, "Invalid zygote arguments", ex); logAndPrintError(newStderr, "Invalid zygote arguments", ex);
pid = -1;
} catch (ZygoteSecurityException ex) { } catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr, logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex); "Zygote security policy prevents request: ", ex);
pid = -1;
} }
if (pid == 0) { try {
// in child if (pid == 0) {
handleChildProc(parsedArgs, descriptors, newStderr); // in child
// should never happen ZygoteInit.closeDescriptorQuietly(serverPipeFd);
return true; serverPipeFd = null;
} else { /* pid != 0 */ handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// in parent...pid of < 0 means failure
return handleParentProc(pid, descriptors, parsedArgs); // 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
ZygoteInit.closeDescriptorQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
ZygoteInit.closeDescriptorQuietly(childPipeFd);
ZygoteInit.closeDescriptorQuietly(serverPipeFd);
} }
} }
@@ -244,8 +270,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: * Current recognized args:
* <ul> * <ul>
* <li> --setuid=<i>uid of child process, defaults to 0</i> * <li> --setuid=<i>uid of child process, defaults to 0</i>
@@ -274,6 +300,7 @@ class ZygoteConnection {
* be handed off to com.android.internal.os.RuntimeInit, rather than * be handed off to com.android.internal.os.RuntimeInit, rather than
* processed directly * processed directly
* Android runtime startup (eg, Binder initialization) is also eschewed. * 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: * <li> If <code>--runtime-init</code> is present:
* [--] &lt;args for RuntimeInit &gt; * [--] &lt;args for RuntimeInit &gt;
* <li> If <code>--runtime-init</code> is absent: * <li> If <code>--runtime-init</code> is absent:
@@ -307,6 +334,9 @@ class ZygoteConnection {
/** from --runtime-init */ /** from --runtime-init */
boolean runtimeInit; boolean runtimeInit;
/** from --nice-name */
String niceName;
/** from --capabilities */ /** from --capabilities */
boolean capabilitiesSpecified; boolean capabilitiesSpecified;
long permittedCapabilities; long permittedCapabilities;
@@ -315,6 +345,9 @@ class ZygoteConnection {
/** from all --rlimit=r,c,m */ /** from all --rlimit=r,c,m */
ArrayList<int[]> rlimits; ArrayList<int[]> rlimits;
/** from --invoke-with */
String invokeWith;
/** /**
* Any args after and including the first non-option arg * Any args after and including the first non-option arg
* (or after a '--') * (or after a '--')
@@ -438,6 +471,23 @@ class ZygoteConnection {
for (int i = params.length - 1; i >= 0 ; i--) { for (int i = params.length - 1; i >= 0 ; i--) {
gids[i] = Integer.parseInt(params[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 { } else {
break; break;
} }
@@ -567,14 +617,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, * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
* the debugger state is specified via the "--enable-debugger" flag * the debugger state is specified via the "--enable-debugger" flag
* in the spawn request. * in the spawn request.
* *
* @param args non-null; zygote spawner args * @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"))) { if ("1".equals(SystemProperties.get("ro.debuggable"))) {
args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
} }
@@ -663,6 +714,49 @@ 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, * Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
@@ -670,6 +764,7 @@ class ZygoteConnection {
* *
* @param parsedArgs non-null; zygote args * @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available. * @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 * @param newStderr null-ok; stream to use for stderr until stdio
* is reopened. * is reopened.
* *
@@ -677,7 +772,7 @@ class ZygoteConnection {
* trampoline to code that invokes static main. * trampoline to code that invokes static main.
*/ */
private void handleChildProc(Arguments parsedArgs, private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, PrintStream newStderr) FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller { throws ZygoteInit.MethodAndArgsCaller {
/* /*
@@ -704,7 +799,7 @@ class ZygoteConnection {
descriptors[1], descriptors[2]); descriptors[1], descriptors[2]);
for (FileDescriptor fd: descriptors) { for (FileDescriptor fd: descriptors) {
ZygoteInit.closeDescriptor(fd); ZygoteInit.closeDescriptorQuietly(fd);
} }
newStderr = System.err; newStderr = System.err;
} catch (IOException ex) { } catch (IOException ex) {
@@ -712,37 +807,48 @@ class ZygoteConnection {
} }
} }
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.runtimeInit) { if (parsedArgs.runtimeInit) {
RuntimeInit.zygoteInit(parsedArgs.remainingArgs); if (parsedArgs.invokeWith != null) {
} else { WrapperInit.execApplication(parsedArgs.invokeWith,
ClassLoader cloader; parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
if (parsedArgs.classpath != null) {
cloader
= new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else { } else {
cloader = ClassLoader.getSystemClassLoader(); RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
} }
} else {
String className; String className;
try { try {
className = parsedArgs.remainingArgs[0]; className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) { } catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError (newStderr, logAndPrintError(newStderr,
"Missing required class name argument", null); "Missing required class name argument", null);
return; return;
} }
String[] mainArgs
= new String[parsedArgs.remainingArgs.length - 1];
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1, System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length); mainArgs, 0, mainArgs.length);
try { if (parsedArgs.invokeWith != null) {
ZygoteInit.invokeStaticMain(cloader, className, mainArgs); WrapperInit.execStandalone(parsedArgs.invokeWith,
} catch (RuntimeException ex) { parsedArgs.classpath, className, mainArgs);
logAndPrintError (newStderr, "Error starting. ", ex); } 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,40 +860,61 @@ class ZygoteConnection {
* if &lt; 0; * if &lt; 0;
* @param descriptors null-ok; file descriptors for child's new stdio if * @param descriptors null-ok; file descriptors for child's new stdio if
* specified. * specified.
* @param pipeFd null-ok; pipe for communication with child.
* @param parsedArgs non-null; zygote args * @param parsedArgs non-null; zygote args
* @return true for "exit command loop" and false for "continue command * @return true for "exit command loop" and false for "continue command
* loop" * loop"
*/ */
private boolean handleParentProc(int pid, private boolean handleParentProc(int pid,
FileDescriptor[] descriptors, Arguments parsedArgs) { FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
if(pid > 0) { if (pid > 0) {
// Try to move the new child into the peer's process group. setChildPgid(pid);
try { }
ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
} catch (IOException ex) { if (descriptors != null) {
// This exception is expected in the case where for (FileDescriptor fd: descriptors) {
// the peer is not in our session ZygoteInit.closeDescriptorQuietly(fd);
// 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");
} }
} }
try { boolean usingWrapper = false;
if (descriptors != null) { if (pipeFd != null && pid > 0) {
for (FileDescriptor fd: descriptors) { DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
ZygoteInit.closeDescriptor(fd); int innerPid = -1;
try {
innerPid = is.readInt();
} catch (IOException ex) {
Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
} finally {
try {
is.close();
} catch (IOException ex) {
}
}
// 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;
usingWrapper = true;
} 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 { try {
mSocketOutStream.writeInt(pid); mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) { } catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex); Log.e(TAG, "Error reading from command socket", ex);
return true; return true;
@@ -808,6 +935,20 @@ class ZygoteConnection {
return false; 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 * Logs an error message and prints it to the specified stream, if
* provided * provided

View File

@@ -22,6 +22,8 @@ import android.content.res.TypedArray;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.LocalServerSocket; import android.net.LocalServerSocket;
import android.os.Debug; import android.os.Debug;
import android.os.FileUtils;
import android.os.Process;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.util.Config; import android.util.Config;
@@ -67,7 +69,7 @@ public class ZygoteInit {
private static final int PRELOAD_GC_THRESHOLD = 50000; private static final int PRELOAD_GC_THRESHOLD = 50000;
public static final String USAGE_STRING = public static final String USAGE_STRING =
" <\"true\"|\"false\" for startSystemServer>"; " <\"start-system-server\"|\"\" for startSystemServer>";
private static LocalServerSocket sServerSocket; private static LocalServerSocket sServerSocket;
@@ -245,6 +247,11 @@ public class ZygoteInit {
} }
} }
static void preload() {
preloadClasses();
preloadResources();
}
/** /**
* Performs Zygote process initialization. Loads and initializes * Performs Zygote process initialization. Loads and initializes
* commonly used classes. * commonly used classes.
@@ -494,11 +501,20 @@ public class ZygoteInit {
closeServerSocket(); closeServerSocket();
/* if (parsedArgs.niceName != null) {
* Pass the remaining arguments to SystemServer. Process.setArgV0(parsedArgs.niceName);
* "--nice-name=system_server com.android.server.SystemServer" }
*/
RuntimeInit.zygoteInit(parsedArgs.remainingArgs); 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 */ /* should never reach here */
} }
@@ -523,20 +539,13 @@ public class ZygoteInit {
try { try {
parsedArgs = new ZygoteConnection.Arguments(args); parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
/* ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
* 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;
/* Request to fork the system server process */ /* Request to fork the system server process */
pid = Zygote.forkSystemServer( pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid, parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null, parsedArgs.gids, parsedArgs.debugFlags, null,
parsedArgs.permittedCapabilities, parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities); parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
@@ -561,9 +570,7 @@ public class ZygoteInit {
registerZygoteSocket(); registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis()); SystemClock.uptimeMillis());
preloadClasses(); preload();
//cacheRegisterMaps();
preloadResources();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis()); SystemClock.uptimeMillis());
@@ -578,9 +585,9 @@ public class ZygoteInit {
throw new RuntimeException(argv[0] + USAGE_STRING); throw new RuntimeException(argv[0] + USAGE_STRING);
} }
if (argv[1].equals("true")) { if (argv[1].equals("start-system-server")) {
startSystemServer(); startSystemServer();
} else if (!argv[1].equals("false")) { } else if (!argv[1].equals("")) {
throw new RuntimeException(argv[0] + USAGE_STRING); throw new RuntimeException(argv[0] + USAGE_STRING);
} }
@@ -760,6 +767,13 @@ public class ZygoteInit {
static native void closeDescriptor(FileDescriptor fd) static native void closeDescriptor(FileDescriptor fd)
throws IOException; throws IOException;
static void closeDescriptorQuietly(FileDescriptor fd) {
try {
closeDescriptor(fd);
} catch (IOException e) {
}
}
/** /**
* Toggles the close-on-exec flag for the specified file descriptor. * Toggles the close-on-exec flag for the specified file descriptor.
* *
@@ -810,6 +824,21 @@ public class ZygoteInit {
static native FileDescriptor createFileDescriptor(int fd) static native FileDescriptor createFileDescriptor(int fd)
throws IOException; throws IOException;
/**
* Gets the integer value of a file descriptor.
*
* @param fd non-null; file descriptor
* @return integer OS file descriptor
*/
static native int getFDFromFileDescriptor(FileDescriptor fd);
/**
* Creates a pipe.
*
* @param outFds non-null; an array to hold two file descriptors, initially null
*/
static native void pipe(FileDescriptor[] outFds) throws IOException;
/** /**
* Class not instantiable. * Class not instantiable.
*/ */

View File

@@ -879,8 +879,11 @@ bail:
* Start the Android runtime. This involves starting the virtual machine * Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class * and calling the "static void main(String[] args)" method in the class
* named by "className". * 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", LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
className != NULL ? className : "(unknown)"); className != NULL ? className : "(unknown)");
@@ -895,7 +898,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
* 'startSystemServer == true' means runtime is obslete and not run from * 'startSystemServer == true' means runtime is obslete and not run from
* init.rc anymore, so we print out the boot start event here. * 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 */ /* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000; const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
@@ -929,13 +932,13 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
/* /*
* We want to call main() with a String array with arguments in it. * 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 * At present we have two arguments, the class name and an option string.
* array to hold it. * Create an array to hold them.
*/ */
jclass stringClass; jclass stringClass;
jobjectArray strArray; jobjectArray strArray;
jstring classNameStr; jstring classNameStr;
jstring startSystemServerStr; jstring optionsStr;
stringClass = env->FindClass("java/lang/String"); stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL); assert(stringClass != NULL);
@@ -944,9 +947,8 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
classNameStr = env->NewStringUTF(className); classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL); assert(classNameStr != NULL);
env->SetObjectArrayElement(strArray, 0, classNameStr); env->SetObjectArrayElement(strArray, 0, classNameStr);
startSystemServerStr = env->NewStringUTF(startSystemServer ? optionsStr = env->NewStringUTF(options);
"true" : "false"); env->SetObjectArrayElement(strArray, 1, optionsStr);
env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
/* /*
* Start VM. This thread becomes the main thread of the VM, and will * Start VM. This thread becomes the main thread of the VM, and will
@@ -990,12 +992,6 @@ bail:
free(slashClassName); free(slashClassName);
} }
void AndroidRuntime::start()
{
start("com.android.internal.os.RuntimeInit",
false /* Don't start the system server */);
}
void AndroidRuntime::onExit(int code) void AndroidRuntime::onExit(int code)
{ {
LOGV("AndroidRuntime onExit calling exit(%d)", code); LOGV("AndroidRuntime onExit calling exit(%d)", code);

View File

@@ -145,6 +145,10 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env, static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
jobject clazz, jobject descriptor) jobject clazz, jobject descriptor)
{ {
if (!descriptor) {
return;
}
int fd; int fd;
int err; int err;
@@ -327,6 +331,30 @@ static jobject com_android_internal_os_ZygoteInit_createFileDescriptor (
return jniCreateFileDescriptor(env, fd); return jniCreateFileDescriptor(env, fd);
} }
static jint com_android_internal_os_ZygoteInit_getFDFromFileDescriptor (
JNIEnv *env, jobject clazz, jobject fd)
{
return jniGetFDFromFileDescriptor(env, fd);
}
static void com_android_internal_os_ZygoteInit_pipe (
JNIEnv *env, jobject clazz, jobjectArray outFds)
{
int fds[2];
if (pipe(fds) < 0) {
jniThrowIOException(env, errno);
return;
}
for (int i = 0; i < 2; i++) {
jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
if (env->ExceptionCheck()) {
return;
}
env->SetObjectArrayElement(outFds, i, fdObj);
}
}
/* /*
* JNI registration. * JNI registration.
*/ */
@@ -355,7 +383,11 @@ static JNINativeMethod gMethods[] = {
{ "selectReadable", "([Ljava/io/FileDescriptor;)I", { "selectReadable", "([Ljava/io/FileDescriptor;)I",
(void *) com_android_internal_os_ZygoteInit_selectReadable }, (void *) com_android_internal_os_ZygoteInit_selectReadable },
{ "createFileDescriptor", "(I)Ljava/io/FileDescriptor;", { "createFileDescriptor", "(I)Ljava/io/FileDescriptor;",
(void *) com_android_internal_os_ZygoteInit_createFileDescriptor } (void *) com_android_internal_os_ZygoteInit_createFileDescriptor },
{ "getFDFromFileDescriptor", "(Ljava/io/FileDescriptor;)I",
(void *) com_android_internal_os_ZygoteInit_getFDFromFileDescriptor },
{ "pipe", "([Ljava/io/FileDescriptor;)V",
(void *) com_android_internal_os_ZygoteInit_pipe },
}; };
int register_com_android_internal_os_ZygoteInit(JNIEnv* env) int register_com_android_internal_os_ZygoteInit(JNIEnv* env)
{ {

View File

@@ -37,6 +37,13 @@ public:
AndroidRuntime(); AndroidRuntime();
virtual ~AndroidRuntime(); virtual ~AndroidRuntime();
enum StartMode {
Zygote,
SystemServer,
Application,
Tool,
};
/** /**
* Register a set of methods in the specified class. * Register a set of methods in the specified class.
*/ */
@@ -61,8 +68,7 @@ public:
int addVmArguments(int argc, const char* const argv[]); int addVmArguments(int argc, const char* const argv[]);
void start(const char *classname, const bool startSystemServer); void start(const char *classname, const char* options);
void start(); // start in android.util.RuntimeInit
static AndroidRuntime* getRuntime(); static AndroidRuntime* getRuntime();

View File

@@ -203,6 +203,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// before we decide it's never going to come up for real. // before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000; static final int PROC_START_TIMEOUT = 10*1000;
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
// could take much longer than usual.
static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
// How long to wait after going idle before forcing apps to GC. // How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000; static final int GC_TIMEOUT = 5*1000;
@@ -841,14 +847,6 @@ public final class ActivityManagerService extends ActivityManagerNative
* Current sequence id for process LRU updating. * Current sequence id for process LRU updating.
*/ */
int mLruSeq = 0; 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 * System monitoring: number of processes that died since the last
@@ -1395,15 +1393,6 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
private ActivityManagerService() { 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()); Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
File dataDir = Environment.getDataDirectory(); File dataDir = Environment.getDataDirectory();
@@ -1872,9 +1861,11 @@ public final class ActivityManagerService extends ActivityManagerNative
if ("1".equals(SystemProperties.get("debug.assert"))) { if ("1".equals(SystemProperties.get("debug.assert"))) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT; debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
} }
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid, // Start the process. It will either succeed and return a result containing
gids, debugFlags, null); // the PID of the new process, or else throw a RuntimeException.
Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags, null);
BatteryStatsImpl bs = app.batteryStats.getBatteryStats(); BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) { synchronized (bs) {
if (bs.isOnBattery()) { if (bs.isOnBattery()) {
@@ -1882,12 +1873,12 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
} }
EventLog.writeEvent(EventLogTags.AM_PROC_START, pid, uid, EventLog.writeEvent(EventLogTags.AM_PROC_START, startResult.pid, uid,
app.processName, hostingType, app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : ""); hostingNameStr != null ? hostingNameStr : "");
if (app.persistent) { if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, pid); Watchdog.getInstance().processStarted(app.processName, startResult.pid);
} }
StringBuilder buf = mStringBuilder; StringBuilder buf = mStringBuilder;
@@ -1901,7 +1892,7 @@ public final class ActivityManagerService extends ActivityManagerNative
buf.append(hostingNameStr); buf.append(hostingNameStr);
} }
buf.append(": pid="); buf.append(": pid=");
buf.append(pid); buf.append(startResult.pid);
buf.append(" uid="); buf.append(" uid=");
buf.append(uid); buf.append(uid);
buf.append(" gids={"); buf.append(" gids={");
@@ -1914,26 +1905,22 @@ public final class ActivityManagerService extends ActivityManagerNative
} }
buf.append("}"); buf.append("}");
Slog.i(TAG, buf.toString()); Slog.i(TAG, buf.toString());
if (pid == 0 || pid == MY_PID) { if (startResult.pid == 0 || startResult.pid == MY_PID) {
// Processes are being emulated with threads. // Processes are being emulated with threads.
app.pid = MY_PID; app.pid = MY_PID;
app.removed = false; app.removed = false;
mStartingProcesses.add(app); mStartingProcesses.add(app);
} else if (pid > 0) { } else {
app.pid = pid; app.pid = startResult.pid;
app.usingWrapper = startResult.usingWrapper;
app.removed = false; app.removed = false;
synchronized (mPidsSelfLocked) { synchronized (mPidsSelfLocked) {
this.mPidsSelfLocked.put(pid, app); this.mPidsSelfLocked.put(startResult.pid, app);
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
msg.obj = app; msg.obj = app;
mHandler.sendMessageDelayed(msg, PROC_START_TIMEOUT); mHandler.sendMessageDelayed(msg, startResult.usingWrapper
? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
} }
} else {
app.pid = 0;
RuntimeException e = new RuntimeException(
"Failure starting process " + app.processName
+ ": returned pid=" + pid);
Slog.e(TAG, e.getMessage(), e);
} }
} catch (RuntimeException e) { } catch (RuntimeException e) {
// XXX do better error recovery. // XXX do better error recovery.

View File

@@ -554,12 +554,12 @@ class ActivityRecord extends IApplicationToken.Stub {
public long getKeyDispatchingTimeout() { public long getKeyDispatchingTimeout() {
synchronized(service) { synchronized(service) {
ActivityRecord r = getWaitingHistoryRecordLocked(); ActivityRecord r = getWaitingHistoryRecordLocked();
if (r == null || r.app == null if (r != null && r.app != null
|| r.app.instrumentationClass == null) { && (r.app.instrumentationClass != null || r.app.usingWrapper)) {
return ActivityManagerService.KEY_DISPATCHING_TIMEOUT; return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
} }
return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT; return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
} }
} }

View File

@@ -74,6 +74,7 @@ class ProcessRecord {
IInstrumentationWatcher instrumentationWatcher; // who is waiting IInstrumentationWatcher instrumentationWatcher; // who is waiting
Bundle instrumentationArguments;// as given to us Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass ComponentName instrumentationResultClass;// copy of instrumentationClass
boolean usingWrapper; // Set to true when process was launched with a wrapper attached
BroadcastRecord curReceiver;// receiver currently running in the app BroadcastRecord curReceiver;// receiver currently running in the app
long lastWakeTime; // How long proc held wake lock at last check long lastWakeTime; // How long proc held wake lock at last check
long lastCpuTime; // How long proc has run CPU at last check long lastCpuTime; // How long proc has run CPU at last check