# Batched consumption #

Most apps draw once per vsync. Therefore, apps can only respond to 1 input event per frame. If multiple input events come in during the period of 1 vsync, it would be wasteful to deliver them all at once to the app. For this reason, input events are batched to only deliver 1 event per frame to the app.

The batching process works in the following manner:

1. `InputDispatcher` sends an event to the app
2. The app's `Looper` is notified about the available event.
3. The `handleEvent` callback is executed. Events are read from fd.
4. If a batched input event is available, `InputConsumer::hasPendingBatch` returns true. No event is sent to the app at this point.
5. The app is notified that a batched event is available for consumption, and schedules a runnable via the `Choreographer` to consume it a short time before the next frame
6. When the scheduled runnable is executed, it doesn't just consume the batched input. It proactively tries to consume everything that has come in to the socket.
7. The batched event is sent to the app, along with any of the other events that have come in.

Let's discuss the specifics of some of these steps.

## 1. Consuming events in `handleEvent` callback ##

The app is notified about the available event via the `Looper` callback `handleEvent`. When the app's input socket becomes readable (e.g., it has unread events), the looper will execute `handleEvent`. At this point, the app is expected to read in the events that have come in to the socket. The function `handleEvent` will continue to trigger as long as there are unread events in the socket. Thus, the app could choose to read events 1 at a time, or all at once. If there are no more events in the app's socket, handleEvent will no longer execute.

Even though it is perfectly valid for the app to read events 1 at a time, it is more efficient to read them all at once. Therefore, whenever the events are available, the app will try to completely drain the socket.

To consume the events inside `handleEvent`, the app calls `InputConsumer::consume(.., consumeBatches=false, frameTime=-1, ..)`. That is, when `handleEvent` runs, there is no information about the upcoming frameTime, and we dont want to consume the batches because there may be other events that come in before the 'consume batched input' runnable runs.

If a batched event comes in at this point (typically, any MOVE event that has source = TOUCHSCREEN), the `consume` function above would actually return a `NULL` event with status `WOULD_BLOCK`. When this happens, the caller (`NativeInputEventReceiver`) is responsible for checking whether `InputConsumer::hasPendingBatch` is set to true. If so, the caller is responsible for scheduling a runnable to consume these batched events.

## 2. Consuming batched events ##

In the previous section, we learned that the app can read events inside the `handleEvent` callback. The other time when the app reads events is when the 'consume batched input' runnable is executed. This runnable is scheduled via the Choreographer by requesting a `CALLBACK_INPUT` event.

Before the batched events are consumed, the socket is drained once again. This is an optimization.

To consume the events inside 'consume batched input' runnable, the app calls `InputConsumer::consume(.., consumeBatches=true, frameTime=, ..)`. At this point, the `consume` function will return all batched events up to the `frameTime` point. There may be batched events remaining.

## 3. Key points ##

Some of the behaviours above should be highlighted, because they may be unexpected.

1. Even if events have been read by `InputConsumer`, `consume` will return `NULL` event with status `WOULD_BLOCK` if those events caused a new batch to be started.

2. Events are read from the fd outside of the regular `handleEvent` case, during batched consumption.

3. The function `handleEvent` will always execute as long as there are unread events in the fd

4. The `consume` function is called in 1 of 2 possible ways:
   - `consumeBatches=false, frameTime=-1`
   - `consumeBatches=true, frameTime=`

   I.e., it is never called with `consumeBatches=true, frameTime=-1`.