What Is OTG Mode in scrcpy and How Does It Work Without USB Debugging?
OTG mode is a special scrcpy operation mode that controls an Android device using keyboard, mouse, and gamepad input without requiring ADB or enabled USB debugging, utilizing the Android Open Accessory (AOA) protocol instead.
OTG mode in the Genymobile/scrcpy repository provides a "headless" control solution for scenarios where enabling USB debugging is impossible or undesirable. Unlike standard scrcpy operation that streams video and audio via ADB, OTG mode creates only a minimal SDL window to capture host input events and forwards them to the device as USB HID reports.
How OTG Mode Works in scrcpy
The implementation relies on a direct USB accessory connection rather than the Android Debug Bridge. The workflow involves four distinct phases orchestrated through the USB stack in app/src/usb/scrcpy_otg.c.
USB Device Selection and AOA Handshake
First, scrcpy scans the USB bus to locate the target device. It opens the device using its own USB abstraction layer (sc_usb_* functions) rather than relying on ADB daemon availability.
/* From app/src/usb/scrcpy_otg.c lines 24-33 */
// USB device selection and opening
ok = sc_usb_init(&s->usb);
if (!ok) goto end;
ok = sc_usb_select_device(&s->usb, serial);
if (!ok) goto end;
Once the USB device is open, scrcpy initiates the Android Open Accessory handshake. The sc_aoa_init function establishes the AOA connection, and sc_aoa_start opens the accessory interface:
/* From app/src/usb/scrcpy_otg.c lines 39-44 & 80-84 */
ok = sc_aoa_init(&s->aoa, &s->usb, NULL);
if (!ok) goto end;
ok = sc_aoa_start(&s->aoa);
if (!ok) goto end;
HID Device Registration
After establishing the AOA connection, scrcpy registers Human Interface Device (HID) interfaces for each requested input type. This registration occurs in app/src/usb/keyboard_aoa.c, app/src/usb/mouse_aoa.c, and app/src/usb/gamepad_aoa.c.
For keyboard input, sc_keyboard_aoa_init sends an HID "open" descriptor via sc_aoa_push_open and initializes the HID state structure:
/* From app/src/usb/keyboard_aoa.c lines 65-77 */
bool sc_keyboard_aoa_init(struct sc_keyboard_aoa *kb, struct sc_aoa *aoa) {
// Send HID open descriptor to register keyboard interface
if (!sc_aoa_push_open(aoa, SC_HID_KEYBOARD, SC_HID_ID_KEYBOARD)) {
return false;
}
// Initialize HID keyboard state
sc_hid_keyboard_init(&kb->hid);
kb->aoa = aoa;
return true;
}
Similar initialization occurs for mouse and gamepad devices through sc_mouse_aoa_init and sc_gamepad_aoa_init.
SDL Event Loop and Input Forwarding
The final component is the minimal SDL environment created in app/src/usb/screen_otg.c. Unlike standard scrcpy which creates a video display window, OTG mode initializes only SDL_INIT_EVENTS and creates a small control window without video rendering.
The sc_screen_otg_handle_event function captures all SDL input events and dispatches them to the appropriate AOA processor:
/* From app/src/usb/screen_otg.c lines 62-84 */
static void sc_screen_otg_handle_event(struct sc_screen_otg *screen, SDL_Event *event) {
switch (event->type) {
case SDL_KEYDOWN:
case SDL_KEYUP:
if (screen->keyboard) {
sc_keyboard_aoa_process_key(screen->keyboard, &event->key);
}
break;
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
if (screen->mouse) {
sc_mouse_aoa_process_mouse(screen->mouse, &event);
}
break;
// ... gamepad handling
}
}
When keyboard events occur, sc_keyboard_aoa_process_key (defined in app/src/usb/keyboard_aoa.c) translates SDL keycodes into HID reports and sends them via sc_aoa_push_input_with_ack_to_wait:
/* From app/src/usb/keyboard_aoa.c */
static void sc_key_processor_process_key(struct sc_key_processor *kp,
const struct sc_key_event *event,
uint64_t ack_to_wait) {
if (event->repeat) return; // HID repeats handled by Android
struct sc_keyboard_aoa *kb = container_of(kp, struct sc_keyboard_aoa, key_processor);
struct sc_hid_input hid_input;
if (sc_hid_keyboard_generate_input_from_key(&kb->hid, &hid_input, event)) {
sc_aoa_push_input_with_ack_to_wait(kb->aoa, &hid_input, ack_to_wait);
}
}
Why OTG Mode Does Not Require USB Debugging
Standard scrcpy operation relies on the Android Debug Bridge (ADB) protocol, which requires the "USB debugging" developer option to be enabled on the target device. ADB runs as a daemon on the device and communicates over a specific USB interface.
OTG mode bypasses ADB entirely by utilizing the Android Open Accessory (AOA) protocol. When a device supports AOA (Android 4.1+), it can recognize connected USB hosts as accessories and communicate with them through standard USB endpoints. Since AOA operates at the USB accessory level rather than the Android debug level, it does not require the USB debugging option to be enabled.
The input injection occurs at the system level through HID reports, which Android processes exactly as it would physical keyboard or mouse events from a real USB peripheral.
Running scrcpy in OTG Mode
To activate OTG mode, use the --otg flag. This creates a minimal control window without video or audio streaming.
# Basic OTG mode (keyboard + mouse)
scrcpy --otg
# Specify device by serial when multiple devices are connected
scrcpy --otg -s 0123456789abcdef
# Disable keyboard input (mouse only)
scrcpy --otg --keyboard=disabled
# Disable mouse input (keyboard only)
scrcpy --otg --mouse=disabled
# Enable gamepad support via AOA
scrcpy --otg --gamepad=aoa
# Or use the short form:
scrcpy -G --otg
The OTG window remains small and minimal since it only serves to capture host input events. All processing occurs through the AOA HID interfaces registered during initialization.
Key Source Files and Implementation Details
The OTG functionality is implemented across several specialized files in the app/src/usb/ directory:
-
app/src/usb/scrcpy_otg.c– Orchestrates the entire OTG workflow, including USB device selection (sc_usb_select_device), AOA initialization (sc_aoa_init), and HID device setup. -
app/src/usb/screen_otg.c– Creates the minimal SDL window and handles the event loop (sc_screen_otg_handle_event) that captures keyboard, mouse, and gamepad events. -
app/src/usb/keyboard_aoa.c– Implements the HID keyboard interface over AOA, including initialization (sc_keyboard_aoa_init) and key event processing (sc_key_processor_process_key). -
app/src/usb/mouse_aoa.c– Provides the HID mouse interface through AOA protocol functions. -
app/src/usb/gamepad_aoa.c– Handles optional gamepad support via AOA HID reports. -
doc/otg.md– Official documentation covering user-facing OTG mode options and usage examples.
These components collectively enable scrcpy to function as a USB HID accessory controller, completely bypassing the ADB infrastructure required for standard screen mirroring.
Summary
- OTG mode allows scrcpy to control Android devices via keyboard, mouse, and gamepad without enabling USB debugging or using ADB.
- The implementation uses the Android Open Accessory (AOA) protocol to register HID devices directly over USB.
- Input events are captured through a minimal SDL window and translated into HID reports that Android processes as physical peripheral input.
- Key source files include
app/src/usb/scrcpy_otg.cfor orchestration andapp/src/usb/keyboard_aoa.cfor HID input processing. - Activate OTG mode with the
--otgflag, optionally combining with--keyboard,--mouse, or--gamepad=aoaflags.
Frequently Asked Questions
Does OTG mode work on all Android devices?
OTG mode requires Android 4.1 (API level 16) or higher with support for the Android Open Accessory (AOA) protocol. Most modern devices support AOA, but some manufacturers may disable or limit accessory mode in their USB stack. If the device does not enter accessory mode when connected, scrcpy will fail to initialize the AOA connection.
Can I see the device screen while using OTG mode?
No. OTG mode intentionally does not stream video or audio. It creates only a minimal SDL window to capture input events from your host machine. To view the device screen simultaneously, you would need to use standard scrcpy with USB debugging enabled, or use a separate physical display for the Android device.
What input devices are supported in OTG mode?
OTG mode supports keyboard, mouse, and gamepad input. Keyboard and mouse are enabled by default when using --otg. Gamepad support must be explicitly enabled with --gamepad=aoa or -G. Each input type registers as a separate HID interface over the AOA protocol, allowing Android to treat them as standard USB peripherals.
Is OTG mode secure for locked-down or production devices?
Yes. Because OTG mode uses the AOA protocol rather than ADB, it does not require the "USB debugging" developer option to be enabled. This makes it suitable for controlling production devices, kiosks, or enterprise hardware where enabling developer options violates security policies. However, physical USB access to the device is still required, and the device must support AOA protocol connections.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →