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

Commit b91c0363 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 1c3c9487 6dae283c
Loading
Loading
Loading
Loading
+12 −0
Original line number Original line Diff line number Diff line
@@ -1462,6 +1462,18 @@ public class Process {
     */
     */
    public static final native int killProcessGroup(int uid, int pid);
    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
     * Remove all process groups.  Expected to be called when ActivityManager
     * is restarted.
     * is restarted.
+15 −0
Original line number Original line Diff line number Diff line
@@ -1252,6 +1252,20 @@ static jint android_os_Process_nativePidFdOpen(JNIEnv* env, jobject, jint pid, j
    return fd;
    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[] = {
static const JNINativeMethod methods[] = {
        {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
        {"getUidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getUidForName},
        {"getGidForName", "(Ljava/lang/String;)I", (void*)android_os_Process_getGidForName},
        {"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},
        {"killProcessGroup", "(II)I", (void*)android_os_Process_killProcessGroup},
        {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
        {"removeAllProcessGroups", "()V", (void*)android_os_Process_removeAllProcessGroups},
        {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen},
        {"nativePidFdOpen", "(II)I", (void*)android_os_Process_nativePidFdOpen},
        {"freezeCgroupUid", "(IZ)V", (void*)android_os_Process_freezeCgroupUID},
};
};


int register_android_os_Process(JNIEnv* env)
int register_android_os_Process(JNIEnv* env)
+7 −3
Original line number Original line Diff line number Diff line
@@ -110,6 +110,8 @@ public final class CachedAppOptimizer {


    private static final String ATRACE_COMPACTION_TRACK = "Compaction";
    private static final String ATRACE_COMPACTION_TRACK = "Compaction";


    private static final int FREEZE_BINDER_TIMEOUT_MS = 100;

    // Defaults for phenotype flags.
    // Defaults for phenotype flags.
    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
    @VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
    @VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
@@ -927,11 +929,13 @@ public final class CachedAppOptimizer {
     * @param pid the target pid for which binder transactions are to be frozen
     * @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
     * @param freeze specifies whether to flush transactions and then freeze (true) or unfreeze
     * binder for the specificed pid.
     * 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.
     * @throws RuntimeException in case a flush/freeze operation could not complete successfully.
     * @return 0 if success, or -EAGAIN indicating there's pending transaction.
     * @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.
     * Retrieves binder freeze info about a process.
@@ -1298,7 +1302,7 @@ public final class CachedAppOptimizer {
        long freezeTime = opt.getFreezeUnfreezeTime();
        long freezeTime = opt.getFreezeUnfreezeTime();


        try {
        try {
            freezeBinder(pid, false);
            freezeBinder(pid, false, FREEZE_BINDER_TIMEOUT_MS);
        } catch (RuntimeException e) {
        } catch (RuntimeException e) {
            Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
            Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
                    + ". Killing it");
                    + ". Killing it");
@@ -1930,7 +1934,7 @@ public final class CachedAppOptimizer {
                // Freeze binder interface before the process, to flush any
                // Freeze binder interface before the process, to flush any
                // transactions that might be pending.
                // transactions that might be pending.
                try {
                try {
                    if (freezeBinder(pid, true) != 0) {
                    if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
                        rescheduleFreeze(proc, "outstanding txns");
                        rescheduleFreeze(proc, "outstanding txns");
                        return;
                        return;
                    }
                    }
+60 −4
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.killProcessQuiet;
import static android.os.Process.startWebView;
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_LRU;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_NETWORK;
@@ -2705,6 +2706,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"})
    @GuardedBy({"mService", "mProcLock"})
    boolean killPackageProcessesLSP(String packageName, int appId,
    boolean killPackageProcessesLSP(String packageName, int appId,
            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
@@ -2757,7 +2802,7 @@ public final class ProcessList {
                boolean shouldAllowRestart = false;
                boolean shouldAllowRestart = false;


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


        final int packageUID = UserHandle.getUid(userId, appId);
        freezeBinderAndPackageCgroup(procs, packageUID);

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


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


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


    @GuardedBy("mService")
    @GuardedBy("mService")
    void killLocked(String reason, @Reason int reasonCode, boolean noisy) {
    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")
    @GuardedBy("mService")
    void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
    void killLocked(String reason, @Reason int reasonCode, @SubReason int subReason,
            boolean noisy) {
            boolean noisy) {
        killLocked(reason, reason, reasonCode, subReason, noisy);
        killLocked(reason, reason, reasonCode, subReason, noisy, true);
    }
    }


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