Merge "CachedAppOptimizer: kill apps that spam binder transactions" into udc-dev

This commit is contained in:
Tim Murray
2023-06-16 04:11:17 +00:00
committed by Android (Google) Code Review
2 changed files with 120 additions and 11 deletions

View File

@@ -123,7 +123,14 @@ public final class CachedAppOptimizer {
"freeze_debounce_timeout";
@VisibleForTesting static final String KEY_FREEZER_EXEMPT_INST_PKG =
"freeze_exempt_inst_pkg";
@VisibleForTesting static final String KEY_FREEZER_BINDER_ENABLED =
"freeze_binder_enabled";
@VisibleForTesting static final String KEY_FREEZER_BINDER_DIVISOR =
"freeze_binder_divisor";
@VisibleForTesting static final String KEY_FREEZER_BINDER_OFFSET =
"freeze_binder_offset";
@VisibleForTesting static final String KEY_FREEZER_BINDER_THRESHOLD =
"freeze_binder_threshold";
static final int UNFREEZE_REASON_NONE =
FrameworkStatsLog.APP_FREEZE_CHANGED__UNFREEZE_REASON_V2__UFR_NONE;
@@ -237,8 +244,8 @@ public final class CachedAppOptimizer {
@VisibleForTesting static final boolean ENABLE_FILE_COMPACT = false;
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = true;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = true;
@VisibleForTesting static final boolean DEFAULT_USE_COMPACTION = true;
@VisibleForTesting static final boolean DEFAULT_USE_FREEZER = true;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -257,7 +264,11 @@ public final class CachedAppOptimizer {
@VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
@VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 10_000L;
@VisibleForTesting static final Boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
@VisibleForTesting static final boolean DEFAULT_FREEZER_EXEMPT_INST_PKG = true;
@VisibleForTesting static final boolean DEFAULT_FREEZER_BINDER_ENABLED = true;
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_DIVISOR = 4;
@VisibleForTesting static final int DEFAULT_FREEZER_BINDER_OFFSET = 500;
@VisibleForTesting static final long DEFAULT_FREEZER_BINDER_THRESHOLD = 1_000;
@VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
Settings.Global.CACHED_APPS_FREEZER_ENABLED);
@@ -393,6 +404,11 @@ public final class CachedAppOptimizer {
updateFreezerDebounceTimeout();
} else if (KEY_FREEZER_EXEMPT_INST_PKG.equals(name)) {
updateFreezerExemptInstPkg();
} else if (KEY_FREEZER_BINDER_ENABLED.equals(name)
|| KEY_FREEZER_BINDER_DIVISOR.equals(name)
|| KEY_FREEZER_BINDER_THRESHOLD.equals(name)
|| KEY_FREEZER_BINDER_OFFSET.equals(name)) {
updateFreezerBinderState();
}
}
}
@@ -455,6 +471,16 @@ public final class CachedAppOptimizer {
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting final Set<Integer> mProcStateThrottle;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile boolean mFreezerBinderEnabled = DEFAULT_FREEZER_BINDER_ENABLED;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mFreezerBinderDivisor = DEFAULT_FREEZER_BINDER_DIVISOR;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile int mFreezerBinderOffset = DEFAULT_FREEZER_BINDER_OFFSET;
@GuardedBy("mPhenotypeFlagLock")
@VisibleForTesting volatile long mFreezerBinderThreshold = DEFAULT_FREEZER_BINDER_THRESHOLD;
// Handler on which compaction runs.
@VisibleForTesting
Handler mCompactionHandler;
@@ -759,6 +785,10 @@ public final class CachedAppOptimizer {
pw.println(" " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
pw.println(" " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
pw.println(" " + KEY_FREEZER_EXEMPT_INST_PKG + "=" + mFreezerExemptInstPkg);
pw.println(" " + KEY_FREEZER_BINDER_ENABLED + "=" + mFreezerBinderEnabled);
pw.println(" " + KEY_FREEZER_BINDER_THRESHOLD + "=" + mFreezerBinderThreshold);
pw.println(" " + KEY_FREEZER_BINDER_DIVISOR + "=" + mFreezerBinderDivisor);
pw.println(" " + KEY_FREEZER_BINDER_OFFSET + "=" + mFreezerBinderOffset);
synchronized (mProcLock) {
int size = mFrozenProcesses.size();
pw.println(" Apps frozen: " + size);
@@ -1264,6 +1294,26 @@ public final class CachedAppOptimizer {
Slog.d(TAG_AM, "Freezer exemption set to " + mFreezerExemptInstPkg);
}
@GuardedBy("mPhenotypeFlagLock")
private void updateFreezerBinderState() {
mFreezerBinderEnabled = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_BINDER_ENABLED, DEFAULT_FREEZER_BINDER_ENABLED);
mFreezerBinderDivisor = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_BINDER_DIVISOR, DEFAULT_FREEZER_BINDER_DIVISOR);
mFreezerBinderOffset = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_BINDER_OFFSET, DEFAULT_FREEZER_BINDER_OFFSET);
mFreezerBinderThreshold = DeviceConfig.getLong(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT,
KEY_FREEZER_BINDER_THRESHOLD, DEFAULT_FREEZER_BINDER_THRESHOLD);
Slog.d(TAG_AM, "Freezer binder state set to enabled=" + mFreezerBinderEnabled
+ ", divisor=" + mFreezerBinderDivisor
+ ", offset=" + mFreezerBinderOffset
+ ", threshold=" + mFreezerBinderThreshold);
}
private boolean parseProcStateThrottle(String procStateThrottleString) {
String[] procStates = TextUtils.split(procStateThrottleString, ",");
mProcStateThrottle.clear();
@@ -1352,6 +1402,7 @@ public final class CachedAppOptimizer {
}
}
}
app.mOptRecord.setLastUsedTimeout(delayMillis);
mFreezeHandler.sendMessageDelayed(
mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
delayMillis);
@@ -2121,12 +2172,54 @@ public final class CachedAppOptimizer {
}
@GuardedBy({"mAm", "mProcLock"})
private void rescheduleFreeze(final ProcessRecord proc, final String reason,
@UnfreezeReason int reasonCode) {
private void handleBinderFreezerFailure(final ProcessRecord proc, final String reason) {
if (!mFreezerBinderEnabled) {
// Just reschedule indefinitely.
unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS);
freezeAppAsyncLSP(proc);
return;
}
/*
* This handles the case where a process couldn't be frozen due to pending binder
* transactions. In order to prevent apps from avoiding the freezer by spamming binder
* transactions, there is an exponential decrease in freezer retry times plus a random
* offset per attempt to avoid phase issues. Once the last-attempted timeout is below a
* threshold, we assume that the app is spamming binder calls and can never be frozen,
* and we will then crash the app.
*/
if (proc.mOptRecord.getLastUsedTimeout() <= mFreezerBinderThreshold) {
// We've given the app plenty of chances, assume broken. Time to die.
Slog.d(TAG_AM, "Kill app due to repeated failure to freeze binder: "
+ proc.getPid() + " " + proc.processName);
mAm.mHandler.post(() -> {
synchronized (mAm) {
// Crash regardless of procstate in case the app has found another way
// to abuse oom_adj
if (proc.getThread() == null) {
return;
}
proc.killLocked("excessive binder traffic during cached",
ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE,
ApplicationExitInfo.SUBREASON_EXCESSIVE_CPU,
true);
}
});
return;
}
long timeout = proc.mOptRecord.getLastUsedTimeout() / mFreezerBinderDivisor;
// range is [-mFreezerBinderOffset, +mFreezerBinderOffset]
int offset = mRandom.nextInt(mFreezerBinderOffset * 2) - mFreezerBinderOffset;
timeout = Math.max(timeout + offset, mFreezerBinderThreshold);
Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid()
+ " " + proc.processName + " (" + reason + ")");
unfreezeAppLSP(proc, reasonCode);
freezeAppAsyncLSP(proc);
+ " " + proc.processName + " (" + reason + "), timeout=" + timeout);
Trace.instantForTrack(Trace.TRACE_TAG_ACTIVITY_MANAGER, ATRACE_FREEZER_TRACK,
"Reschedule freeze " + proc.processName + ":" + proc.getPid()
+ " timeout=" + timeout + ", reason=" + reason);
unfreezeAppLSP(proc, UNFREEZE_REASON_BINDER_TXNS);
freezeAppAsyncLSP(proc, timeout);
}
/**
@@ -2173,7 +2266,7 @@ public final class CachedAppOptimizer {
// transactions that might be pending.
try {
if (freezeBinder(pid, true, FREEZE_BINDER_TIMEOUT_MS) != 0) {
rescheduleFreeze(proc, "outstanding txns", UNFREEZE_REASON_BINDER_TXNS);
handleBinderFreezerFailure(proc, "outstanding txns");
return;
}
} catch (RuntimeException e) {
@@ -2234,7 +2327,7 @@ public final class CachedAppOptimizer {
if ((freezeInfo & TXNS_PENDING_WHILE_FROZEN) != 0) {
synchronized (mProcLock) {
rescheduleFreeze(proc, "new pending txns", UNFREEZE_REASON_BINDER_TXNS);
handleBinderFreezerFailure(proc, "new pending txns");
}
return;
}

View File

@@ -127,6 +127,12 @@ final class ProcessCachedOptimizerRecord {
@GuardedBy("mProcLock")
private @UptimeMillisLong long mEarliestFreezableTimeMillis;
/**
* This is the most recently used timeout for freezing the app in millis
*/
@GuardedBy("mProcLock")
private long mLastUsedTimeout;
@GuardedBy("mProcLock")
long getLastCompactTime() {
return mLastCompactTime;
@@ -281,6 +287,16 @@ final class ProcessCachedOptimizerRecord {
mEarliestFreezableTimeMillis = earliestFreezableTimeMillis;
}
@GuardedBy("mProcLock")
long getLastUsedTimeout() {
return mLastUsedTimeout;
}
@GuardedBy("mProcLock")
void setLastUsedTimeout(long lastUsedTimeout) {
mLastUsedTimeout = lastUsedTimeout;
}
@GuardedBy("mProcLock")
boolean isFreezeExempt() {
return mFreezeExempt;