# ANR detection in InputDispatcher # 'ANR' means 'application not responding'. This is an event that gets triggered when the system thinks that an application is too slow to respond. A dialog may pop up because of an ANR event. ANRs can be triggered by multiple systems within Android. In InputDispatcher, ANRs are raised in 2 cases: 1. An event was sent to a connection, and the response was not received within a certain timeout. 2. The application did not have a focused window, and an input event that requires focus was generated by the user. Let's consider each of these cases. ## 1. Application does not respond to an input event that was sent to it. ## The most common case is when an application does not respond to input that dispatcher sent to it. Typically, it means an application is performing a long operation on its UI thread. When the event is being dispatched to an application, the normal flow is: `mPendingEvent` → `connection.outboundQueue` → `connection.waitQueue`. Every dispatch cycle, InputDispatcher will check all connections to see if any are unresponsive. To determine whether an app is not responding, we look at the oldest entry in the `waitQueue`. If the entry sits in the `waitQueue` past `entry.timeoutTime`, we trigger an ANR. ### Checking if a connection is unresponsive ### When a dispatch entry is sent to the app, its `deliveryTime` and `timeoutTime` fields are populated. The `deliveryTime` is the time that the event is delivered to the app. This is simply the current time inside `publishMotionEvent`. The `timeoutTime` is the time when this entry would be considered overdue. At that time, the ANR process would start for this connection. Most connections are associated with a window, and each window may have a custom timeout time. To calculate the timeout time of a specific event, simply add the `window.dispatchingTimeout` to the current time. In case where there is no associated window, such as gesture monitors, use the default dispatching timeout which is defined in `android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS`. The `timeoutTime` field of the `DispatchEntry` is needed because the window associated with a specific connection may change its timeout time. Therefore, entries sent prior to the timeout change would need to follow the previous timeout value. If a window timeout changes, it only affects new events being dispatched, and does not alter the timeout times of events already sent to the app. For example, if an application is being debugged, the ActivityManager may want to increase the timeout time for a window to prevent the ANR dialog from appearing or the app from getting killed. Looping through `waitQueue`s of all connections on every dispatch cycle could be costly. To improve this, we introduced the `AnrTracker` class. `AnrTracker` uses a multiset (a set that allows duplicate entries) to keep track of the next time a dispatch entry would become out of date. Duplicate entries are allowed because there may be two events with an identical timeout time. This is unlikely to happen in practice today, but is possible if the window timeouts are different or if the device has a high input report rate or a low clock resolution. On each dispatch cycle, InputDispatcher checks `AnrTracker` for the nearest timeout value. If the nearest timeout value is in the past, InputDispatcher will trigger the ANR for the corresponding connection. When an application sends a response for a particular dispatch entry, that entry is removed from the connection's `waitQueue`, and it is also removed from the `AnrTracker`. During normal operation, the entries are removed from `AnrTracker` quickly. ### How to test ### In order to test this behaviour, you can create an application that calls `SystemClock.sleep` while handling a click event. When this happens, the expectation is that the ANR dialog will come up within a short period of sending an input event (typically 5 seconds). While the app is not responding, it is expected that touches on other applications and gesture monitors still continue to work. ## 2. Application does not have a focused window, and a focused event comes in ## This is a legacy behaviour that we are maintaining inside InputDispatcher. When an application is launched, WindowManager calls `setFocusedApplication` to tell InputDispatcher that there is a focused application. This is used by InputDispatcher purely for ANR and debugging purposes. After launching, an application may not add a focused window. This could be either due to a bug in WindowManager or in the app. The legacy behaviour in this situation is as follows: touches will continue to function normally, without causing an ANR. If there is a focused event, however, it would require a focused window to be dispatched. InputDispatcher will keep this focused event inside mPendingEvent until: * A focused window is added * Timeout occurs * User touches another application To keep track of this timeout, when this situation is detected initially, `mInputTargetWaitTimeoutTime` and `mAwaitedFocusedApplication` are set. When the `mInputTargetWaitTimeoutTime` expires, an ANR will be raised. ### How to test ### Create an empty application that sets `FLAG_NOT_FOCUSABLE` on its window in `onCreate`. Touching the application's window should not cause an ANR. Sending a key event to the application, however, should cause ANR. One easy way to do this is by pressing or gesturing the BACK key. In this scenario, `adb shell dumpsys input` will reveal that there's no focused window in the current display. ## Extending the timeout based on the response from policy ## When the policy processes the ANR notification and responds with a positive timeout, InputDispatcher marks the connection as "responsive" by setting `inputPublisherBlocked = false`. All of the entries for this connection inside AnrTracker will be modified to expire at `time = (current time) + (timeout extension returned by policy)`. If the policy wants to abort dispatch, it returns a timeout value of 0. In this case, InputDispatcher will synthesize cancel events for the connection. When an app is unresponsive, new touches do not go to the app. They get dropped with a warning log. This is done to prevent overwhelming the app with events in case it later becomes responsive.