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

Commit 9557e800 authored by T.J. Mercier's avatar T.J. Mercier Committed by Android (Google) Code Review
Browse files

Merge "Freeze package cgroup before killing"

parents 3761fe44 79825c6f
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -1481,6 +1481,18 @@ public class Process {
     */
    public static final native int killProcessGroup(int uid, int pid);

    /**
      * Freeze the cgroup for the given UID.
      * This cgroup may contain child cgroups which will also be frozen. If this cgroup or its
      * children contain processes with Binder interfaces, those interfaces should be frozen before
      * the cgroup to avoid blocking synchronous callers indefinitely.
      *
      * @param uid The UID to be frozen
      * @param freeze true = freeze; false = unfreeze
      * @hide
      */
    public static final native void freezeCgroupUid(int uid, boolean freeze);

    /**
     * Remove all process groups.  Expected to be called when ActivityManager
     * is restarted.
+15 −0
Original line number Diff line number Diff line
@@ -1252,6 +1252,20 @@ static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, j
    return fd;
}

void android_os_Process_freezeCgroupUID(JNIEnv* env, jobject clazz, jint uid, jboolean freeze) {
    bool success = true;

    if (freeze) {
        success = SetUserProfiles(uid, {"Frozen"});
    } else {
        success = SetUserProfiles(uid, {"Unfrozen"});
    }

    if (!success) {
        jniThrowRuntimeException(env, "Could not apply user profile");
    }
}

static const JNINativeMethod methods[] = {
        {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
        {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
@@ -1293,6 +1307,7 @@ static const JNINativeMethod methods[] = {
        {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
        {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
        {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen},
        {"freezeCgroupUid", "(IZ)V", (void*)android_os_Process_freezeCgroupUID},
};

int register_android_os_Process(JNIEnv* env)
+8 −4
Original line number Diff line number Diff line
@@ -112,6 +112,8 @@ public final class CachedAppOptimizer {
    private static final String ATRACE_COMPACTION_TRACK = "Compaction";
    private static final String ATRACE_FREEZER_TRACK = "Freezer";

    private static final int FREEZE_BINDER_TIMEOUT_MS = 100;

    // Defaults for phenotype flags.
    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
@@ -929,11 +931,13 @@ public final class CachedAppOptimizer {
     * @param pid the target pid for which binder transactions are to be frozen
     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
     * binder for the specificed pid.
     * @param timeoutMs the timeout in milliseconds to wait for the binder interface to freeze
     * before giving up.
     *
     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
     * @return 0 if success, or -EAGAIN indicating there's pending transaction.
     */
    private static native int freezeBinder(int pid, boolean freeze);
    public static native int freezeBinder(int pid, boolean freeze, int timeoutMs);

    /**
     * Retrieves binder freeze info about a process.
@@ -1300,7 +1304,7 @@ public final class CachedAppOptimizer {
        long freezeTime = opt.getFreezeUnfreezeTime();

        try {
            freezeBinder(pid, false);
            freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
        } catch (RuntimeException e) {
            Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
                    + ". Killing it");
@@ -1355,7 +1359,7 @@ public final class CachedAppOptimizer {
            }
            Slog.d(TAG_AM, "quick sync unfreeze " + pid);
            try {
                freezeBinder(pid, false);
                freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
            } catch (RuntimeException e) {
                Slog.e(TAG_AM, "Unable to quick unfreeze binder for " + pid);
                return;
@@ -1950,7 +1954,7 @@ public final class CachedAppOptimizer {
                // Freeze binder interface before the process, to flush any
                // transactions that might be pending.
                try {
                    if (freezeBinder(pid, true) != 0) {
                    if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
                        rescheduleFreeze(proc, "outstanding txns");
                        return;
                    }
+66 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
import static android.system.OsConstants.*;

import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -2711,6 +2712,50 @@ public final class ProcessList {
        }
    }

    private static boolean freezePackageCgroup(int packageUID, boolean freeze) {
        try {
            Process.freezeCgroupUid(packageUID, freeze);
        } catch (RuntimeException e) {
            final String logtxt = freeze ? "freeze" : "unfreeze";
            Slog.e(TAG, "Unable to " + logtxt + " cgroup uid: " + packageUID + ": " + e);
            return false;
        }
        return true;
    }

    private static void freezeBinderAndPackageCgroup(ArrayList<Pair<ProcessRecord, Boolean>> procs,
                                                     int packageUID) {
        // Freeze all binder processes under the target UID (whose cgroup is about to be frozen).
        // Since we're going to kill these, we don't need to unfreze them later.
        // The procs list may not include all processes under the UID cgroup, but unincluded
        // processes (forks) should not be Binder users.
        int N = procs.size();
        for (int i = 0; i < N; i++) {
            final int uid = procs.get(i).first.uid;
            final int pid = procs.get(i).first.getPid();
            int nRetries = 0;
            // We only freeze the cgroup of the target package, so we do not need to freeze the
            // Binder interfaces of dependant processes in other UIDs.
            if (pid > 0 && uid == packageUID) {
                try {
                    int rc;
                    do {
                        rc = CachedAppOptimizer.freezeBinder(pid, true, 10 /* timeout_ms */);
                    } while (rc == -EAGAIN && nRetries++ < 1);
                    if (rc != 0) Slog.e(TAG, "Unable to freeze binder for " + pid + ": " + rc);
                } catch (RuntimeException e) {
                    Slog.e(TAG, "Unable to freeze binder for " + pid + ": " + e);
                }
            }
        }

        // We freeze the entire UID (parent) cgroup so that newly-specialized processes also freeze
        // despite being added to a new child cgroup. The cgroups of package dependant processes are
        // not frozen, since it's possible this would freeze processes with no dependency on the
        // package being killed here.
        freezePackageCgroup(packageUID, true);
    }

    @GuardedBy({"mService", "mProcLock"})
    boolean killPackageProcessesLSP(String packageName, int appId,
            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
@@ -2763,7 +2808,7 @@ public final class ProcessList {
                boolean shouldAllowRestart = false;

                // If no package is specified, we call all processes under the
                // give user id.
                // given user id.
                if (packageName == null) {
                    if (userId != UserHandle.USER_ALL && app.userId != userId) {
                        continue;
@@ -2806,14 +2851,24 @@ public final class ProcessList {
            }
        }

        final int packageUID = UserHandle.getUid(userId, appId);
        final boolean doFreeze = appId >= Process.FIRST_APPLICATION_UID
                              && appId <= Process.LAST_APPLICATION_UID;
        if (doFreeze) {
            freezeBinderAndPackageCgroup(procs, packageUID);
        }

        int N = procs.size();
        for (int i=0; i<N; i++) {
            final Pair<ProcessRecord, Boolean> proc = procs.get(i);
            removeProcessLocked(proc.first, callerWillRestart, allowRestart || proc.second,
                    reasonCode, subReason, reason);
                    reasonCode, subReason, reason, !doFreeze /* async */);
        }
        killAppZygotesLocked(packageName, appId, userId, false /* force */);
        mService.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_PROCESS_END);
        if (doFreeze) {
            freezePackageCgroup(packageUID, false);
        }
        return N > 0;
    }

