Flipper got archived, so I built an Android inspector that needs no SDK

GitHub: https://github.com/ESeungJun/droidlens
Installers for macOS, Windows and Linux are on the Releases page.
If you've done Android work for a while, you've probably used Flipper. You add its SDK to your app, and you get the network log, the database and the view tree on your desktop. Meta archived it, and I kept missing it. (I wrote this with help from Claude and checked every number myself.)
So I built my own. It's called DroidLens, and it does a few things differently.
- It works over adb alone, with no SDK, so it can open any app on the phone.
- It has an MCP server, so a Claude Code agent can drive the device and check things for you.
- What the agent checks gets saved as a YAML scenario you can replay as a regression test.
The first version took about a day, from the first commit to v0.5.1. It's at v0.10.3 now.
The result first
- Screen: scrcpy-based mirroring up to 60fps, with click, swipe and text input
- View hierarchy: a tree and a 3D exploded view of the layout
- Screen flow: it taps through the app by itself and draws a graph of how screens connect
- Libraries: it detects which libraries an app uses from the class names inside the APK
- App info: version, SDK levels, activities, permissions, and a deep link launcher
- Scenarios: flows the agent verified, saved as YAML and replayable from the CLI
What I wanted from it
I wrote down four goals before I started, and I used them every time I had to pick between two options.
- Look inside any app, without an SDK.
- Let an agent use it directly, not only a person.
- Be usable by someone who isn't an Android developer.
- Keep running without me babysitting it.
No SDK, just adb
The thing that bothered me most about Flipper was the SDK. You can't look at someone else's app at all. And your own app needs the SDK in the build, so release builds are off limits too.
Every core feature runs on plain adb:
- the view hierarchy comes from
uiautomator dump, - the current activity from
dumpsys window, and fragments fromdumpsys activity top, - libraries come from pulling the APK and matching class names in
classes*.dexagainst 103 signatures.
That means anything installed on the phone is fair game. The README screenshots are of Google's Calculator app for exactly that reason.
Some things really do need to look from inside the app: network traffic, SharedPreferences, databases. Those went into an optional SDK. You add it to debug builds only, and release builds get an empty implementation (sdk-noop). It needs no init code and no INTERNET permission.
adb was too slow, so I put a small agent on the phone
adb worked, but it was slow. One uiautomator dump took 1.5 to 3 seconds. Checking the current fragment took anywhere from 1 to 7.
For a person clicking around, that's annoying but fine. For an agent that reads the screen dozens of times while it works, it was a dead end.
I moved the work onto the device instead. DroidLens installs a 13KB agent APK, starts it with am instrument, and keeps a UiAutomation connection open. It answers requests over a socket.
As a bonus, Korean text input started working. adb shell input text only handles ASCII, which is a real problem when your users type in Korean.
If the agent fails to install or start, DroidLens falls back to the old uiautomator path. Everything still works, just slower.
From looking to checking
At this point it was a Flipper replacement and not much more. But I do most of my development with Claude Code now, so I wanted the agent to use this thing too.
I added an MCP server. It hands Claude Code the same tools I use in the app: read the screen, tap, swipe, type, launch an app, read logs, look at network calls. That was 43 tools in the first version and it's 50 now.
That made this kind of request possible:
Log in, open Settings, and check that dark mode turns on. The agent reads the real screen on a real phone, taps through it, and checks the result. If it passes, the steps are saved as a YAML scenario. You can replay it from the CLI, and it exits with code 1 on failure, so it fits into CI.
Setup is one button in the app's settings tab, which runs claude mcp add for you. The MCP server runs on the Node that ships inside Electron, so there's nothing extra to install.
The auto-explorer connected the wrong screens
The screen flow tab taps through the app on its own and draws how screens connect.
When I ran it on a real app, it linked a category menu to a screen that had nothing to do with it. Three things were going wrong at once:
- It kept tapping the coordinates it recorded the first time. After going to another screen and coming back, the list had scrolled, so a different item was sitting there.
- It never checked that "back" actually returned to the original screen. If it landed somewhere else, it carried on tapping anyway.
- Its screen signature included the contents of scrollable areas, so the same screen with different list items counted as two screens.
Now it finds the same button again right before tapping, and the signature ignores what's inside scrollable areas.
It also had two inputs, "max depth" and "max steps". After using it for a bit I couldn't tell you the difference between them, and I wrote it. I removed steps and kept depth. Internally it stops after 500 taps, and it never taps buttons that look like pay, delete, log out or send.
Checking it: the SDK was logging passwords
Before v0.5.0, I ran two code reviews and one security review in parallel. The worst finding was in the SDK's redaction.
The SDK hides passwords and tokens in the network log. But it only matched keys named exactly password or token. So newPassword, fcmToken and jwt were being logged in plain text. The reviewer found it by actually compiling the SDK and running it.
I switched to partial matching on key names, and extended redaction to JSON inside strings, GraphQL arguments, and multipart and XML bodies.
Then the automated security review found a problem in my fix. One of the new regexes slowed down badly on certain inputs (ReDoS). A single 256KB input took more than 60 seconds. After the fix it takes about 20ms.
There was an older one too. The on-device agent used to listen on a TCP port, which meant any other app on the same phone could connect and borrow its UiAutomation privileges. (Not my proudest moment.) It now uses a Unix socket and only accepts connections from the shell or root user.
What broke when I actually installed it
I tested the one-line installer and auto-update by installing them for real, and two things broke:
- An environment variable I used to pin the version during testing leaked into the newly launched app. The new app thought it was the old version, updated itself, and did it again. An update loop.
- The in-app agent chat didn't work on Windows only. Going through cmd.exe was mangling JSON and multi-line arguments.
Neither would have shown up if I'd only run it in dev mode.
What I learned
The goal I spent the most time on was "usable by someone who isn't an Android developer". Every feature I added made the app a little harder for a first-time user. So before adding anything, I asked whether someone opening it for the first time would understand it. If even I got confused by an option, it went.
I also didn't find any of the security bugs by using the app. The password leak and the open port only showed up when a review read the code looking for exactly that kind of problem.
If you try it and something's missing or broken, please open an issue. I'm especially curious whether mirroring and install work for you on Windows and Linux.
- GitHub: https://github.com/ESeungJun/droidlens
- LinkedIn: https://www.linkedin.com/in/승준-이-53707a328/
Originally published on Medium.





