Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3f9dd287 authored by Jeff Brown's avatar Jeff Brown
Browse files

Increase activity timeouts when using a wrapper process.

This patch enables the Zygote to tell the ActivityManager when
it has started a process with a wrapper attached so that the
ActivityManager can allow it extra time to start up or process
events.

This is useful when wrapping an app with Valgrind or other tools
which add significant runtime overhead.

Bug: 4584468
Change-Id: I5db6f2f15cd30b0ec40f547d2fadfa216de2926d
parent 5623b074
Loading
Loading
Loading
Loading
+31 −25
Original line number Diff line number Diff line
@@ -270,13 +270,12 @@ public class Process {
     * @param targetSdkVersion The target SDK version for the app.
     * @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, int targetSdkVersion,
@@ -376,14 +375,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 {
@@ -394,7 +390,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()));
@@ -414,11 +411,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) {
@@ -433,8 +432,6 @@ public class Process {

            throw new ZygoteStartFailedEx(ex);
        }

        return pid;
    }

    /**
@@ -449,18 +446,16 @@ public class Process {
     * @param debugFlags Additional flags.
     * @param targetSdkVersion The target SDK version for the app.
     * @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, int targetSdkVersion,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        int pid;

        synchronized(Process.class) {
            ArrayList<String> argsForZygote = new ArrayList<String>();

@@ -517,14 +512,8 @@ public class Process {
                }
            }

            pid = zygoteSendArgsAndGetPid(argsForZygote);
            return zygoteSendArgsAndGetResult(argsForZygote);
        }

        if (pid <= 0) {
            throw new ZygoteStartFailedEx("zygote start failed:" + pid);
        }

        return pid;
    }
    
    /**
@@ -808,4 +797,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;
    }
}
+3 −0
Original line number Diff line number Diff line
@@ -900,6 +900,7 @@ class ZygoteConnection {
            }
        }

        boolean usingWrapper = false;
        if (pipeFd != null && pid > 0) {
            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
            int innerPid = -1;
@@ -924,6 +925,7 @@ class ZygoteConnection {
                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
@@ -934,6 +936,7 @@ class ZygoteConnection {

        try {
            mSocketOutStream.writeInt(pid);
            mSocketOutStream.writeBoolean(usingWrapper);
        } catch (IOException ex) {
            Log.e(TAG, "Error reading from command socket", ex);
            return true;
+23 −19
Original line number Diff line number Diff line
@@ -208,6 +208,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;
@@ -1950,9 +1956,13 @@ 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",
            // 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,
                    app.info.targetSdkVersion, null);
            BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
            synchronized (bs) {
                if (bs.isOnBattery()) {
@@ -1960,12 +1970,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;
@@ -1979,7 +1989,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={");
@@ -1992,21 +2002,15 @@ public final class ActivityManagerService extends ActivityManagerNative
            }
            buf.append("}");
            Slog.i(TAG, buf.toString());
            if (pid > 0) {
                app.pid = pid;
            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);
                }
            } else {
                app.pid = 0;
                RuntimeException e = new RuntimeException(
                        "Failure starting process " + app.processName
                        + ": returned pid=" + pid);
                Slog.e(TAG, e.getMessage(), e);
                mHandler.sendMessageDelayed(msg, startResult.usingWrapper
                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
            }
        } catch (RuntimeException e) {
            // XXX do better error recovery.
+5 −5
Original line number Diff line number Diff line
@@ -658,12 +658,12 @@ final 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;
        }
    }

+1 −0
Original line number Diff line number Diff line
@@ -78,6 +78,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