diff options
author | Jeff Brown <jeffbrown@google.com> | 2011-08-10 13:41:36 -0700 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2011-08-10 13:41:36 -0700 |
commit | dbff91077ddf437b62794e077d2ce9e10fff8170 (patch) | |
tree | 350f326738f900e12e5d518b5300123d8c6ddd96 | |
parent | e15a73ee4c9a28d89888095c7649a70c116ee160 (diff) | |
parent | d5d7e164d316e595a64faf1555839d1939da0863 (diff) | |
download | frameworks_base-dbff91077ddf437b62794e077d2ce9e10fff8170.zip frameworks_base-dbff91077ddf437b62794e077d2ce9e10fff8170.tar.gz frameworks_base-dbff91077ddf437b62794e077d2ce9e10fff8170.tar.bz2 |
Merge "Support wrapping app processes to inject debug instrumentation. Bug: 4437846"
-rw-r--r-- | cmds/app_process/app_main.cpp | 71 | ||||
-rw-r--r-- | cmds/runtime/main_runtime.cpp | 4 | ||||
-rw-r--r-- | core/java/android/os/Process.java | 75 | ||||
-rw-r--r-- | core/java/com/android/internal/os/RuntimeInit.java | 141 | ||||
-rw-r--r-- | core/java/com/android/internal/os/WrapperInit.java | 120 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteConnection.java | 249 | ||||
-rw-r--r-- | core/java/com/android/internal/os/ZygoteInit.java | 71 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 24 | ||||
-rw-r--r-- | core/jni/com_android_internal_os_ZygoteInit.cpp | 34 | ||||
-rw-r--r-- | include/android_runtime/AndroidRuntime.h | 10 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 55 | ||||
-rw-r--r-- | services/java/com/android/server/am/ActivityRecord.java | 10 | ||||
-rw-r--r-- | services/java/com/android/server/am/ProcessRecord.java | 1 |
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> [--] <start class name> <args> * </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> [--] <start class name> <args> + * </ul> + */ + static class Arguments { + /** first non-option argument */ + String startClass; + + /** all following arguments */ + String[] startArgs; + + /** + * Constructs instance and parses args + * @param args runtime command-line args + * @throws IllegalArgumentException + */ + Arguments(String args[]) throws IllegalArgumentException { + parseArgs(args); + } + + /** + * Parses the commandline arguments intended for the Runtime. + */ + private void parseArgs(String args[]) + throws IllegalArgumentException { + int curArg = 0; + for (; curArg < args.length; curArg++) { + String arg = args[curArg]; + + if (arg.equals("--")) { + curArg++; + break; + } else if (!arg.startsWith("--")) { + break; + } + } + + if (curArg == args.length) { + throw new IllegalArgumentException("Missing classname argument to RuntimeInit!"); + } + + startClass = args[curArg++]; + startArgs = new String[args.length - curArg]; + System.arraycopy(args, curArg, startArgs, 0, startArgs.length); + } + } } diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java new file mode 100644 index 0000000..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: * [--] <args for RuntimeInit > * <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 < 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 |