Android's App Standby Buckets (since Android 9) classify every app by how recently it was used, and gate how often its background jobs (WorkManager / JobScheduler) may actually run — independent of how often the app asks to run one. This sim compresses the real multi-day timescale into hours so the whole ladder plays out live.
| Bucket | Idle threshold | Max deferred window |
| Active | just opened | 0 h (immediate) |
| Working Set | < 8 h idle | 2 h |
| Frequent | < 24 h idle | 8 h |
| Rare | < 48 h idle | 24 h |
| Restricted | ≥ 48 h idle | quota-capped (3 / 24 h) |
A requested job is placed in a pending queue the moment the interval elapses. The OS only lets the oldest pending job execute once the time since the last execution reaches that bucket's deferred window:
canRun = (simClock − lastExecTime) ≥ deferredWindow(bucket) × dozeFactor
Restricted also caps total runs to a rolling 24h quota, regardless of window.
Toggling Battery Saver / Doze multiplies every deferred window by 3× (Doze's real maintenance windows back off exponentially while the device is stationary and screen-off — this sim uses one flat multiplier as a readable stand-in). The result: an app the user opens often stays in Active/Working Set and its sync/refresh jobs run close to on schedule; an app nobody has opened in two days lands in Restricted and most of its requested jobs simply never execute — the real-world reason "background sync" silently stops working for apps a user abandons.