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
This commit is contained in:
Marco Ballesio
2021-01-27 10:03:27 -08:00
parent d23e082711
commit 8b5cdef0da
2 changed files with 53 additions and 22 deletions

View File

@@ -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,

View File

@@ -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