All posts

October 6, 2026

Quick test: one click to see if your AI-built Android app crashes on a real phone

Built an Android app with Cursor, Claude Code or Expo and have no Android phone to try it on? Quick test cold-starts your APK on a real phone, taps around inside it for a minute, reads the crash log, and hands you a fix prompt to paste back into your AI.

If you built an Android app with Cursor, Claude Code, Lovable, Bolt, Replit or Expo, the build is no longer the hard part. The hard part is the step after it: does the app actually open on a phone? If you don't own an Android phone, the honest answer has been "no idea until someone else installs it".

Quick test is our answer to that, and it shipped at the end of September. Rent a real phone, drop your APK into the Apps tab, and press Quick test on its row. About two minutes later you have a verdict, the crash if there was one, a likely cause in plain words, and a prompt to paste straight back into the tool that wrote the app.

This post covers what it reports, what it actually runs on the phone, and the Android quirks we had to work around to make it safe on a phone the next renter will use.

What you get back

A Quick test ends in one of six verdicts, most serious first:

Verdict What it means
Crashed Android logged a Java or native crash for your app.
Stopped responding (ANR) The main thread was blocked long enough for Android to show "app isn't responding".
Did not open The app never reached the screen.
Closed without a crash report The process ended, but Android logged no crash. The app closed itself, got backed out of, or was stopped to free memory.
No crash, errors logged It survived, but it wrote error lines worth reading.
Passed It opened, survived about a minute of taps, and logged no crashes, freezes or errors.

Under the verdict there are three buttons:

  • Copy fix prompt for your AI. It holds the stack trace, the phone's model and Android version (read from the phone itself), your app's version and targetSdk, how the app was tested, and a likely cause. The prompt asks for the root cause and the exact code change. It also asks the AI to say so if the problem is a build or configuration setting rather than code, because with AI-built apps it very often is.
  • Download report. The same facts as Markdown, ready for an issue or a commit message.
  • Ask DeviceyAI to explore it. More on that below.

What runs on the phone

Nothing exotic, and that is the point. Every step is a standard Android tool, so you could reproduce any result by hand.

  1. Facts first. getprop for the brand, model and Android release. Your app's versionName, versionCode and targetSdk. The phone's clock, which becomes the anchor for reading the log at the end.
  2. Find the launcher activity. cmd package resolve-activity --brief -a android.intent.action.MAIN -c android.intent.category.LAUNCHER <package>. If there isn't one, the test stops and says so. An APK with no launcher activity is usually a test APK, and those belong in the Automation tab's Espresso runner.
  3. Cold start. am force-stop, then am start -W -n <component>. -W waits for the launch to finish and reports LaunchState and TotalTime, so the launch time in your report is Android's own measurement, not ours.
  4. Check it stayed up. Wait five seconds, then run pidof. A lot of AI-built apps die right here: they open, try to reach a server, and fall over.
  5. About a minute of taps. Three bursts of Android's monkey tool, 70 taps each, 300 ms apart. Monkey may only touch your app, plus Android's permission dialog so that a permission prompt can be answered. The seeds are in the report. Monkey run with the same seed generates the same sequence of events.
  6. Read the log. logcat -d across the crash, events, main and system buffers, starting from the anchor taken in step 1, then a final pidof.

That comes to about two minutes of session time. A Quick test has no price of its own: it is ordinary rental time.

Why it doesn't just run monkey

adb shell monkey -p com.example.app 500 is the obvious one-liner, and most guides stop there. We started there too, and then changed almost every default.

Monkey's exit unlocks screen rotation. In AOSP, monkey's cleanup injects a rotation event that ends in thawRotation(), which hands the phone back to its accelerometer. Our phones lie flat in a rack. A flat phone with a live accelerometer picks an orientation more or less at random, and the next renter gets apps that open sideways. That was a real bug here in August. So the Quick test never launches the app through monkey (am start does that). It also forces portrait again after every burst, because the thaw happens each time monkey exits.

Taps only. Monkey's defaults include swipes, trackball moves, system keys, app switches, rotation and permission changes. A swipe that starts at the top edge pulls down the notification shade and can toggle quick settings, which the next renter would inherit. A permission event revokes your app's permissions mid-run, which kills the app and gets reported as your crash. So the run uses --pct-touch 100 and sets every other --pct-* to zero. Monkey refuses to run if those weights don't add up to exactly 100 when all of them are given.

Monkey ignores a hang-up. /system/bin/monkey traps SIGHUP, so closing the adb connection does not stop it. Pressing Stop has to find monkey on the phone and kill it. The script prints its own process ID and then execs monkey, and Stop kills that ID.

