Loading services/core/java/com/android/server/am/CachedAppOptimizer.java +104 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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(); } } } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -2121,12 +2172,54 @@ public final class CachedAppOptimizer { } @GuardedBy({"mAm", "mProcLock"}) private void rescheduleFreeze(final ProcessRecord proc, final String reason, @UnfreezeReason int reasonCode) { Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid() + " " + proc.processName + " (" + reason + ")"); unfreezeAppLSP(proc, 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 + "), 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); } /** Loading Loading @@ -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) { Loading Loading @@ -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; } Loading services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading
services/core/java/com/android/server/am/CachedAppOptimizer.java +104 −11 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading @@ -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); Loading Loading @@ -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(); } } } Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading @@ -2121,12 +2172,54 @@ public final class CachedAppOptimizer { } @GuardedBy({"mAm", "mProcLock"}) private void rescheduleFreeze(final ProcessRecord proc, final String reason, @UnfreezeReason int reasonCode) { Slog.d(TAG_AM, "Reschedule freeze for process " + proc.getPid() + " " + proc.processName + " (" + reason + ")"); unfreezeAppLSP(proc, 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 + "), 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); } /** Loading Loading @@ -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) { Loading Loading @@ -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; } Loading
services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java +16 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading