<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nemarpuc</title>
    <description>The latest articles on DEV Community by Nemarpuc (@nemarpuc).</description>
    <link>https://dev.to/nemarpuc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4149747%2F23cfcdbe-15ec-4b61-9ed4-ae80f464a961.png</url>
      <title>DEV Community: Nemarpuc</title>
      <link>https://dev.to/nemarpuc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nemarpuc"/>
    <language>en</language>
    <item>
      <title>Control Android from your PC over USB: no root, no adb, no app</title>
      <dc:creator>Nemarpuc</dc:creator>
      <pubDate>Tue, 29 Sep 2026 13:12:27 +0000</pubDate>
      <link>https://dev.to/nemarpuc/libaoahid-a-c-abi-library-for-sending-hid-input-to-android-over-usb-1h6f</link>
      <guid>https://dev.to/nemarpuc/libaoahid-a-c-abi-library-for-sending-hid-input-to-android-over-usb-1h6f</guid>
      <description>&lt;p&gt;I've released &lt;a href="https://github.com/nemarpuc/libaoahid" rel="noopener noreferrer"&gt;libaoahid&lt;/a&gt;, a library that lets a PC send keyboard, mouse, gamepad, and multi-touch input to an Android device over a USB cable. It's implemented in C++20 but exposes a stable C ABI, so it can be used from C, C++, or any language that can call C. It's MIT-licensed.&lt;/p&gt;

&lt;p&gt;It started when I wanted to build something to automate Android. While looking for a way to do it I came across AOA's HID feature, but implementing it ran into bug after bug. So I set out to build a library that takes care of the complicated parts while keeping full flexibility and as little latency as possible.&lt;/p&gt;

&lt;p&gt;The device needs no root and no app. To Android it looks like a USB keyboard or touchscreen has been plugged in, so the input is handled like any ordinary device's. It runs on Windows and Linux, and can also be used from Python, C#, and Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1KjUMLr2y3o" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is &lt;a href="https://github.com/nemarpuc/aoahid_player" rel="noopener noreferrer"&gt;aoahid_player&lt;/a&gt;, an app built with the library. It plays back a CSV script, then changes the volume and the track from the PC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Compared with other approaches
&lt;/h2&gt;

&lt;p&gt;The usual ways to drive Android from a PC are &lt;code&gt;adb shell input&lt;/code&gt; and accessibility services. The first needs USB debugging and injects input in software; the second needs an app installed on the device and granted permissions.&lt;/p&gt;

&lt;p&gt;With the HID feature of AOA 2.0, which libaoahid uses, you just plug the device in. No settings, no app.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Since Android 4.1, Android has had &lt;a href="https://source.android.com/docs/core/interaction/accessories/aoa2" rel="noopener noreferrer"&gt;Android Open Accessory (AOA) 2.0&lt;/a&gt;. Its HID feature lets a USB-connected PC register a virtual input device on the phone and send input through it.&lt;/p&gt;

&lt;p&gt;Building something practical on raw AOA is harder than it looks. The HID descriptor has to be in a shape Android accepts, and multi-touch only works if each finger's state is tracked properly. libaoahid takes care of that.&lt;/p&gt;

&lt;p&gt;At the same time, the library never picks values such as resolution or bit width for you; the caller specifies all of them, so you always know exactly what is being sent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supported input
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kind&lt;/th&gt;
&lt;th&gt;Covers&lt;/th&gt;
&lt;th&gt;Tested on hardware&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Touch&lt;/td&gt;
&lt;td&gt;Multi-touch&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keyboard&lt;/td&gt;
&lt;td&gt;Every key at once&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mouse&lt;/td&gt;
&lt;td&gt;Motion, buttons, wheel&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gamepad&lt;/td&gt;
&lt;td&gt;Sticks, buttons, D-pad&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Media keys&lt;/td&gt;
&lt;td&gt;Volume, play/pause, next track&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pen and others&lt;/td&gt;
&lt;td&gt;Pressure, hover, etc.&lt;/td&gt;
&lt;td&gt;Not yet&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Tested on a Samsung Galaxy Tab S11 and a POCO F6 Pro, from both Windows 10 and Arch Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;

