Droidline: Two Commands, No USB or Root, to Drive Real Android Phones from Anywhere

Recently I came across a project on GitHub: Droidline. It's quite interesting, so I'm sharing it with you.
For this project, just keep a few key points in mind:
Droidlineis an automation tool that drives real Android phones with code. The PC side is written in Go, the phone side in Kotlin, and it's MIT licensed.- Open-sourced on October 7, 2026. The author is KnifeLemon, a Korean developer. Their previous offline file converter, Filee, has already accumulated 22.8K stars.
- Official site: droidline.dev. The app, error messages, and docs are all trilingual (Chinese, English, Korean).
What it does is simple to summarize in one sentence: No ADB, no USB cable, no root. Install an app on the phone, scan a QR code to pair, and the PC side can drive this real device using Python, Node.js, the command line, HTTP, or MCP.

First, What It Bypasses
The Appium and uiautomator2 ecosystem has been around for years, and it works well. But it has plenty of restrictions: developer options, USB debugging, and a data cable, all required. When you have many real test devices, your desk becomes a row of cables. Loose cables, driver issues, phones going to sleep—there are countless reasons scripts fail at 3 a.m. If you want to go wireless, you end up paying by the hour on cloud device platforms.
Droidline eliminates all of that. You only install a normal app on the phone, enable the accessibility service and the built-in Droidline keyboard, scan a QR code to pair with the PC, and then the phone initiates the outbound connection. The PC doesn't need to open a single port. On the same Wi-Fi network it works directly; if you're away and the phone is on mobile data, you can use a tunnel or a self-hosted relay. All communication is end-to-end encrypted, and the relay only ever sees ciphertext.

What Can It Do
- Real-device automated testing.
dump()pulls the screen into an element tree. Commands locate elements bytextorid, not coordinates. Waiting logic is built in. On failure, error messages tell you exactly which page you're stuck on, for example:NOT_FOUND: Could not find text 'Log in' within 10s. Flows like registration and login that require cross-app SMS verification codes can run end to end. - One PC drives a row of phones. Different phones run in parallel, each with a name, and each can be assigned its own SOCKS5 or HTTP proxy, all without root. Combined with
cuts_network, you can switch IPs via airplane mode and continue the task. Scenarios that need multiple IPs for scraping or data generation can be handled directly. - Give AI agents a hand on real phones. Run
droidline mcp, and Claude or Cursor can see the real screen, tap buttons, and with--read-onlyyou can restrict it to view-only. - Small automations. Receive phone notifications on the PC, reply, forward to webhooks, periodically grab data from an app, or turn off Wi-Fi at night—things like that can be solved with a few lines of script.

Two Commands, Phone Connects to PC Wirelessly
The PC side has releases for Windows, macOS, and Linux. The phone needs Android 9 or above. No real device nearby? No problem—the release package includes a droidline-fakephone emulator, and the official tutorial walks you through your first script against it.
Download the package for your system from Releases, put it in your PATH, install droidline-agent.apk on the phone, then run two commands:
droidline serve
droidline pair
The first starts the service, the second shows a QR code. Scan it with the phone app to complete pairing. No camera? Tap "Pair on this Wi-Fi" in the app and enter the 6-digit code it displays. Once connected, you can tap the real phone. Start with the settings page:
droidline launch com.android.settings
droidline touch text "Network & internet"
For Python, pip install droidline and try the login flow against the demo app in the repo:
from droidline import connect
d = connect()
d.launch("dev.droidline.demo")
if d.exists("text", "Close ad"):
d.touch("text", "Close ad")
d.input("id", "email", "knife")
d.touch("text", "Log in")
print(d.get_text("id", "greeting"))
Connecting to Claude Code via MCP takes one line:
claude mcp add droidline -- droidline mcp
You only need to remember three components to get started. droidline serve is the resident service; all entry points connect to its port 8780. droidline pair handles pairing. droidline mcp is the adapter for agents. inspect (record scripts, suggest selectors) and webdriver (let Appium clients use it directly) can be enabled as needed; they consume no resources when off.

Some Clever Design Choices
One spec generates five entry points. The spec/commands.json file at the repo root defines all commands and trilingual error messages. The Python, Node.js, and .NET SDKs plus the MCP tool list are all generated from it. CI runs validation, and if an SDK method doesn't match the spec, the build fails. So touch is called touch across all five entry points, and only one copy of the docs needs to be maintained. Teams building multi-platform SDKs can copy this approach directly.
The phone connects outward; the PC opens no ports. With the direction reversed, the security model follows naturally. Pairing decides who can control the phone. After the handshake, every line of communication is end-to-end encrypted; tunnels and self-hosted relays just carry ciphertext. When the phone reconnects after a network drop, the task continues from where it left off. In-flight commands are cached by the phone side, so nothing is re-executed.
Permission boundaries are restrained. The phone side only uses the accessibility service and a self-installed keyboard—permissions any ordinary app can get. The cost is listed plainly in the README: it can't unlock a PIN lock, there's no single command to force-stop or clear data, and it can't open pages not exported by other apps. A tool that explains "what it can't do" up front is more valuable than a simple list of features.
What's Still Short
The version is still 0.1. The author states on the Status page that server and SDK tests currently run against a phone emulator. Cross-brand real-device verification is still in progress. If you're going to production, run your own devices through it first.
Games and apps with custom-drawn UIs have no elements to grab; you fall back to coordinate-based taps and color checks. On Android 15 and above, the system hides one-time verification codes from notifications, so you can only know a code arrived and read it inside the app. These are the natural ceiling of the accessibility-permission approach—not something one patch can fix.
Final Thoughts
Use the tool or not. The Appium ecosystem is mature, and your team's script assets are already there; there's no need to migrate just to avoid a cable. But the ideas are worth taking away. A single source spec generating multi-platform SDKs saves the maintenance cost of five inconsistent interface sets. The phone-initiated outbound connection plus end-to-end encryption sets a security-sound example for remotely driving real devices. Writing permission boundaries and impossible things into the README first means users hit fewer pitfalls and trust comes faster.
For test developers, it has two most practical values. The barrier to real-device regression testing is lowered—no cables, no root, and SMS verification code flows can run end to end. Hook up MCP, and AI agents can run exploratory testing on real devices, which is much closer to reality than tapping around in an emulator.
Project address: https://github.com/KnifeLemon/Droidline
Top 1 from juejin.cn, machine-translated. The original thread is authoritative.
The element tree has a premise: the dump only gets anything when the app is willing to report its UI to the accessibility service. On the same screen I measured two extremes in practice: normally 200+ nodes, but after one tap it became 0. It wasn't that the permission was missing — that window had the anti-screenshot flag set, so the element tree and the screenshot went empty together, and force-stop didn't restore it; the only fix was clearing data and logging in again. So "locating by text/id instead of coordinates" is rock-solid in system settings and your own apps, but once you enter mini-programs, Flutter self-drawn UIs, or a locked window like the one above, you have to fall back to screenshot-plus-vision, and the screenshot is black in those windows. When both paths die at the same time, that page simply can't be tapped that day; all you can do is honestly report that the screen can't be obtained. The fakephone emulator won't give you the 0-node case, so adding this scenario on a real device is more valuable.