summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Brown <jeffbrown@google.com>2011-05-16 17:08:42 -0700
committerJeff Brown <jeffbrown@android.com>2011-07-28 14:00:27 -0700
commitd5d7e164d316e595a64faf1555839d1939da0863 (patch)
treea91f61091b3f1025cd58cab417167b83dcdcbcec
parent4d1fa60bfe660fd0fc6671116da597c0027cbb16 (diff)
downloadframeworks_base-d5d7e164d316e595a64faf1555839d1939da0863.zip
frameworks_base-d5d7e164d316e595a64faf1555839d1939da0863.tar.gz
frameworks_base-d5d7e164d316e595a64faf1555839d1939da0863.tar.bz2
Support wrapping app processes to inject debug instrumentation.
Bug: 4437846 Change-Id: Ib0559e5224b0fa0df074e485787307b6634e8654
-rw-r--r--cmds/app_process/app_main.cpp71
-rw-r--r--cmds/runtime/main_runtime.cpp4
-rw-r--r--core/java/android/os/Process.java75
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java141
-rw-r--r--core/java/com/android/internal/os/WrapperInit.java120
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java249
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java71
-rw-r--r--core/jni/AndroidRuntime.cpp24
-rw-r--r--core/jni/com_android_internal_os_ZygoteInit.cpp34
-rw-r--r--include/android_runtime/AndroidRuntime.h10
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java55
-rw-r--r--services/java/com/android/server/am/ActivityRecord.java10
-rw-r--r--services/java/com/android/server/am/ProcessRecord.java1
13 files changed, 638 insertions, 227 deletions
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 7decf9a..48f25a5 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -128,10 +128,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]
@@ -142,39 +139,53 @@ int main(int argc, const char* const argv[])
int i = runtime.addVmArguments(argc, argv);
- // Next arg is parent directory
- if (i < argc) {
- runtime.mParentDir = argv[i++];
- }
-
- // Next arg is startup classname or "--zygote"
- if (i < argc) {
- arg = argv[i++];
- if (0 == strcmp("--zygote", arg)) {
- bool startSystemServer = (i < argc) ?
- strcmp(argv[i], "--start-system-server") == 0 : false;
- setArgv0(argv0, "zygote");
- set_process_name("zygote");
- runtime.start("com.android.internal.os.ZygoteInit",
- startSystemServer);
+ // Parse runtime arguments. Stop at first unrecognized option.
+ bool zygote = false;
+ bool startSystemServer = false;
+ bool application = false;
+ const char* parentDir = NULL;
+ const char* niceName = NULL;
+ const char* className = NULL;
+ while (i < argc) {
+ const char* arg = argv[i++];
+ if (!parentDir) {
+ parentDir = arg;
+ } else if (strcmp(arg, "--zygote") == 0) {
+ zygote = true;
+ niceName = "zygote";
+ } else if (strcmp(arg, "--start-system-server") == 0) {
+ startSystemServer = true;
+ } else if (strcmp(arg, "--application") == 0) {
+ application = true;
+ } else if (strncmp(arg, "--nice-name=", 12) == 0) {
+ niceName = arg + 12;
} else {
- set_process_name(argv0);
-
- runtime.mClassName = arg;
+ className = arg;
+ break;
+ }
+ }
- // Remainder of args get passed to startup class main()
- runtime.mArgC = argc-i;
- runtime.mArgV = argv+i;
+ if (niceName && *niceName) {
+ setArgv0(argv0, niceName);
+ set_process_name(niceName);
+ }
- LOGV("App process is starting with pid=%d, class=%s.\n",
- getpid(), runtime.getClassName());
- runtime.start();
- }
+ runtime.mParentDir = parentDir;
+
+ if (zygote) {
+ runtime.start("com.android.internal.os.ZygoteInit",
+ startSystemServer ? "start-system-server" : "");
+ } else if (className) {
+ // Remainder of args get passed to startup class main()
+ runtime.mClassName = className;
+ runtime.mArgC = argc - i;
+ runtime.mArgV = argv + i;
+ runtime.start("com.android.internal.os.RuntimeInit",
+ application ? "application" : "tool");
} else {
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
return 10;
}
-
}
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 83cb533..76155fd 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -497,8 +497,8 @@ int main(int argc, char* const argv[])
} else {
#ifndef HAVE_ANDROID_OS
QuickRuntime* runt = new QuickRuntime();
- runt->start("com/android/server/SystemServer",
- false /* spontaneously fork system server from zygote */);
+ runt->start("com/android/server/SystemServer",
+ "" /* spontaneously fork system server from zygote */);
#endif
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index e1fdfb6..e128974 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -257,13 +257,12 @@ public class Process {
* @param enableDebugger True if debugging should be enabled for this 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
- * being emulated by a thread
+ * @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
- public static final int start(final String processClass,
+ public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags,
@@ -294,8 +293,7 @@ public class Process {
} else {
new Thread(runnable).start();
}
-
- return 0;
+ return new ProcessStartResult();
}
}
@@ -303,7 +301,7 @@ public class Process {
* Start a new process. Don't supply a custom nice name.
* {@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) {
return start(processClass, "", uid, gid, gids,
debugFlags, zygoteArgs);
@@ -418,14 +416,11 @@ public class Process {
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
* @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
*/
- private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
+ private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx {
-
- int pid;
-
openZygoteSocketIfNeeded();
try {
@@ -436,7 +431,8 @@ public class Process {
* b) a number of newline-separated argument strings equal to count
*
* 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()));
@@ -456,11 +452,13 @@ public class Process {
sZygoteWriter.flush();
// Should there be a timeout on this?
- pid = sZygoteInputStream.readInt();
-
- if (pid < 0) {
+ ProcessStartResult result = new ProcessStartResult();
+ result.pid = sZygoteInputStream.readInt();
+ if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
+ result.usingWrapper = sZygoteInputStream.readBoolean();
+ return result;
} catch (IOException ex) {
try {
if (sZygoteSocket != null) {
@@ -475,8 +473,6 @@ public class Process {
throw new ZygoteStartFailedEx(ex);
}
-
- return pid;
}
/**
@@ -490,18 +486,16 @@ public class Process {
* new process should setgroup() to.
* @param enableDebugger True if debugging should be enabled for this 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
*/
- private static int startViaZygote(final String processClass,
+ private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags,
String[] extraArgs)
throws ZygoteStartFailedEx {
- int pid;
-
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -553,15 +547,9 @@ public class Process {
argsForZygote.add(arg);
}
}
-
- pid = zygoteSendArgsAndGetPid(argsForZygote);
- }
- if (pid <= 0) {
- throw new ZygoteStartFailedEx("zygote start failed:" + pid);
+ return zygoteSendArgsAndGetResult(argsForZygote);
}
-
- return pid;
}
/**
@@ -616,6 +604,20 @@ public class Process {
}
/**
+ * Returns the parent process id for a currently running process.
+ * @param pid the process id
+ * @return the parent process id of the process, or -1 if the process is not running.
+ * @hide
+ */
+ public static final int getParentPid(int pid) {
+ String[] procStatusLabels = { "PPid:" };
+ long[] procStatusValues = new long[1];
+ procStatusValues[0] = -1;
+ Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+ return (int) procStatusValues[0];
+ }
+
+ /**
* Set the priority of a thread, based on Linux priorities.
*
* @param tid The identifier of the thread/process to change.
@@ -826,4 +828,21 @@ public class Process {
* @hide
*/
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;
+ }
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 59600dc..2de90ba 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -23,7 +23,6 @@ import android.os.Debug;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemProperties;
-import android.util.Config;
import android.util.Log;
import android.util.Slog;
@@ -46,6 +45,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;
@@ -90,14 +90,14 @@ public class RuntimeInit {
}
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 */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
int hasQwerty = getQwertyKeyboard();
- if (Config.LOGV) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
+ if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
if (hasQwerty == 1) {
System.setProperty("qwerty", "1");
}
@@ -184,11 +184,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 {
@@ -226,6 +221,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();
/*
@@ -234,7 +236,7 @@ public class RuntimeInit {
*/
finishInit();
- if (Config.LOGV) Slog.d(TAG, "Leaving RuntimeInit!");
+ if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
public static final native void finishInit();
@@ -246,7 +248,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>
*
@@ -254,43 +255,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.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
- System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
+ if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
+
+ redirectLogStreams();
commonInit();
zygoteInitNative();
- int curArg = 0;
- for ( /* curArg */ ; curArg < argv.length; curArg++) {
- String arg = argv[curArg];
-
- if (arg.equals("--")) {
- curArg++;
- break;
- } else if (!arg.startsWith("--")) {
- break;
- } else if (arg.startsWith("--nice-name=")) {
- String niceName = arg.substring(arg.indexOf('=') + 1);
- Process.setArgV0(niceName);
- }
- }
+ applicationInit(argv);
+ }
+
+ /**
+ * The main function called when an application is started through a
+ * wrapper process.
+ *
+ * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
+ * which calls {@link WrapperInit#main} which then calls this method.
+ * So we don't need to call commonInit() here.
+ *
+ * @param argv arg strings
+ */
+ public static void wrapperInit(String[] argv)
+ throws ZygoteInit.MethodAndArgsCaller {
+ if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
+
+ applicationInit(argv);
+ }
- if (curArg == argv.length) {
- 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
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();
@@ -353,4 +371,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);
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
new file mode 100644
index 0000000..8bbcf7a
--- /dev/null
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -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());
+ }
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index c473fd2..f5321b5 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -26,8 +26,10 @@ 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;
@@ -193,15 +195,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 +216,44 @@ class ZygoteConnection {
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,
parsedArgs.gids, parsedArgs.debugFlags, rlimits);
+ } catch (IOException 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
+ ZygoteInit.closeDescriptorQuietly(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
+ 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:
* <ul>
* <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
* 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 +334,9 @@ class ZygoteConnection {
/** from --runtime-init */
boolean runtimeInit;
+ /** from --nice-name */
+ String niceName;
+
/** from --capabilities */
boolean capabilitiesSpecified;
long permittedCapabilities;
@@ -315,6 +345,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 +471,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 +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,
* the debugger state is specified via the "--enable-debugger" flag
* in the spawn request.
*
* @param args non-null; zygote spawner args
*/
- private static void applyDebuggerSecurityPolicy(Arguments args) {
+ public static void applyDebuggerSystemProperty(Arguments args) {
if ("1".equals(SystemProperties.get("ro.debuggable"))) {
args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
}
@@ -664,12 +715,56 @@ class ZygoteConnection {
}
/**
+ * Applies zygote security policy.
+ * Based on the credentials of the process issuing a zygote command:
+ * <ol>
+ * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
+ * wrapper command.
+ * <li> Any other uid may not specify any invoke-with argument.
+ * </ul>
+ *
+ * @param args non-null; zygote spawner arguments
+ * @param peer non-null; peer credentials
+ * @throws ZygoteSecurityException
+ */
+ private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
+ throws ZygoteSecurityException {
+ int peerUid = peer.getUid();
+
+ if (args.invokeWith != null && peerUid != 0) {
+ throw new ZygoteSecurityException("Peer is not permitted to specify "
+ + "an explicit invoke-with wrapper command");
+ }
+ }
+
+ /**
+ * Applies invoke-with system properties to the zygote arguments.
+ *
+ * @param parsedArgs non-null; zygote args
+ */
+ public static void applyInvokeWithSystemProperty(Arguments args) {
+ if (args.invokeWith == null && args.niceName != null) {
+ if (args.niceName != null) {
+ String property = "wrap." + args.niceName;
+ if (property.length() > 31) {
+ property = property.substring(0, 31);
+ }
+ args.invokeWith = SystemProperties.get(property);
+ if (args.invokeWith != null && args.invokeWith.length() == 0) {
+ args.invokeWith = null;
+ }
+ }
+ }
+ }
+
+ /**
* Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
* if successful or returning if failed.
*
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
+ * @param pipeFd null-ok; pipe for communication back to Zygote.
* @param newStderr null-ok; stream to use for stderr until stdio
* is reopened.
*
@@ -677,7 +772,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 +799,7 @@ class ZygoteConnection {
descriptors[1], descriptors[2]);
for (FileDescriptor fd: descriptors) {
- ZygoteInit.closeDescriptor(fd);
+ ZygoteInit.closeDescriptorQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
@@ -712,37 +807,48 @@ class ZygoteConnection {
}
}
- if (parsedArgs.runtimeInit) {
- RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
- } else {
- ClassLoader cloader;
+ if (parsedArgs.niceName != null) {
+ Process.setArgV0(parsedArgs.niceName);
+ }
- if (parsedArgs.classpath != null) {
- cloader
- = new PathClassLoader(parsedArgs.classpath,
- ClassLoader.getSystemClassLoader());
+ if (parsedArgs.runtimeInit) {
+ if (parsedArgs.invokeWith != null) {
+ WrapperInit.execApplication(parsedArgs.invokeWith,
+ parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
} else {
- cloader = ClassLoader.getSystemClassLoader();
+ RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
}
-
+ } else {
String className;
try {
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
- logAndPrintError (newStderr,
+ logAndPrintError(newStderr,
"Missing required class name argument", null);
return;
}
- String[] mainArgs
- = new String[parsedArgs.remainingArgs.length - 1];
+ String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
- try {
- ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
- } catch (RuntimeException ex) {
- logAndPrintError (newStderr, "Error starting. ", ex);
+ if (parsedArgs.invokeWith != null) {
+ WrapperInit.execStandalone(parsedArgs.invokeWith,
+ parsedArgs.classpath, className, mainArgs);
+ } else {
+ ClassLoader cloader;
+ if (parsedArgs.classpath != null) {
+ cloader = new PathClassLoader(parsedArgs.classpath,
+ ClassLoader.getSystemClassLoader());
+ } else {
+ cloader = ClassLoader.getSystemClassLoader();
+ }
+
+ try {
+ ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
+ } catch (RuntimeException ex) {
+ logAndPrintError(newStderr, "Error starting.", ex);
+ }
}
}
}
@@ -754,40 +860,61 @@ 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) {
+ setChildPgid(pid);
+ }
+
+ if (descriptors != null) {
+ for (FileDescriptor fd: descriptors) {
+ ZygoteInit.closeDescriptorQuietly(fd);
+ }
+ }
- if(pid > 0) {
- // Try to move the new child into the peer's process group.
+ boolean usingWrapper = false;
+ if (pipeFd != null && pid > 0) {
+ DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
+ int innerPid = -1;
try {
- ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
+ innerPid = is.readInt();
} catch (IOException ex) {
- // This exception is expected in the case where
- // the peer is not in our session
- // TODO get rid of this log message in the case where
- // getsid(0) != getsid(peer.getPid())
- Log.i(TAG, "Zygote: setpgid failed. This is "
- + "normal if peer is not in our session");
+ Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException ex) {
+ }
}
- }
- try {
- if (descriptors != null) {
- for (FileDescriptor fd: descriptors) {
- ZygoteInit.closeDescriptor(fd);
+ // Ensure that the pid reported by the wrapped process is either the
+ // child process that we forked, or a descendant of it.
+ if (innerPid > 0) {
+ int parentPid = innerPid;
+ while (parentPid > 0 && parentPid != pid) {
+ parentPid = Process.getParentPid(parentPid);
+ }
+ if (parentPid > 0) {
+ Log.i(TAG, "Wrapped process has pid " + innerPid);
+ pid = innerPid;
+ 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 {
mSocketOutStream.writeInt(pid);
+ mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex);
return true;
@@ -808,6 +935,20 @@ class ZygoteConnection {
return false;
}
+ private void setChildPgid(int pid) {
+ // Try to move the new child into the peer's process group.
+ try {
+ ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
+ } catch (IOException ex) {
+ // This exception is expected in the case where
+ // the peer is not in our session
+ // TODO get rid of this log message in the case where
+ // getsid(0) != getsid(peer.getPid())
+ Log.i(TAG, "Zygote: setpgid failed. This is "
+ + "normal if peer is not in our session");
+ }
+ }
+
/**
* Logs an error message and prints it to the specified stream, if
* provided
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 060f858..958bd7a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -22,6 +22,8 @@ import android.content.res.TypedArray;
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.Config;
@@ -67,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;
@@ -245,6 +247,11 @@ public class ZygoteInit {
}
}
+ static void preload() {
+ preloadClasses();
+ preloadResources();
+ }
+
/**
* Performs Zygote process initialization. Loads and initializes
* commonly used classes.
@@ -494,11 +501,20 @@ public class ZygoteInit {
closeServerSocket();
- /*
- * 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 */
}
@@ -523,20 +539,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) {
@@ -561,9 +570,7 @@ public class ZygoteInit {
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
- preloadClasses();
- //cacheRegisterMaps();
- preloadResources();
+ preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
@@ -578,9 +585,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);
}
@@ -760,6 +767,13 @@ public class ZygoteInit {
static native void closeDescriptor(FileDescriptor fd)
throws IOException;
+ static void closeDescriptorQuietly(FileDescriptor fd) {
+ try {
+ closeDescriptor(fd);
+ } catch (IOException e) {
+ }
+ }
+
/**
* Toggles the close-on-exec flag for the specified file descriptor.
*
@@ -811,6 +825,21 @@ public class ZygoteInit {
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.
*/
private ZygoteInit() {
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d9759bf..9b4d446 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -879,8 +879,11 @@ bail:
* 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)");
@@ -895,7 +898,7 @@ void AndroidRuntime::start(const char* className, const bool startSystemServer)
* 'startSystemServer == true' means runtime is obslete 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,
@@ -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.
- * 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);
@@ -944,9 +947,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
@@ -990,12 +992,6 @@ bail:
free(slashClassName);
}
-void AndroidRuntime::start()
-{
- start("com.android.internal.os.RuntimeInit",
- false /* Don't start the system server */);
-}
-
void AndroidRuntime::onExit(int code)
{
LOGV("AndroidRuntime onExit calling exit(%d)", code);
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index ada4dd3..666a5d6 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -145,6 +145,10 @@ static void com_android_internal_os_ZygoteInit_reopenStdio(JNIEnv* env,
static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
jobject clazz, jobject descriptor)
{
+ if (!descriptor) {
+ return;
+ }
+
int fd;
int err;
@@ -327,6 +331,30 @@ static jobject com_android_internal_os_ZygoteInit_createFileDescriptor (
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.
*/
@@ -355,7 +383,11 @@ static JNINativeMethod gMethods[] = {
{ "selectReadable", "([Ljava/io/FileDescriptor;)I",
(void *) com_android_internal_os_ZygoteInit_selectReadable },
{ "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)
{
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index 09f0de1..7975850 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -37,6 +37,13 @@ public:
AndroidRuntime();
virtual ~AndroidRuntime();
+ enum StartMode {
+ Zygote,
+ SystemServer,
+ Application,
+ Tool,
+ };
+
/**
* Register a set of methods in the specified class.
*/
@@ -61,8 +68,7 @@ public:
int addVmArguments(int argc, const char* const argv[]);
- void start(const char *classname, const bool startSystemServer);
- void start(); // start in android.util.RuntimeInit
+ void start(const char *classname, const char* options);
static AndroidRuntime* getRuntime();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d24ce7e..854f96d 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -203,6 +203,12 @@ public final class ActivityManagerService extends ActivityManagerNative
// before we decide it's never going to come up for real.
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.
static final int GC_TIMEOUT = 5*1000;
@@ -841,14 +847,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
@@ -1395,15 +1393,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();
@@ -1872,9 +1861,11 @@ public final class ActivityManagerService extends ActivityManagerNative
if ("1".equals(SystemProperties.get("debug.assert"))) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
- int pid = Process.start("android.app.ActivityThread",
- mSimpleProcessManagement ? app.processName : null, uid, uid,
- gids, debugFlags, null);
+
+ // Start the process. It will either succeed and return a result containing
+ // 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();
synchronized (bs) {
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,
hostingNameStr != null ? hostingNameStr : "");
if (app.persistent) {
- Watchdog.getInstance().processStarted(app.processName, pid);
+ Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
StringBuilder buf = mStringBuilder;
@@ -1901,7 +1892,7 @@ public final class ActivityManagerService extends ActivityManagerNative
buf.append(hostingNameStr);
}
buf.append(": pid=");
- buf.append(pid);
+ buf.append(startResult.pid);
buf.append(" uid=");
buf.append(uid);
buf.append(" gids={");
@@ -1914,26 +1905,22 @@ public final class ActivityManagerService extends ActivityManagerNative
}
buf.append("}");
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.
app.pid = MY_PID;
app.removed = false;
mStartingProcesses.add(app);
- } else if (pid > 0) {
- app.pid = pid;
+ } else {
+ app.pid = startResult.pid;
+ app.usingWrapper = startResult.usingWrapper;
app.removed = false;
synchronized (mPidsSelfLocked) {
- this.mPidsSelfLocked.put(pid, app);
+ this.mPidsSelfLocked.put(startResult.pid, app);
Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
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) {
// XXX do better error recovery.
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4d773e4..e872586 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -554,12 +554,12 @@ class ActivityRecord extends IApplicationToken.Stub {
public long getKeyDispatchingTimeout() {
synchronized(service) {
ActivityRecord r = getWaitingHistoryRecordLocked();
- if (r == null || r.app == null
- || r.app.instrumentationClass == null) {
- return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
+ if (r != null && r.app != null
+ && (r.app.instrumentationClass != null || r.app.usingWrapper)) {
+ return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
}
-
- return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
+
+ return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
}
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 353ff6d..d809863 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -74,6 +74,7 @@ class ProcessRecord {
IInstrumentationWatcher instrumentationWatcher; // who is waiting
Bundle instrumentationArguments;// as given to us
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
long lastWakeTime; // How long proc held wake lock at last check
long lastCpuTime; // How long proc has run CPU at last check