When an ANR surfaces in Android Vitals, a bug report, or viaDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/AmolPardeshi99/android-performance-skills/llms.txt
Use this file to discover all available pages before exploring further.
ApplicationExitInfo, a structured analysis workflow helps you quickly determine whether the root cause is in your app code or in the system (OEM freeze policy, Binder thread pool pressure, memory overload). This page walks through the complete triage process.
Six-step ANR triage process
Locate the exact ANR time
Search the EventLog for
am_anr — this timestamp is the most accurate marker of when the ANR was triggered.Read the ANR reason from MainLog / SystemLog
Search for
"ANR in" to find:- Time of ANR
- Process name and PID
- Reason string (e.g.,
Input dispatching timed out,Broadcast of Intent) - CPU load averages (1/5/15 min)
- CPU usage per process, before and during the ANR
- Memory pressure (PSI)
Assess system state at ANR time
Before reading thread stacks, rule out system-wide causes:
- Was CPU load more than 3× the device core count? (system-wide overload)
- Was
system_serverorsurfaceflingerconsuming >50%? - Were there
lowmemorykillerevents? Waskswapd0running continuously? - Was
iowaithigh? (check major page fault counts in the CPU log) - Were other unrelated apps being killed (
am_kill,am_proc_died)?
Read the thread stacks in the ANR trace file
Open the ANR trace file and find the
"main" thread entry:- Is the main thread
Blocked,TimedWaiting, orNative(normal idle)? - If
Blocked: which lock object? Which thread holds it? - If
Native: is it a Binder call? Which system service?
If the main thread shows
Native state with nativePollOnce in the stack (idle Looper), the app was not executing any work when the ANR fired. This is a strong signal of a system-induced ANR, not an app bug.Collect the context window (5 seconds before am_anr)
Combine EventLog, MainLog, and SystemLog into one file. Search the 5-second window before the
am_anr timestamp for:Slow operation,Slow dispatch,Slow deliverydvm_lock_sample- App launches, screen on/off transitions
- OEM freeze/unfreeze log lines (
HansManager,RefrigeratorManager) - Lock release events that would explain why the main thread was finally unblocked
If inconclusive: add tracing or reproduce locally
When the thread stacks and CPU log do not point to a clear root cause:
- Enable
StrictModewithpenaltyDeath()in a debug build to hard-fail on the first violation - Capture a Perfetto trace to correlate timeline events with thread activity
- Use
ApplicationExitInfo.traceInputStreamto upload the full tombstone trace to your observability backend
Reading thread state in traces.txt / anr_* files
Pulling the trace files
Normal idle main thread
A healthy main thread waiting for work looks like this. ThenativePollOnce call means the Looper is blocked in epoll_wait, which is the correct idle state — not a problem:
Blocked main thread (app bug)
When the main thread is waiting for a monitor lock held by another thread, the trace showsBlocked state with an explicit lock reference:
held by thread 34 reference. Find thread 34 in the same trace file to read its stack and determine what it is doing while holding the lock.
Thread state reference
| State in trace | Java Thread.State | Meaning |
|---|---|---|
Native | RUNNABLE | Executing JNI or waiting in Looper epoll (normal idle) |
Runnable | RUNNABLE | Actively executing Java code |
Blocked | BLOCKED | Waiting for a monitor lock held by another thread |
Waiting | WAITING | In Object.wait() with no timeout |
TimedWaiting | TIMED_WAITING | In Object.wait(timeout) or Thread.sleep() |
kWaitingForGcToComplete | WAITING | GC pause — indicates GC-pressure ANR |
CPU usage log interpretation
The"ANR in" log section prints CPU data over two time windows. Both windows are needed to understand whether the ANR was sudden or building over time:
Load average line
Page faults
Each process entry reportsminor and major page fault counts:
- Minor faults — the page was already in memory cache. This is normal memory access activity and not a concern.
- Major faults — the page had to be read from disk. A high
majorcount means the process is triggering disk I/O and is a potential ANR contributor. Highmajorcounts onsystem_serverorkswapd0indicate memory pressure driving the ANR.
Distinguishing app bug vs system-induced ANR
Strong signals the ANR is an application bug
- Main thread
Blockedon a lock your own code created - Main thread inside a
synchronized {}block that is doing I/O or waiting for a sub-thread result - Main thread stack shows your code in a Binder call to a service you host
- CPU usage of your process is very high before the ANR (tight loop, heavy computation)
- No system-wide CPU overload or memory pressure is visible in the log
Strong signals the ANR is system-induced
- Main thread
Native(idle Looper) — a message dispatch simply never arrived from the system - OEM freeze/unfreeze log lines appear around the ANR time (e.g.,
HansManager,RefrigeratorManager) system_serverorsurfaceflingerCPU usage is extremely high- High
majorpage faults onsystem_server, orkswapd0continuously active - Multiple unrelated apps are ANR-ing in the same time window
- Load average is well above the device core count for a sustained period
Real-world pattern: lock inversion deadlock
One of the most common production ANR patterns is a lock inversion deadlock between the main thread and a background worker. The main thread calls into a utility (logging, image cache, analytics) that holds Lock A and tries to acquire Lock B, while a background thread holds Lock B and tries to acquire Lock A. The ANR trace signature for this pattern:EventLogger lock that the main thread needs, and is itself waiting on an AtomicInteger inside DatabaseHelper. This circular dependency cannot resolve on its own.
- Never hold a lock while calling into a system or third-party API
- Use a
limitedParallelism(1)dispatcher instead of nestedsynchronized {}blocks - In crash and log handlers, avoid any
synchronizedcalls that may contend with worker threads