← all articles

Can one Android app control another through UI Automator?

Yes, but only in one setup. The code doing the controlling has to run as an instrumentation test that adb started, and that is where its permission to read and tap another app’s screen comes from. An ordinary app you install from the Play Store can’t call UI Automator and start driving someone else’s app.

If you got here from this Stack Overflow question, that is the direct answer. The rest is for anyone who wants to know why, in plain terms, with no Android testing background assumed.

It matters if you test Android apps or automate phones you own. It also matters a little if you just want to know what one app on your handset can do to another. I’m a Singapore-based operator, not an Android framework engineer, so where I describe internals I’m working from Google’s docs and the AOSP source, and I’ll flag the parts I’m less sure about.

what it is

UI Automator is Google’s UI testing framework for flows that leave a single app. It ships as an AndroidX library, and the Android developer docs position it for cross-app UI tests between the system and installed apps. You write tests in Kotlin or Java, package them into a test APK, and they can see and act on whatever is on screen: your app, another app, the launcher, the notification shade, the settings screen.

“Control” needs pinning down. UI Automator doesn’t call the other app’s code, and it doesn’t read its memory or files. It reads the screen the way a screen reader does, as a tree of nodes carrying text, resource ids, bounds and a few flags. Then it acts by sending taps, swipes, key events and text. If a button is on screen and exposed to accessibility, you can press it. If it isn’t exposed, you can’t.

Espresso is the framework people compare it with. It runs inside your app’s process, finds views by id, and waits for the UI thread to go idle before it acts, which makes it fast and steady. The price is scope. Espresso only sees your own app’s windows, so the moment a test needs to tap the system permission dialog or another app, it has nothing to grab. That gap is why UI Automator exists. I compare them properly in espresso vs ui automator.

My position, and you can argue with it: for an app you own, write Espresso first and treat every UI Automator line as a cost you pay only at the seams.

how it works

The chain has four links.

First, a test APK. It depends on the androidx.test.uiautomator library and uses AndroidJUnitRunner as its runner.

Second, adb starts it. Gradle’s connectedAndroidTest task does this for you, or you can run it by hand:

adb shell am instrument -w com.example.driver.test/androidx.test.runner.AndroidJUnitRunner

adb relays that command from your PC to the phone (client, server and daemon, which I cover in what is adb). The relevant detail is that am, the activity manager command line tool, runs as the shell user, uid 2000.

Third, and this is the part that answers the question. The instrumentation gets a UiAutomation connection backed by that shell identity. Google’s UiAutomation reference describes the class as a special type of accessibility service that can also inject raw input events. Injecting events into other apps and reading their window content are protected permissions a normal app can’t be granted. As far as I can tell the shell user holds them, which is why a test started over adb can do what an installed app can’t. Root isn’t involved. Treat my description of the plumbing as a reading of the source, not gospel, and OEM skins can differ.

Fourth, your test code. You get a UiDevice, launch the other app, then find and act on things:

val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
val pkg = "com.example.otherapp" // an app you own or are authorised to test

device.executeShellCommand("monkey -p $pkg -c android.intent.category.LAUNCHER 1")
device.wait(Until.hasObject(By.pkg(pkg).depth(0)), 5_000)

device.findObject(By.res(pkg, "login_button")).click()

executeShellCommand runs with shell privileges too, so it can launch an installed app by package name. I used it because Android 11 added package visibility filtering, and getLaunchIntentForPackage can return null for a package you haven’t declared in a queries element.

To see what your test sees, run adb shell uiautomator dump /sdcard/dump.xml, then adb pull the file. Anything missing from that XML is invisible to your test.

Older tutorials sometimes run tests with adb shell uiautomator runtest and a jar file. That is the first-generation approach. Everything above is the instrumentation-based one, which is what the current Android docs cover.

To try it you need a phone or emulator with USB debugging on, adb on your PC and Android Studio. An emulator is fine for learning.

why it matters

Where it earns its place:

  • Flows that leave your app. Runtime permission dialogs, share sheets, the notification shade, a deep link that bounces through a browser and back. Espresso can’t see those windows. UI Automator can.
  • Chores on devices you own. Python’s uiautomator2 library drives the same layer over adb, and I wrote up the RPA angle in android rpa with adb and uiautomator2. Only point it at devices and apps you own or are authorised to test.
  • Device farms. Appium’s UiAutomator2 driver is built on this layer, so a farm that runs Appium tests on Android is usually using it underneath. The roundup of open-source android device farm tools shows what’s out there.
  • Knowing what your phone allows. Once you see that cross-app control needs adb or an accessibility permission you granted yourself, an app asking for accessibility access stops being a routine tap-through.

One practical note on hardware. Emulators cover most of what I’ve described. If you need a real handset on a Singapore mobile network, cloudf.one rents real Android phones in Singapore on dedicated hardware, each with a persistent Singapore mobile IP, controlled from the browser. It’s Singapore only. There’s also no self-service adb: adb and Appium reach a rented phone only through a WireGuard tunnel that we set up on request (email [email protected]), so plan for that before you count on it for test runs.

common misconceptions

“Any app can drive any other app if it bundles UI Automator.” No. The library is only a client. What counts is who started the instrumentation and with which identity. The route open to a normal installed app is an AccessibilityService, which the user has to switch on by hand in Settings. Google Play has a specific policy on AccessibilityService API use, as of September 2026, and the text changes, so read the current version before you build on it.

“It needs root.” It doesn’t. Shell access over adb is enough on a stock phone. Root is a separate topic and not one I’m covering here.

“It can get inside the other app.” It can’t. It sees the accessibility tree and sends input, nothing more, so private storage and app code stay out of reach. Games and other canvas-drawn screens expose few or no nodes, so you fall back to raw coordinates. If an app is built to resist automation, that is the owner’s decision, and this article isn’t a way around it. Automate only apps you own or are authorised to test.

“Cross-app tests are as stable as Espresso ones.” They aren’t. Espresso syncs with your app’s main thread. UI Automator has no hook into another app’s idle state, so it polls and waits, and timing bites. why android ui tests are flaky goes through the causes.

where to go from here

Depending on what you’re after:

Written by Xavier Fok

disclosure: this article may contain affiliate links. if you buy through them we may earn a commission at no extra cost to you. verdicts are independent of payouts. last reviewed by Xavier Fok on 2026-09-26.

free download
The adb and scrcpy command cheat sheet

The commands we reach for on a rack of real phones, on one printable page. Leave your email and we will also tell you when we publish a new guide, a few times a month at most.

from the team behind cloudf.one
Passes on the emulator, fails on a real phone?

cloudf.one rents real Android phones in Singapore that you control from the browser, each on a persistent Singapore mobile IP. Real hardware, a real OEM build and a real carrier network, without buying and racking the phones yourself.

test on a real phone →
read on
More from ADB Handbook

adb commands, scrcpy flags, automation frameworks, and why a test passes on one phone and fails on the next.

browse all articles →