Merge "CachedAppOptimizer: kill apps that spam binder transactions" into udc-dev
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user