&lt;p&gt;Prebuilt archives are on &lt;a href="https://github.com/nemarpuc/libaoahid/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;. To build from source you need CMake 3.20+, a C++20 compiler, and libusb 1.0.30 or later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/nemarpuc/libaoahid
&lt;span class="nb"&gt;cd &lt;/span&gt;libaoahid
cmake &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-B&lt;/span&gt; build &lt;span class="nt"&gt;-DAOAHID_BUILD_SHARED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ON
cmake &lt;span class="nt"&gt;--build&lt;/span&gt; build &lt;span class="nt"&gt;--config&lt;/span&gt; Release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Moving a mouse looks like this (error handling left out):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Find and open the device */&lt;/span&gt;
&lt;span class="n"&gt;aoahid_discover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;aoahid_device_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aoahid_discovery_get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;found&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;device_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Define a mouse and register it on the device */&lt;/span&gt;
&lt;span class="n"&gt;mopt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;button_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;mopt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aoahid_integer_field&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;mopt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;aoahid_integer_field&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;127&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;aoahid_spec_create_mouse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mopt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;aoahid_node_open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;node_options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Send input */&lt;/span&gt;
&lt;span class="n"&gt;aoahid_mouse_move&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;aoahid_node_submit_blocking&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mouse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Complete, runnable code and small programs for checking behavior on a real device are in &lt;a href="https://github.com/nemarpuc/libaoahid/tree/main/examples" rel="noopener noreferrer"&gt;examples&lt;/a&gt;. Setup steps are in the &lt;a href="https://github.com/nemarpuc/libaoahid/blob/main/docs/QUICKSTART.md" rel="noopener noreferrer"&gt;QUICKSTART&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bridge for using adb at the same time
&lt;/h2&gt;

&lt;p&gt;On Windows, only one process can open a USB device. So even with the driver on WinUSB or libusbK, AOA input and adb normally couldn't be used together.&lt;/p&gt;

&lt;p&gt;That's why I made &lt;a href="https://github.com/nemarpuc/aoahid_adb_proxy" rel="noopener noreferrer"&gt;aoahid_adb_proxy&lt;/a&gt;. It relays adb traffic over the app's own USB connection to a local port, so while input is being sent you can still use adb and scrcpy with &lt;code&gt;adb connect 127.0.0.1:6555&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Samsung on Windows
&lt;/h2&gt;

&lt;p&gt;When a Samsung device is connected to Windows, Samsung's own USB driver gets installed. With it, input works but the adb bridge doesn't.&lt;/p&gt;

&lt;p&gt;Replacing the driver for the whole device with WinUSB using &lt;a href="https://zadig.akeo.ie/" rel="noopener noreferrer"&gt;Zadig&lt;/a&gt; fixes it. Regular adb keeps working on WinUSB, but tools that need the manufacturer's driver, such as Samsung Smart Switch, may stop working. The detailed steps are in &lt;a href="https://github.com/nemarpuc/aoahid_player#troubleshooting" rel="noopener noreferrer"&gt;aoahid_player's README&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  aoahid_player
&lt;/h2&gt;

&lt;p&gt;The app in the demo. It plays back actions written in CSV and can record real actions into CSV. It also supports looping, speed changes, live control with the PC's mouse and keyboard, and playback to several devices at once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@screen 1080x2400
t,0,1,500,500,16
t,0,0,500,500,16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This script taps the screen at (500, 500). Prebuilt downloads for Windows and Linux are on &lt;a href="https://github.com/nemarpuc/aoahid_player/releases" rel="noopener noreferrer"&gt;Releases&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Some input types, such as pen, haven't been tested on hardware yet. AOA is an Android feature, so iOS isn't supported.&lt;/p&gt;

&lt;p&gt;I'd like to hear what you would use it for, and whether you've hit similar driver problems with other manufacturers on Windows. Let me know in the comments. Bug reports and feature requests are welcome as GitHub issues.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;libaoahid: &lt;a href="https://github.com/nemarpuc/libaoahid" rel="noopener noreferrer"&gt;https://github.com/nemarpuc/libaoahid&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;aoahid_adb_proxy: &lt;a href="https://github.com/nemarpuc/aoahid_adb_proxy" rel="noopener noreferrer"&gt;https://github.com/nemarpuc/aoahid_adb_proxy&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;aoahid_player: &lt;a href="https://github.com/nemarpuc/aoahid_player" rel="noopener noreferrer"&gt;https://github.com/nemarpuc/aoahid_player&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This article was written with the help of AI. Much of the code in these projects was also written with an AI coding assistant; the design, hardware testing, and review were done by me.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>usb</category>
      <category>cpp</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
