USB (Universal Serial Bus) on an Android device is highly complex because modern smartphones utilize the USB OTG (On-The-Go) or USB Type-C Dual-Role specifications.
Unlike a desktop PC, which is always a USB "Host," an Android phone must dynamically switch roles depending on what you plug into it.
The USB Gadget Framework
When you plug your Android phone into your laptop, your laptop is the Host, and your phone is the Device. In the Linux kernel, acting as a USB device is managed by the USB Gadget Framework.
The Gadget Framework allows the Android phone to pretend to be almost any type of USB peripheral by loading different "functions."
- MTP (Media Transfer Protocol): The phone pretends to be a media camera, allowing the PC to browse the
/sdcarddirectory. - RNDIS / USB Tethering: The phone pretends to be a USB Ethernet adapter, routing the PC's network traffic through the phone's 5G modem.
- MIDI: The phone pretends to be a musical keyboard.
ADB over USB
The most critical function for developers is ADB (Android Debug Bridge). ADB is implemented as a custom USB Gadget function called ffs (FunctionFS).
When you enable USB Debugging, the init process tells the kernel Gadget Framework to bind the ADB daemon to the USB hardware endpoints.
# You can see how Android dynamically configures USB gadget properties via sysfs
adb shell getprop | grep sys.usb
# Output: [sys.usb.config]: [mtp,adb]
# Output: [sys.usb.state]: [mtp,adb]
USB Host Mode
When you plug a USB flash drive or a physical keyboard into your Android phone using a USB-C adapter, the phone flips roles and becomes the USB Host.
It uses standard Linux USB Host controllers (like xHCI). The kernel enumerates the attached device and automatically loads the appropriate standard Linux driver (e.g., usb-storage for the flash drive or usbhid for the keyboard).
USB Type-C and Power Delivery (PD)
The physical USB-C port introduces immense complexity. A Type-C port can be plugged in upside down, it can route DisplayPort video, and it handles extreme fast charging via USB Power Delivery (USB-PD).
To manage this, devices have a dedicated Type-C Port Controller (TCPC) chip. The kernel driver for this chip runs a complex state machine (TCPM) that negotiates voltage contracts with the wall charger (e.g., "I need 9V at 3A for fast charging") and handles the hardware interrupts to flip the data pins if the cable is inserted upside down.
// Example of a USB-C state machine changing roles
if (tcpm_port_is_source(port)) {
tcpm_set_charge_current(port, 3000); // 3 Amps
} else {
tcpm_enable_gadget(port); // Switch to device mode
}