跪拜 Guibai
← Back to the summary

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:

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

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

Comments

Top 1 from juejin.cn, machine-translated. The original thread is authoritative.

booboosui

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.