@@ -2821,12 +2876,19 @@ public final class ProcessList {
    boolean removeProcessLocked(ProcessRecord app,
            boolean callerWillRestart, boolean allowRestart, int reasonCode, String reason) {
        return removeProcessLocked(app, callerWillRestart, allowRestart, reasonCode,
                ApplicationExitInfo.SUBREASON_UNKNOWN, reason);
                ApplicationExitInfo.SUBREASON_UNKNOWN, reason, true);
    }

    @GuardedBy("mService")
    boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart,
            boolean allowRestart, int reasonCode, int subReason, String reason) {
        return removeProcessLocked(app, callerWillRestart, allowRestart, reasonCode, subReason,
                reason, true);
    }

    @GuardedBy("mService")
    boolean removeProcessLocked(ProcessRecord app, boolean callerWillRestart,
            boolean allowRestart, int reasonCode, int subReason, String reason, boolean async) {
        final String name = app.processName;
        final int uid = app.uid;
        if (DEBUG_PROCESSES) Slog.d(TAG_PROCESSES,
@@ -2863,7 +2925,7 @@ public final class ProcessList {
                    needRestart = true;
                }
            }
            app.killLocked(reason, reasonCode, subReason, true);
            app.killLocked(reason, reasonCode, subReason, true, async);
            mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
                    false /* fromBinderDied */);
            if (willRestart) {
+16 −3
Original line number Diff line number Diff line
@@ -1056,18 +1056,30 @@ class ProcessRecord implements WindowProcessListener {

    @GuardedBy("mService")
    void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
        killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy);
        killLocked(reason, reasonCode, ApplicationExitInfo.SUBREASON_UNKNOWN, noisy, true);
    }

    @GuardedBy("mService")
    void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
            boolean noisy) {
        killLocked(reason, reason, reasonCode, subReason, noisy);
        killLocked(reason, reason, reasonCode, subReason, noisy, true);
    }

    @GuardedBy("mService")
    void killLocked(String reason, String description, @Reason int reasonCode,
            @SubReason int subReason, boolean noisy) {
        killLocked(reason, description, reasonCode, subReason, noisy, true);
    }

    @GuardedBy("mService")
    void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
            boolean noisy, boolean asyncKPG) {
        killLocked(reason, reason, reasonCode, subReason, noisy, asyncKPG);
    }

    @GuardedBy("mService")
    void killLocked(String reason, String description, @Reason int reasonCode,
            @SubReason int subReason, boolean noisy, boolean asyncKPG) {
        if (!mKilledByAm) {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
            if (reasonCode == ApplicationExitInfo.REASON_ANR
@@ -1084,7 +1096,8 @@ class ProcessRecord implements WindowProcessListener {
                EventLog.writeEvent(EventLogTags.AM_KILL,
                        userId, mPid, processName, mState.getSetAdj(), reason);
                Process.killProcessQuiet(mPid);
                ProcessList.killProcessGroup(uid, mPid);
                if (asyncKPG) ProcessList.killProcessGroup(uid, mPid);
                else Process.killProcessGroup(uid, mPid);
            } else {
                mPendingStart = false;
            }
Loading