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

Commit ad4b867d authored by Tao Wu's avatar Tao Wu
Browse files

Retry when starting process from appZygote

We only retry when the original zygote process is dead.
Also we don't kill the dead process again.

Bug: 361799815
Test: manual - Launching new chrome tab after killing chrome_zygote
Flag: android.os.app_zygote_retry_start
Change-Id: Iad42ae4f8763fc06a2f2f183da934ee8ed536a58
parent c271045a
Loading
Loading
Loading
Loading
+86 −1
Original line number Diff line number Diff line
@@ -16,15 +16,22 @@

package android.os;

import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.content.pm.ProcessInfo;
import android.util.Log;
import android.util.Pair;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Zygote;

import dalvik.system.VMRuntime;

import java.util.Map;

/**
 * AppZygote is responsible for interfacing with an application-specific zygote.
 *
@@ -94,12 +101,90 @@ public class AppZygote {
        return mAppInfo;
    }

    /**
     * Start a new process.
     *
     * <p>Wrap ZygoteProcess.start with retry logic.
     *
     * @param processClass The class to use as the process's main entry
     *                     point.
     * @param niceName A more readable name to use for the process.
     * @param uid The user-id under which the process will run.
     * @param gids Additional group-ids associated with the process.
     * @param runtimeFlags Additional flags.
     * @param targetSdkVersion The target SDK version for the app.
     * @param seInfo null-ok SELinux information for the new process.
     * @param abi non-null the ABI this app should be started with.
     * @param instructionSet null-ok the instruction set to use.
     * @param appDataDir null-ok the data directory of the app.
     * @param packageName null-ok the name of the package this process belongs to.
     * @param isTopApp Whether the process starts for high priority application.
     * @param disabledCompatChanges null-ok list of disabled compat changes for the process being
     *                             started.
     * @param pkgDataInfoMap Map from related package names to private data directory
     *                       volume UUID and inode number.
     * @param allowlistedDataInfoList Map from allowlisted package names to private data directory
     *                       volume UUID and inode number.
     * @param zygoteArgs Additional arguments to supply to the Zygote process.
     * @return An object that describes the result of the attempt to start the process.
     * @throws RuntimeException on fatal start failure
     */
    public final Process.ProcessStartResult startProcess(@NonNull final String processClass,
            final String niceName,
            int uid, @Nullable int[] gids,
            int runtimeFlags, int mountExternal,
            int targetSdkVersion,
            @Nullable String seInfo,
            @NonNull String abi,
            @Nullable String instructionSet,
            @Nullable String appDataDir,
            @Nullable String packageName,
            boolean isTopApp,
            @Nullable long[] disabledCompatChanges,
            @Nullable Map<String, Pair<String, Long>>
            pkgDataInfoMap,
            @Nullable Map<String, Pair<String, Long>>
            allowlistedDataInfoList,
            @Nullable String[] zygoteArgs) {
        try {
            return getProcess().start(processClass,
                    niceName, uid, uid, gids, runtimeFlags, mountExternal,
                    targetSdkVersion, seInfo, abi, instructionSet,
                    appDataDir, null, packageName,
                    /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                    disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList,
                    false, false, false,
                    zygoteArgs);
        } catch (RuntimeException e) {
            if (!Flags.appZygoteRetryStart()) {
                throw e;
            }
            final boolean zygote_dead = getProcess().isDead();
            if (!zygote_dead) {
                throw e; // Zygote process is alive. Do nothing.
            }
        }
        // Retry here if the previous start fails.
        Log.w(LOG_TAG, "retry starting process " + niceName);
        stopZygote();
        return getProcess().start(processClass,
                niceName, uid, uid, gids, runtimeFlags, mountExternal,
                targetSdkVersion, seInfo, abi, instructionSet,
                appDataDir, null, packageName,
                /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                disabledCompatChanges, pkgDataInfoMap, allowlistedDataInfoList,
                false, false, false,
                zygoteArgs);
    }

    @GuardedBy("mLock")
    private void stopZygoteLocked() {
        if (mZygote != null) {
            mZygote.close();
            // use killProcessGroup() here, so we kill all untracked children as well.
            if (!mZygote.isDead()) {
                Process.killProcessGroup(mZygoteUid, mZygote.getPid());
            }
            mZygote = null;
        }
    }
+37 −1
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
package android.os;

import android.net.LocalSocketAddress;
import android.system.ErrnoException;
import android.system.Os;

import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Represents a connection to a child-zygote process. A child-zygote is spawend from another
@@ -30,9 +34,23 @@ public class ChildZygoteProcess extends ZygoteProcess {
     */
    private final int mPid;

    ChildZygoteProcess(LocalSocketAddress socketAddress, int pid) {
    /**
     * The UID of the child zygote process.
     */
    private final int mUid;


    /**
     * If this zygote process was dead;
     */
    private AtomicBoolean mDead;


    ChildZygoteProcess(LocalSocketAddress socketAddress, int pid, int uid) {
        super(socketAddress, null);
        mPid = pid;
        mUid = uid;
        mDead = new AtomicBoolean(false);
    }

    /**
@@ -41,4 +59,22 @@ public class ChildZygoteProcess extends ZygoteProcess {
    public int getPid() {
        return mPid;
    }

    /**
     * Check if child-zygote process is dead
     */
    public boolean isDead() {
        if (mDead.get()) {
            return true;
        }
        try {
            if (Os.stat("/proc/" + mPid).st_uid == mUid) {
                return false;
            }
        } catch (ErrnoException e) {
            // Do nothing, it's dead.
        }
        mDead.set(true);
        return true;
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -1319,6 +1319,6 @@ public class ZygoteProcess {
            throw new RuntimeException("Starting child-zygote through Zygote failed", ex);
        }

        return new ChildZygoteProcess(serverAddress, result.pid);
        return new ChildZygoteProcess(serverAddress, result.pid, uid);
    }
}
+7 −0
Original line number Diff line number Diff line
@@ -149,6 +149,13 @@ flag {
    is_exported: true
}

flag {
     name: "app_zygote_retry_start"
     namespace: "arc_next"
     description: "Guard the new added retry logic in app zygote."
     bug: "361799815"
}

flag {
    name: "battery_part_status_api"
    is_exported: true
+5 −6
Original line number Diff line number Diff line
@@ -2553,13 +2553,12 @@ public final class ProcessList {
                final AppZygote appZygote = createAppZygoteForProcessIfNeeded(app);

                // We can't isolate app data and storage data as parent zygote already did that.
                startResult = appZygote.getProcess().start(entryPoint,
                        app.processName, uid, uid, gids, runtimeFlags, mountExternal,
                startResult = appZygote.startProcess(entryPoint,
                        app.processName, uid, gids, runtimeFlags, mountExternal,
                        app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
                        app.info.dataDir, null, app.info.packageName,
                        /*zygotePolicyFlags=*/ ZYGOTE_POLICY_FLAG_EMPTY, isTopApp,
                        app.getDisabledCompatChanges(), pkgDataInfoMap, allowlistedAppDataInfoMap,
                        false, false, false,
                        app.info.dataDir, app.info.packageName, isTopApp,
                        app.getDisabledCompatChanges(), pkgDataInfoMap,
                        allowlistedAppDataInfoMap,
                        new String[]{PROC_START_SEQ_IDENT + app.getStartSeq()});
            } else {
                regularZygote = true;