Support wrapping app processes to inject debug instrumentation.

Bug: 4437846

Change-Id: I4552501c693716b14714afb5c5248edaca9547ab
This commit is contained in:
Jeff Brown
2011-05-16 17:08:42 -07:00
parent cbc38e74f0
commit ebed7d6e35
11 changed files with 514 additions and 208 deletions

View File

@@ -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++];
// 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 {
className = arg;
break;
}
}
// 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);
} else {
set_process_name(argv0);
if (niceName && *niceName) {
setArgv0(argv0, niceName);
set_process_name(niceName);
}
runtime.mClassName = arg;
runtime.mParentDir = parentDir;
// Remainder of args get passed to startup class main()
runtime.mArgC = argc-i;
runtime.mArgV = argv+i;
LOGV("App process is starting with pid=%d, class=%s.\n",
getpid(), runtime.getClassName());
runtime.start();
}
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;
}
}

View File

@@ -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
}

View File

@@ -633,6 +633,20 @@ public class Process {
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.
*

View File

@@ -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> [--] &lt;start class name&gt; &lt;args&gt;
* </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];
applicationInit(argv);
}
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);
}
}
/**
* 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");
if (curArg == argv.length) {
Slog.e(TAG, "Missing classname argument to RuntimeInit!");
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> [--] &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,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());
}
}

View File

@@ -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:
* [--] &lt;args for RuntimeInit &gt;
* <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;
}
@@ -663,6 +719,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,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
@@ -670,6 +769,7 @@ class ZygoteConnection {
*
* @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.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.runtimeInit) {
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
} else {
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader
= new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
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 &lt; 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) {
// 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");
if (pid > 0) {
setChildPgid(pid);
}
if (descriptors != null) {
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
}
try {
if (descriptors != null) {
for (FileDescriptor fd: descriptors) {
ZygoteInit.closeDescriptor(fd);
if (pipeFd != null && pid > 0) {
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
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;
} 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

View File

@@ -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);
}
@@ -695,15 +698,6 @@ public class ZygoteInit {
static native void reopenStdio(FileDescriptor in,
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.
*

View File

@@ -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);

View File

@@ -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",

View File

@@ -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();

View File

@@ -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()) {