device_gamepad_state
Present a connected game controller to an Android device and set its state. The device sees a REAL controller — a kernel-level input device that native games and apps receive exactly as they would a pad plugged into the phone.
HOW TO FIRE INPUT PROPERLY:
• One call = ONE frame = one instant in time. A button stays pressed until you send a frame WITHOUT it, so every press needs a matching release frame — keydown then keyup. Press after press just holds them all down.
• For anything that should look played rather than stepped — mashing, combos, a stick sweep — pass frames instead of calling repeatedly. The sequence plays out device-side at intervalMs (default 33 ms ≈ 30 fps). A round trip per frame cannot reach that cadence, so a rapid sequence built from single calls always reads as held buttons.
• Alternate press and release inside frames: [{buttons:[1]}, {}, {buttons:[1,1]}, {}] is tap A, release, tap A+B, release.
• Axes are [leftX, leftY, rightX, rightY], -1..1. Sweep them across frames to roll a stick; omitted axes read as centred.
• Confirm with device_gamepad_status.
Buttons and axes use W3C standard order, identical to the iOS gamepad tools — the mapping onto whatever the chosen controller reports is done for you. Pick profile to match the hardware you want the device to believe is attached; it changes both the reported identity and the button layout, so a game showing on-screen prompts shows the right ones. This is the tool to reach for. device_uhid_gamepad_state is the raw twin — it takes the fixed-format report directly (one profile, rotated bit order) instead of translating from W3C arrays; use it only to send an exact report byte-for-byte.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| axes | No | Axis values -1..1: [leftX, leftY, rightX, rightY]. Omitted axes read as centred. | |
| udid | Yes | Device serial number (UDID) | |
| frames | No | Play a SEQUENCE of frames at intervalMs instead of setting one state. This is how you produce real gameplay input: include the release frames (an entry with no buttons releases everything). Capped at 300 frames. Overrides buttons/axes when present. | |
| buttons | No | Button values 0..1 in W3C standard order (0-3 face, 4/5 shoulders, 6/7 analog triggers, 8 select, 9 start, 10/11 stick clicks, 12-15 d-pad, 16 home, 17 touchpad). >= 0.12 counts as pressed. Omitted releases everything. | |
| profile | No | Which controller the device should believe is attached (default dualshock4). Changing it between calls unplugs and re-attaches the pad. | |
| intervalMs | No | Gap between frames in ms (default 33). Lower = faster mashing; 33 ms matches a 30 fps controller poll. | |
| browserTouchpad | No | Also expose the touchpad click (index 17) to a web page open on the device. Off by default. The pad is a REAL input device here, and Chromium's Android mapper fills indices 0-16 only, so index 17 reaches the kernel and native apps but can never reach a browser gamepad tester on its own — not even from a physically-plugged DualShock. This overlays it onto the page. Indices 0-16 still come from the real pad; only 17 is synthesized. Needs an inspectable page open; ignored if there is none. After a navigation the page only exposes a pad once it sees activity, and the touchpad alone cannot provide it — lead with a stick sweep, e.g. frames [{axes:[1,0,0,0]},{axes:[0,0,0,0]},{buttons:[...17]}], which wakes it without adding a button to a tester's history. |