summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/am/CachedAppOptimizer.java115
-rw-r--r--services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java16
2 files changed, 120 insertions, 11 deletions
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 1fa2bea6e5ea..eccff2a74eac 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -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;
}
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index ffe5a6e6b958..f5c5ea8ae55a 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -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;
@@ -282,6 +288,16 @@ final class ProcessCachedOptimizerRecord {
}
@GuardedBy("mProcLock")
+ long getLastUsedTimeout() {
+ return mLastUsedTimeout;
+ }
+
+ @GuardedBy("mProcLock")
+ void setLastUsedTimeout(long lastUsedTimeout) {
+ mLastUsedTimeout = lastUsedTimeout;
+ }
+
+ @GuardedBy("mProcLock")
boolean isFreezeExempt() {
return mFreezeExempt;
}