I tried to mitigate the issue and it seems I was to some extent successful tweaking the two system-level mechanism below. Here is my debugging session summary:
Details
Summary
After updating to Android 15, the Fairphone 4 aggressively kills background apps — even when sufficient RAM is available (~4.5 GB free out of 7.5 GB). Switching between two apps (e.g., browser to OTP authenticator and back) frequently causes the first app to be killed and restarted from scratch. Music players (Spotify, Tidal), password managers (KeePass), and browsers (Firefox, Brave) are particularly affected. Setting apps to “Unrestricted” battery optimization does not help. This is a regression from Android 13 where multitasking worked normally on the same device.
Device Information
- Device: Fairphone 4 (FP4)
- Build: FP4.SREL.15.14.4
- Android Version: 15 (API 35)
- Security Patch: 2026-01-05
- Hardware: Qualcomm (
ro.hardware=qcom)
- RAM: 7,683 MB total (~7.5 GB)
Problem Description
Background apps are killed within minutes of switching away, despite ample free memory. This makes basic multitasking unreliable:
- Copying an OTP token from an authenticator app causes the browser to lose the login page
- Spotify/Tidal playback stops when the app is evicted from memory
- KeePass is killed while switching to the app that needs the password
- Twitch streams stop after briefly switching away
Severity
This is not a minor inconvenience — it fundamentally breaks Android’s multitasking model. A phone with 7.5 GB RAM should comfortably hold several apps in memory.
Root Cause Analysis
Using adb shell dumpsys activity exit-info, the kill reasons were examined for affected apps. Two system-level mechanisms are responsible:
1. LOW_MEMORY kills (LMKD) — despite sufficient free RAM
The Low Memory Killer Daemon (LMKD) is killing cached apps even when 4.5 GB of RAM is free. This suggests the LMKD thresholds are tuned far too aggressively for a device with 7.5 GB RAM.
2. TOO_MANY_EMPTY_PROCS — cached process cap too low
Android’s ActivityManager enforces a cap of 30 empty (cached) processes (CUR_MAX_EMPTY_PROCESSES=30). Event logs show every automated kill is logged as empty #31 — the instant a 31st cached process exists, the oldest is killed. With modern apps spawning multiple processes (WebView sandboxes, work managers, crash helpers), 30 slots fill up quickly.
3. App compaction is DISABLED
A critical finding: use_compaction=false in the activity_manager device config. App compaction (introduced in Android 10) compresses cached apps in RAM, significantly reducing their memory footprint. With compaction disabled, cached apps consume far more RAM than necessary, leading to more frequent LMKD kills. This appears to be a Fairphone configuration choice, as AOSP defaults to compaction being enabled.
4. Fairphone-customized cached process limit
The setting mCustomizedMaxCachedProcesses=32 shows Fairphone has applied a custom cached process limit lower than the AOSP default. While the effective CUR_MAX_CACHED_PROCESSES was 60, the customization to 32 may affect internal behavior in some code paths.
Evidence
Kill statistics (from dumpsys activity exit-info)
Over a ~24 hour period (2026-04-07 to 2026-04-08):
| App |
LOW_MEMORY kills |
TOO_MANY_EMPTY kills |
Total system kills |
| KeePass2Android |
10 |
6 |
16 |
| Spotify |
7 |
1 |
8 (+ additional user-initiated) |
| Firefox |
6 |
4 |
10 |
Sample kill log (Firefox)
timestamp=2026-04-08 15:46:11.938 reason=3 (LOW_MEMORY)
timestamp=2026-04-08 15:24:58.846 reason=13 (OTHER KILLS BY SYSTEM) subreason=3 (TOO MANY EMPTY PROCS)
timestamp=2026-04-08 14:35:06.921 reason=3 (LOW_MEMORY)
timestamp=2026-04-08 14:02:39.758 reason=3 (LOW_MEMORY)
Firefox was killed 4 times in under 2 hours, despite ~4.5 GB RAM being free.
Sample kill log (KeePass)
timestamp=2026-04-08 10:44:34.114 reason=3 (LOW_MEMORY) rss=156MB
timestamp=2026-04-08 09:01:47.724 reason=3 (LOW_MEMORY) rss=106MB
timestamp=2026-04-08 01:19:11.301 reason=13 (TOO MANY EMPTY PROCS) rss=160MB
timestamp=2026-04-08 01:03:54.747 reason=3 (LOW_MEMORY)
timestamp=2026-04-08 00:44:48.504 reason=3 (LOW_MEMORY)
timestamp=2026-04-07 23:59:15.097 reason=3 (LOW_MEMORY)
timestamp=2026-04-07 23:15:25.613 reason=13 (TOO MANY EMPTY PROCS) rss=72MB
timestamp=2026-04-07 22:59:35.201 reason=13 (TOO MANY EMPTY PROCS)
KeePass — a lightweight app using only 72–160 MB RSS — was killed 8 times in ~12 hours.
ActivityManager event log (logcat -b events)
Every automated kill shows the same pattern — process killed as empty #31:
am_kill: [0,22034,org.mozilla.firefox,995,empty #31,188776]
am_kill: [0,21543,com.werner.spotprisen,985,empty #31,76904]
am_kill: [0,22547,com.google.android.apps.messaging,985,empty #31,175556]
am_kill: [0,20520,ch.protonmail.android,985,empty #31,90028]
am_kill: [0,22898,com.textra,905,empty #31,128544]
Note: even system apps like Google Messages and Proton Mail are being killed.
Problematic configuration values
use_compaction=false # App compaction DISABLED
mCustomizedMaxCachedProcesses=32 # Fairphone custom limit (AOSP default is higher)
CUR_MAX_EMPTY_PROCESSES=30 # Hard cap on empty processes
CUR_TRIM_EMPTY_PROCESSES=15 # During memory trim, keep only 15
CUR_TRIM_CACHED_PROCESSES=10 # During memory trim, keep only 10
Memory state at time of investigation
MemTotal: 7,683,924 kB (~7.5 GB)
MemAvailable: 4,491,668 kB (~4.3 GB free)
4.3 GB of RAM was free while apps were being killed. This strongly indicates the LMKD thresholds are misconfigured.
Attempted Workarounds That Did NOT Help
| Workaround |
Result |
| Set apps to “Unrestricted” battery optimization |
No effect — kills are from LMKD/process cap, not Doze |
| Allow background usage for affected apps |
No effect — same reason |
| Clear app caches |
Temporary, apps killed again within minutes |
ADB Workarounds That DO Help (but require reapplication after reboot)
The following adb commands mitigate the issue, confirming the root cause is in system configuration:
# Enable app compaction (compress cached apps in RAM)
adb shell device_config put activity_manager use_compaction true
# Increase cached process limits
adb shell device_config put activity_manager max_cached_processes 64
# Increase max empty process time
adb shell device_config put activity_manager max_empty_time_millis 7200000000
# Disable app standby bucket enforcement
adb shell settings put global app_standby_enabled 0
# Disable phantom process killing
adb shell settings put global settings_enable_monitor_phantom_procs false
These settings do not persist across reboots and may be overwritten by Google Play Services config sync.
Suggested Fix for Fairphone
-
Enable app compaction (use_compaction=true) — This is the single most impactful change. AOSP enables this by default. Fairphone’s decision to disable it causes cached apps to consume significantly more RAM, leading to premature LMKD kills on a device with plenty of memory.
-
Review LMKD thresholds — The low memory killer is triggering with 4+ GB free RAM. The minfree thresholds should be reviewed for a 7.5 GB device.
-
Increase or remove mCustomizedMaxCachedProcesses=32 — This Fairphone-specific customization limits cached processes below AOSP defaults. On a device with 7.5 GB RAM, this is unnecessarily restrictive.
-
Increase CUR_MAX_EMPTY_PROCESSES — The limit of 30 is being hit constantly. Modern apps (especially those using WebView) spawn multiple processes, filling the 30 slots quickly.
Regression Information
- Working: Android 13 on the same Fairphone 4 — multitasking worked normally
- Broken: Android 15 (FP4.SREL.15.14.4) — background apps killed aggressively
- Same apps, same usage pattern — the only change was the OS update
How to Reproduce
- Use Fairphone 4 with Android 15 (FP4.SREL.15.14.4)
- Open 5–6 apps (browser, email, messaging, music player, etc.)
- Use the browser to start a login flow that requires 2FA
- Switch to an authenticator/OTP app to copy the code
- Switch back to the browser — observe it has been killed and the login page must reload
- Run
adb shell dumpsys activity exit-info <package> to confirm LOW_MEMORY or TOO_MANY_EMPTY_PROCS kill reason
How to Verify the Fix
After applying the ADB workarounds above:
adb shell dumpsys activity settings | grep -E "use_compaction|CUR_MAX"
Expected output:
use_compaction=true
CUR_MAX_CACHED_PROCESSES=64
CUR_MAX_EMPTY_PROCESSES=32
Then repeat the reproduction steps — apps should remain in memory during normal multitasking.
Impact
- All Fairphone 4 users on Android 15 are likely affected
- Basic multitasking (switching between 2–3 apps) is unreliable
- Music/audio playback interrupted when app process is killed
- Password managers killed mid-workflow, requiring re-authentication
- Authentication flows broken when browser is killed during 2FA
- No UI-accessible workaround exists — requires ADB
Related
- AOSP CachedAppOptimizer source (compaction enabled by default):
frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java