diff options
| -rw-r--r-- | services/core/java/com/android/server/am/CachedAppOptimizer.java | 115 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java | 16 |
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; } |