You can also preorder the book by emailing preorder@NewAndroidBook.com.
Note some links (to other chapters in the book) will not work in this file (since it is partial), but external links will. Feedback, questions and requests are always welcome.
Chapter XII - The Android Input Architecture
Most users interact with their device through its touch screen. Android, however, can handle input from multiple input sources, of which the touch screen is only one. Additional sources may be a physical keyboard, a mouse, joystick or other controller, and virtually any other HID (Human Interface Devices), or even device sensors. Android provides a surprisingly elaborate stack structure, which receives input from the lower level kernel driver, translates the raw input data into a more manageable higher level event (key press, gesture, and such), and then propagates that to the active view in the foreground activity, by (eventually) invoking the callback associated with the view. On the way, Android uses both native level and Java level components. The high level view is shown in figure 11-1:
This chapter explores the Android input stack. We'll take a bottom-up approach, starting with the lower layer - provided by the device driver and the Linux kernel, through the runtime frameworks, conversion to an event, and finding the correct handler for that event. Be warned: The journey up the stack is long and arduous. But understanding it will hopefully make you gain new appreciation to just what happens when you next touch your device.
Input Handling at the Linux Kernel Layer
Interrupt Handling
An input event begins with an interrupt, which is generated when the device (touch screen, physical button, headphone jack, etc) detects the physical event. At the lowest level, this event causes electrical current, which is converted to an interrupt, which is delivered via the interrupt controller to the CPU, on one of the many interrupt request lines (IRQs).
Linux provides a default interrupt handler in the form of do_IRQ
. This function checks if any device drivers have "claimed" the interrupt (by calling request_irq()
). Usually, each driver will claim the IRQ line corresponding to its device, though it is not uncommon to see interrupt lines shared between two or more drivers. A driver claiming an IRQ also supplies a callback - often referred to as an ISR (Interrupt Service Routine), which will be invoked by do_IRQ
when an interrupt is received on the claimed IRQ.
You can view the interrupt lines claimed by drivers if you look at
In other cases, however, like the HTC One M8, the physical keys are all grouped under one interrupt, gpio_keys
. Another interesting feature is that on multi-core CPUs, you will actually see multiple interrupt count columns when additional cores are active. You can further control which core responds to which interrupt by writing a hexadecimal mask value to the
You can empirically determine which IRQs are associated with which devices by viewing
Interrupts are effectively handled at the highest possible priority. With rare exceptions, an interrupt will preempt whichever thread is executing on the CPU, as it demands the kernel's immediate attention. Desktop systems make use of this fact in some cases (for example, with the SysRQ mechanism), though a more immediate application is that, when a device is hanging, most users intuitively press buttons, tap, swipe, or otherwise try to "wake up" the device. In practice, this can, in theory, be detrimental, as more events trigger more interrupts, which can increase the system load by preempting what the CPU was doing, in favor of handling otherwise meaningless gestures. In practice, however, interrupt handling is so quick the load they introduce is fairly minimal.
Well behaved drivers obey the strict requirements of handling IRQs - they never block, and take as little time as possible, deferring work toa software IRQ (the kernel's ksoftirq
threads). Interrupts therefore usually have minimal effects on the system. In addition some devices can use tricks such as interrupt coalescing (firing one interrupt instead of several), and devices will not fire interrupts when the device is sleeping (for example, the touch screen, when the display is turned off).
The Linux Input Driver Model
Android uses the standard Linux input driver model, introduced back in 2.4 and standardized in 2.6 and later. The Linux kernel documentation contains a subdirectory documenting the model and usage of its programming interfaces (
A device driver responsible for an input device is required to allocate an input_dev
structure and populate its capabilties. This structure is defined in <linux/input.h>
and shown in figure figInputDev, along with the ioctl(2)
codes which can be used to retrieve selected fields from user mode:
field | contains | ioctl(2) code |
---|---|---|
name | device display name | EVIOCGNAME |
phys | device physical path in /sys | EVIOCGPHYS |
uniq | unique code, if any | EVIOCGUNIQ |
id | struct input_id | |
propbit | device properties and quirks | EVIOCGPROP |
evbit | EV_ event types supported by device | |
keybit | keys/buttons this device has | EVIOCGBIT(EV_KEY..) |
relbit | relative axes for the device | EVIOCGBIT(EV_REL..) |
absbit | absolute axes for the device | EVIOCGBIT(EV_ABS..) |
mscbit | miscellaneous events supported by device | EVIOCGBIT(EV_MSC..) |
ledbit | LEDs present on the device | EVIOCGBIT(EV_LED..) |
sndbit | sound effects supported by device | EVIOCGBIT(EV_SND..) |
ffbit | supported force feedback effects, if any | EVIOCGBIT(EV_FF..) |
swbit | switches present on the device | EVIOCGBIT(EV_SW..) |
hint_events_per_packet | average # of events generated by device | |
keycodemax | size of keycode table | |
keycodesize | size of elements in keycode table | |
keycode | map of scancodes to keycodes for device | |
getkeycode | (legacy) retrieve current keymap. | |
ff | Force-Feedback, if any | |
repeat_key | Last pressed key, for auto-repeat | |
timer | auto-repeat timer | |
rep | auto-repeat parameters | |
mt | struct input_mt holding Multitouch state | |
absinfo | Absolute axes coordinate information | |
key | current state of device keys/buttons | EVIOCGKEY |
led | current state of device LEDs, if any | EVIOCGLED |
sw | current state of device switches, if any | EVIOCGSW |
open | callback for open(2) on device | |
close | callback for close(2) on device | |
flush | flush device events,e.g. force-feedback | |
event | handler for events sent to device |
<linux/input.h>
)The input driver registers its input_dev
s with the kernel input manager by a call to input_device_register
. Doing so will automatically create a sysfs entries (symbolic links) for the device in evdev_fops file_operations
structure (in <drivers/input/evdev.c>
), with generic blocking implementations for read, write, poll, etc.
As shown in the figure, the key fields of the input_dev
structure are accessible via standard ioctl(2)
calls with specific EVIOC*
constants. Additionally, the capabilties and properties of a device are exported to user mode via entries in sysfs, under /sys/class/input/event##/device/capabilities/ and /sys/class/input/event##/device/properties.
The Linux device drivers respond to interrupts, generated by the respective devices. The drivers then report the events using the input_report_[key/...]
functions, and events are then queued onto the /dev/input/event## device as structs containing the timestamp, event type, associated code and value. User mode applications use the standard system calls (that is read(2)
, select(2)
/poll(2)
and the like) to retrieve events (always an integer multiple of sizeof(input_event)
) from the device. The supported event types are defined in <input/event.h>
and shown in table 11-evttypes:
# | Event code | Specifies |
---|---|---|
0x00 | EV_SYN | Separate/synchronize other events (e.g. SYN_REPORT/SYN_MT_REPORT ), or report events lost (SYN_DROPPED ) |
0x01 | EV_KEY | Key press (KEY_* ) or touch (BTN_TOUCH ) |
0x02 | EV_REL | Relative changes to a property. Changes relayed through REL_[XYZ] values. |
0x03 | EV_ABS | Absolute coordinates for an event. Values are usually ABS_[XYZ] , or ABS_MT for multi-touch |
0x04 | EV_MSC | Miscellaneous codes |
0x05 | EV_SW | Binary switches. E.g. SW_JACK_PHYSICAL_INSERT for headphone insertion |
0x11 | EV_LED | Used for device LEDs, if any |
0x12 | EV_SND | Used for sound devices |
0x14 | EV_REP | Used for auto-repeating events |
0x15 | EV_FF | Used for force-feedback capable devices (e.g. joysticks). An EVIOCSFF ioctl may be used to upload force feedback effects |
0x16 | EV_PWR | Reserved for power events. Largely unused |
0x17 | EV_FF_STATUS | Used for force-feedback capable devices. |
Naturally, not all input devices support all event classes; The input_dev
structure maps the various events a particular device supports by its bitmaps, and the device's sysfs entry makes those bitmaps visible in user mode as
Note, that even though the Linux input model was designed for HID type devices, it can be used on virtually any devices, including sensors. This is shown in the following experiment.
You can examine input events yourself through the shell. First, look at the files in
root@generic:/ # ls -l /dev/input
crw-rw---- root input 13, 64 2013-11-15 18:24 event0
crw-rw---- root input 13, 63 2013-11-15 18:24 mice
crw-rw---- root input 13, 32 2013-11-15 18:24 mouse0
Output on a real device will likely be different, as these have many more input channels, mapped to its various sensors; The Samsung S3, for example, has event0 through event13, and no "mouse0". The major and minor numbers, however, should be the same, and the major (13) is associated with the kernel input driver subsystems (as can be verified with grep input /proc/devices
).
The system keeps track of all devices quite conveniently in
shell@htc_m8wl:/ $ cat /proc/bus/input/devices
I: Bus=0000 Vendor=0000 Product=0000 Version=0000
N: Name="h2w headset"
P: Phys=
S: Sysfs=/devices/virtual/input/input6
U: Uniq=
H: Handlers=kbd event6 keychord
B: PROP=0
B: EV=3
B: KEY=84 10000 138 0 e0800 0 0 0
..
To see the properties and capabilities of a given device, say
## File sizes are reported as 4k (pagesize) arbitrarily - all virtual anyway
shell@generic:/sys/class/input/event0$ ls -l
-r--r--r-- root root 4096 2013-11-15 18:24 dev
lrwxrwxrwx root root 2013-11-15 18:24 device -> ../../input0
drwxr-xr-x root root 2013-11-15 18:24 power
lrwxrwxrwx root root 2013-11-15 18:24 subsystem -> ../../../../../class/input
-rw-r--r-- root root 4096 2013-11-15 18:24 uevent
root@generic:/sys/class/input/event0 # cat uevent
MAJOR=13
MINOR=64
DEVNAME=input/event0
By accessing the device nodes, you can treat the input sources as files, and by using a command such as "cat" on them, dump raw events directly to standard output, though those would be appear to be nothing more than synchronized garbage. A better way would be to use the
When used as getevent_main()
, which uses Linux's inotify to enumerate the files in ioctl
s from the previous table to obtain the device information. Finally, it adds the device to an array of file descriptors which it polls continuously. As each device (event source) signals input,
-
-i : Show HID codes and other information -
-l : Show numbers in human readable constant form (e.g. "0001" as KEY_ESC) -
-p : Show device capabilities (as per sysfs capabilities file) and exit -
-t : Show timestamps -
-h : Show help on all switches (-n, -t, -s, -S, -v, -d, -p, -i, -l, -q, -c and -r) then exit
Armed with this information, you can conduct this simple experiment: Make sure your display isn't sleeping, then run
shell@generic:/$ getevent -l
could not get driver version for /dev/input/mouse0, Not a typewriter
add device 1: /dev/input/event0
name: "qwerty2"
could not get driver version for /dev/input/mice, Not a typewriter
/dev/input/event0: EV_ABS ABS_X 00000083 # X position of touch
/dev/input/event0: EV_ABS ABS_Y 00000129 # Y position of touch
/dev/input/event0: EV_KEY BTN_TOUCH DOWN # Touch start
/dev/input/event0: EV_SYN SYN_REPORT 00000000 # Event "terminator"
/dev/input/event0: EV_KEY BTN_TOUCH UP # Touch end
/dev/input/event0: EV_SYN SYN_REPORT 00000000 # Event "terminator"
On a real device (for example, the Samsung S3) you'll see much more output, owing to the myriad input sources it has. The msm8960 sensors, for example, are what enables headphone vendors to control music playing and call answering through the headphone jack.
shell@s3:/$ getevent -l
## getevent will display all enumerated devices:
add device 1: /dev/input/event6 name: "sec_touchscreen"
add device 2: /dev/input/event8 name: "barometer_sensor"
add device 3: /dev/input/event4 name: "sec_jack"
add device 4: /dev/input/event3 name: "light_sensor"
add device 5: /dev/input/event2 name: "proximity_sensor"
add device 6: /dev/input/event0 name: "sii9234_rcp"
add device 7: /dev/input/event12 name: "msm8960-snd-card Headset Jack"
add device 8: /dev/input/event11 name: "msm8960-snd-card Button Jack"
add device 9: /dev/input/event10 name: "msm8960-snd-card Volumeup Jack"
add device 10: /dev/input/event9 name: "msm8960-snd-card Volumedown Jack"
add device 11: /dev/input/event13 name: "sec_touchkey"
add device 12: /dev/input/event1 name: "fsa9485"
add device 13: /dev/input/event5 name: "sec_keys" # physical keys
add device 14: /dev/input/event7 name: "sec_powerkey" # power button - KEY_POWER
# These are sent regularly from the light sensor, as it's highly sensitive
/dev/input/event3: EV_REL REL_X 00000020
/dev/input/event3: EV_REL REL_Y 00000021
/dev/input/event3: EV_REL REL_Z 00000015
/dev/input/event3: EV_REL REL_MISC 00000024
/dev/input/event3: EV_SYN SYN_REPORT 00000000
## Touch event: Notice ABS_MT is used here, rather than BTN_TOUCH
/dev/input/event6: EV_ABS ABS_MT_TRACKING_ID 00000043 # unique ID for this touch
/dev/input/event6: EV_ABS ABS_MT_WIDTH_MAJOR 0000000a # major axis of approaching ellipse
/dev/input/event6: EV_ABS ABS_MT_POSITION_X 0000011f # center x touch position
/dev/input/event6: EV_ABS ABS_MT_POSITION_Y 0000022d # center y touch position
/dev/input/event6: EV_ABS ABS_MT_TOUCH_MAJOR 00000010 # major axis of touch ellipse
/dev/input/event6: EV_ABS ABS_MT_TOUCH_MINOR 0000000c # minor axis of touch ellipse
/dev/input/event6: EV_ABS ABS_MT_TOOL_X ffffffc4 # center y tool position
## Physical buttons: HOME and VOLUMEDOWN together (notice /dev/input/event5)
/dev/input/event5: EV_KEY KEY_VOLUMEDOWN DOWN
/dev/input/event5: EV_SYN SYN_REPORT 00000000
/dev/input/event5: EV_KEY KEY_HOMEPAGE DOWN
/dev/input/event5: EV_SYN SYN_REPORT 00000000
/dev/input/event5: EV_KEY KEY_VOLUMEDOWN UP
/dev/input/event5: EV_SYN SYN_REPORT 00000000
/dev/input/event5: EV_KEY KEY_HOMEPAGE UP
/dev/input/event5: EV_SYN SYN_REPORT 00000000
sendevent
As it so happens, toolbox also has a sendevent
tool which you can use to simulate events at the lowest level, by writing directly to the input_event
from the command line, and calling write(2)
to send it to the device node. The caller needs write access to the device node - which the shell provides for you even on non-rooted device thanks to its group membership in the
Using this tool alongside
- While
getevent spits out the events in hexadecimal,sendevent will expect decimal input. So you will have to do the conversion yourself (or use a simple script to do so) - Most events, though atomic to the user, are broken up into multiple components, and require the
SYN_REPORT
synthetic event to act as a delimiter. It's therefore important to send that event at the end of the sequence.
# simulate EV_KEY KEYHOMEPAGE DOWN followed by REPORT
shell@s3$ sendevent /dev/input/event5 1 172 1; sendevent /dev/input5 0 0 0
# To simulate home button hold, delay the following line, simulating the UP/REPORT
shell@s3$ sendevent /dev/input/event5 1 172 0; sendevent /dev/input5 0 0 0
As we continue traversing up the input stack, the next experiment you encounter will introduce you to yet another method of simulating input events.