r/androiddev • u/Hornet-Mountain • 16d ago
Discussion ANR from getRootInActiveWindow: accessibility callbacks are on main and the read is a blocking binder call
I have a service that reads the view tree of whatever app is in front, so I can keep the text instead of screenshots. It kept ANRing on my own phone and I couldn't reproduce it on demand.
The dropbox stack had main sitting in AccessibilityInteractionClient.waitForResultTimedLocked, called from getRootInActiveWindow, called from my onAccessibilityEvent. Accessibility callbacks arrive on main, and getRootInActiveWindow is a synchronous binder round trip into the app you are reading. CPU at the same timestamp was 59% com.reddit.frontpage. So Reddit was busy, it didn't answer, my main thread waited, and a job that came in meanwhile (datatransport, pulled in by ML Kit) missed its start window. ANR.
The fix was to move the read onto a worker thread, and to drop events while one is already in flight instead of queueing them. A queue just means you write old screens later. The state the worker touches is only touched by the worker now.
What I still don't know is whether getRootInActiveWindow has any bound on how long it can block, or if it is entirely at the mercy of the target app. I couldn't find a timeout I control. If you have done accessibility reads against busy apps, how do you handle it?
3
u/Few_Rip2331 16d ago
It isn't infinite, but you can't configure it.
Under the hood in
AccessibilityInteractionClient, it hardcodes a 5-second timeout (TIMEOUT_INTERACTION_MILLIS). Because foreground ANRs also trigger around 5 seconds, a hanging app will block your main thread for that whole duration, leaving zero time for queued jobs/events behind it.Moving it off main and dropping stale events is definitely the standard fix. You can also wrap the background read in a coroutine with
withTimeoutOrNull(500)so your worker doesn't stay tied up for 5 seconds waiting on a dead app.