It never clears the log. logcat -c is the usual way to get a clean log for a test, but it also wipes the crash buffer's history, which is what we read when we reconstruct an incident. The test reads from its own time anchor (logcat -T) instead, and filters by time again on our side in case a phone ignores the anchor.

The failures it recognises

Most AI-built apps that fail on their first real phone don't have a logic bug. They fail because of the gap between the machine the app was built on and a phone in someone's hand. The Quick test matches the log against the common cases and says what to do:

  • Unable to load script or Could not connect to development server. This is an Expo or React Native development build looking for Metro on your computer. A rented phone can't reach your computer, so build a standalone APK instead: an EAS preview profile with "buildType": "apk", or ./gradlew assembleRelease.
  • CLEARTEXT communication … not permitted. Android blocks plain http:// by default. Serve the API over HTTPS, or allow that one domain in a network security config.
  • A failed connection to localhost, 127.0.0.1 or 10.0.2.2. The app is calling your computer. On a real phone, localhost is the phone, and 10.0.2.2 only means "the host machine" inside the emulator. Point the app at your deployed backend.
  • MissingPluginException. A Flutter plugin isn't registered in this build. Run flutter clean and rebuild.
  • UnsatisfiedLinkError or dlopen failed. A native library is missing for the phone's CPU. Include arm64-v8a in your ABI filters, or build a universal APK.
  • ClassNotFoundException or NoSuchMethodError in a release build. R8 removed or renamed something. Add a keep rule, or compare against a debug build.
  • NetworkOnMainThreadException, Default FirebaseApp is not initialized, SecurityException: Permission Denial, OutOfMemoryError, Resources$NotFoundException and InflateException. Each one comes with a one-line next step.

The likely cause goes into the fix prompt as well. That way the AI starts at the right layer, instead of rewriting your UI code to fix a URL.

If you uploaded an .aab

Plenty of tools produce an Android App Bundle by default, because that is what the Play Store wants. A phone can't install one. If you drop an .aab (or an .apks, .xapk or .apkm) on the Apps tab, you don't get a bare error. You get a row explaining how to build an .apk in Expo/EAS, Flutter or Android Studio.

Random taps vs. a first-time user

A Quick test taps at random. It answers one question well, whether the app survives contact with a finger, and it is blind to everything else. It won't fill in your sign-up form, and it won't notice a button that does nothing.

That is what Ask DeviceyAI to explore it is for. DeviceyAI is the agent built into every session, and it uses the app on purpose. It reads the screen, opens every screen it can reach, tries the main buttons and text fields, and checks logcat after each step. It finishes with a bug report that includes steps to reproduce. If the Quick test saw a crash, the instruction names the exception class and asks DeviceyAI to find the steps that trigger it.

The button only fills in DeviceyAI's message box. Nothing runs, and no AI credits are spent, until you press Send. Only two facts from the test go into that instruction: the package name and the exception class, each checked against a strict pattern. The crash message stays out, because your app chose that text and DeviceyAI treats anything on the device as untrusted input.

From a terminal

The same test runs from the devicerent CLI, using the same verdict and report code as the website:

npm install -g devicerent
devicerent login
devicerent rent "Pixel 7a"
devicerent test quick app-release.apk --report quick-test.md --fix-prompt
devicerent stop

Given an .apk, it installs the app first. --seed replays an earlier run's tap sequence. The exit code is non-zero when the app crashed, froze, closed or never opened, so a script can act on the result.

What it isn't

  • A test suite. It catches crashes on launch and under random taps, which is the most common way an AI-built app breaks on a real phone, and it does that in a couple of minutes with no setup. For flows that must keep working, write an Espresso or Appium test (or ask DeviceyAI to write one) and run it from the Automation tab.
  • Hot reload. Metro, Expo Go and development builds reach your computer through adb reverse, and the session doesn't carry that. Test a standalone APK.
  • iOS, Maestro or Detox. There is no iOS, and none is planned. Espresso, Appium and anything that speaks plain adb work.
  • A device matrix in one click. It runs on the phone you pressed it on. To compare stock Android with Samsung's One UI, rent a Pixel and a Galaxy and run it on each. The differences are bigger than you'd think.

For now, live sessions need Chrome, Edge, Brave or Opera on a computer. Safari support is being worked on.

Try it

Rent a phone, open the Apps tab, drop your APK, and press Quick test. If it passes, you have learned something in two minutes. If it doesn't, you already have the prompt for the fix.