From 8b5cdef0da56bf602674227e699a7a8e36645a21 Mon Sep 17 00:00:00 2001 From: Marco Ballesio Date: Wed, 27 Jan 2021 10:03:27 -0800 Subject: [PATCH] CachedAppOptimizer: adapt freezer to uid/pid hiearchy. The uid/pid hierarchy freezer is supported if the cgroup.freeze file is present under any uid cgroup. Check on uid_0 which is granted to exist. Also handle binder when enabling/disabling freezer. Freeze the binder interfaces for all froze processes after enabling the app freezer. Similarly, unfreeze the interfaces of frozen processes when the app freezer is enabled. Bug: 168907513 Bug: 173173405 Test: verified proper functioning of the freezer after boot on devices supporting the uid/pid hierarchy. Test: ran "adb shell am dumpsys meminfo -a", verified that no processes were killed by ActivityManager and that the command did not hang. Change-Id: Ia7fa25cb6c59da73d34460ead4833cec69f7f6bc --- .../android/server/am/CachedAppOptimizer.java | 74 +++++++++++++------ .../com/android/server/am/ProcessRecord.java | 1 + 2 files changed, 53 insertions(+), 22 deletions(-) diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java index dd09a1c856826..85347467e33a4 100644 --- a/services/core/java/com/android/server/am/CachedAppOptimizer.java +++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java @@ -232,6 +232,8 @@ public final class CachedAppOptimizer { @VisibleForTesting Handler mCompactionHandler; private Handler mFreezeHandler; + @GuardedBy("mAm") + private boolean mFreezerOverride = false; // Maps process ID to last compaction statistics for processes that we've fully compacted. Used // when evaluating throttles that we only consider for "full" compaction, so we don't store @@ -454,21 +456,35 @@ public final class CachedAppOptimizer { } } - try { - enableFreezerInternal(enable); - return true; - } catch (java.lang.RuntimeException e) { - if (enable) { - mFreezerDisableCount = 0; - } else { - mFreezerDisableCount = 1; - } + // Override is applied immediately, restore is delayed + synchronized (mAm) { + int processCount = mAm.mProcessList.mLruProcesses.size(); - Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): " - + e.toString()); + mFreezerOverride = !enable; + Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride); + + for (int i = 0; i < processCount; i++) { + ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i); + + if (process == null) { + continue; + } + + if (enable && process.freezerOverride) { + freezeAppAsync(process); + process.freezerOverride = false; + } + + if (!enable && process.frozen) { + unfreezeAppLocked(process); + + // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag) + process.freezerOverride = true; + } + } } - return false; + return true; } /** @@ -515,7 +531,7 @@ public final class CachedAppOptimizer { FileReader fr = null; try { - fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze"); + fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze"); char state = (char) fr.read(); if (state == '1' || state == '0') { @@ -744,6 +760,8 @@ public final class CachedAppOptimizer { void unfreezeAppLocked(ProcessRecord app) { mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app); + app.freezerOverride = false; + if (!app.frozen) { if (DEBUG_FREEZER) { Slog.d(TAG_AM, @@ -753,6 +771,8 @@ public final class CachedAppOptimizer { return; } + // Unfreeze the binder interface first, to avoid transactions triggered by timers fired + // right after unfreezing the process to fail boolean processKilled = false; try { @@ -1131,12 +1151,31 @@ public final class CachedAppOptimizer { return; } + if (mFreezerOverride) { + proc.freezerOverride = true; + Slog.d(TAG_AM, "Skipping freeze for process " + pid + + " " + name + " curAdj = " + proc.curAdj + + "(override)"); + return; + } + if (pid == 0 || proc.frozen) { // Already frozen or not a real process, either one being // launched or one being killed return; } + // Freeze binder interface before the process, to flush any + // transactions that might be pending. + try { + freezeBinder(pid, true); + } catch (RuntimeException e) { + Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); + proc.kill("Unable to freeze binder interface", + ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_INVALID_STATE, true); + } + long unfreezeTime = proc.freezeUnfreezeTime; try { @@ -1163,15 +1202,6 @@ public final class CachedAppOptimizer { EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name); - try { - freezeBinder(pid, true); - } catch (RuntimeException e) { - Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name); - proc.kill("Unable to freeze binder interface", - ApplicationExitInfo.REASON_OTHER, - ApplicationExitInfo.SUBREASON_INVALID_STATE, true); - } - // See above for why we're not taking mPhenotypeFlagLock here if (mRandom.nextFloat() < mFreezerStatsdSampleRate) { FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED, diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 520a28bfd8892..63195d35b0499 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -179,6 +179,7 @@ class ProcessRecord implements WindowProcessListener { int reqCompactAction; // The most recent compaction action requested for this app. int lastCompactAction; // The most recent compaction action performed for this app. boolean frozen; // True when the process is frozen. + boolean freezerOverride; // An override on the freeze state is in progress. long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process private int mCurSchedGroup; // Currently desired scheduling class