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:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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> [--] <start class name> <args>
|
* <li> <code> [--] <start class name> <args>
|
||||||
* </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> [--] <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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
120
core/java/com/android/internal/os/WrapperInit.java
Normal file
120
core/java/com/android/internal/os/WrapperInit.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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:
|
||||||
* [--] <args for RuntimeInit >
|
* [--] <args for RuntimeInit >
|
||||||
* <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 < 0;
|
* if < 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
|
||||||
|
|||||||
@@ -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.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user