← all articles

UiAutomation vs AccessibilityService: what's the difference

A lot of people land on this question after seeing both names in the same stack trace or the same Appium log and assuming they are two flavours of one thing. They are not. They share a lot of plumbing underneath, but they sit in very different places in Android’s security model, and that decides almost everything else about them.

UiAutomation is the testing api. You get it from an Instrumentation object, which means your code runs in a test process started from the shell, usually with adb shell am instrument. It can read the accessibility tree of any window on screen, inject input events, take screenshots, and, since Android 7.0 (API 24), temporarily adopt the shell’s permissions. AccessibilityService is the app-installed api. It is a normal apk with a declared service that the user switches on in Settings. It sees the same kind of node tree and can dispatch gestures, but it lives under the rules of a regular installed app, including Play policy if you ship through the store.

My short verdicts, from running phone farms and test rigs in Singapore: if you are testing an app you own, from a laptop or CI box, use UiAutomation (usually through UiAutomator2 or Appium). If you are building something that has to live on the phone and work without a host attached, for example an assistive tool, use AccessibilityService. If you are trying to use AccessibilityService as a cheaper way to do remote testing, stop and read the sections below first, because the setup cost moves around rather than disappearing.

One framing note. The template for this series is built around proxy vendors, so the head-to-head axes below are adapted to these two apis. Neither costs money, so “winner” means “right tool for the job”.

TL;DR comparison table

UiAutomation AccessibilityService
pricing free, part of the Android framework free, part of the Android framework
how it runs instrumentation process started from adb shell installed apk, enabled by the user in Settings
privilege can adopt shell permission identity (API 24+) normal app privilege plus accessibility capabilities
input injects events directly, screenshots, window tree dispatchGesture, global actions, node actions, screenshots on newer versions
host needed yes, something has to launch it over adb no, runs on its own once enabled
support AOSP and developer.android.com docs, Android issue tracker same, plus Play policy for store apps
target user QA engineers, test automation, CI assistive tech developers, on-device automation tools

UiAutomation at a glance

UiAutomation is a class in android.app. You do not construct it. You call getUiAutomation() on an Instrumentation, and the system wires up a connection to the accessibility subsystem on your behalf. The official UiAutomation reference describes it as the class that lets a test simulate user interaction and inspect the screen across app boundaries.

What that means in practice:

  • it runs in an instrumentation process, which is why you start it with adb shell am instrument -w ... and not by tapping an icon
  • it can see windows from any app on the device, not just your own, which is the whole point of UI Automator versus Espresso
  • it can call adoptShellPermissionIdentity() on API 24 and above, so test code can do things like granting runtime permissions or reading protected state that a normal app cannot
  • it injects touch and key events at the input layer, so your taps are not limited to what an accessibility gesture can express

Most people never touch the raw class. They use UI Automator, which wraps it, or Appium’s UiAutomator2 driver, which pushes a server apk to the phone and drives it over HTTP. If you run that server inside a container, I covered the plumbing in running UiAutomator2 device automation inside a Docker container.

The cost of this model is the host. Something has to run adb, keep the instrumentation alive, and reconnect when the usb or wifi link drops.

AccessibilityService at a glance

AccessibilityService is an abstract class in android.accessibilityservice. You subclass it, declare it in the manifest with the BIND_ACCESSIBILITY_SERVICE permission, describe what it needs in an xml config, and ship it as an apk. The user then has to go to Settings and turn it on. The AccessibilityService reference and the guide on building accessibility services cover the contract.

Once enabled it receives accessibility events, can walk the active window’s node tree, can perform node actions like click and scroll, can fire global actions like back and home, and since API 24 can dispatch gestures with dispatchGesture(). Newer versions added more, for example a screenshot call, so check the reference for the API level you target.

What defines it:

  • it is a normal installed app, so it needs no host once it is enabled
  • the user has to consent in Settings, and that is deliberate
  • on Android 13 and later, apps installed outside a store can run into the restricted settings flow when you try to enable an accessibility service, as of October 2026
  • if you publish on Google Play, the store has its own policy about what accessibility services may be used for, and that policy has changed over the years. read the current version before you build a product on it

It is the right shape for on-device tools. It is an awkward shape for anything where the device is remote and nobody is holding it.

head-to-head

scope of reach

This replaces “IP pool size” in the template. The question is how much of the device each api can see and touch.

UiAutomation has the wider reach. Because it runs under instrumentation and can adopt shell identity, it can see all windows, inject low-level input, and do setup that a normal app cannot, like granting a permission before a test starts. If you have ever installed a test-only apk with the -t flag, that is the same family of shell-side privilege, and I wrote up the details in installing a test-only apk with adb install -t.

AccessibilityService sees what the accessibility framework exposes and can act through the actions and gestures the framework allows. Apps can mark views as not important for accessibility or hide content from the tree, and that limits both apis equally on reads. On writes, the service is limited to gestures and node actions, while UiAutomation can inject raw events.

Winner on reach: UiAutomation.

lifecycle control

This replaces “rotation control”. It is about who starts, stops, and restarts the thing.

With UiAutomation, you control the lifecycle from the host. You start the instrumentation, you kill it, you restart it. In Appium, the UiAutomator2 server gets reinstalled or restarted by the driver when it crashes. That is a lot of control, and it is also a lot of moving parts.

With AccessibilityService, the system owns the lifecycle. Android binds the service when the user enables it, rebinds it after a reboot, and can kill it under memory pressure. You can ask the user to re-enable it, but you cannot do it silently from inside a normal app. A device with adb access can change the enabled-services setting from the shell, which is how test rigs often handle it, but that is you using the shell privilege again.

Winner on control: UiAutomation, for anyone who has a host. AccessibilityService wins only if “nobody has to start it” is the requirement.

device and os coverage

This replaces “geo coverage”. Both apis are old enough that coverage is broad. The differences are in the details.

UiAutomation has been around since API 18, with later additions like shell permission adoption in API 24. AccessibilityService is older still, but dispatchGesture() and other useful pieces came later, so the gesture story depends on API level.

The practical issue is that vendor skins can change behaviour around background services and accessibility toggles. Some manufacturers aggressively kill background services, which hurts an AccessibilityService more than an instrumentation run that a host is actively babysitting.

Winner on coverage: tie, with a small edge to UiAutomation for predictability under a host.

reliability

This replaces “connection success rate”. Here is where each tends to fail, from operating rigs:

  • UiAutomation failures usually come from the link: adb dropping, the instrumentation process dying, a version mismatch between the server apk and the driver
  • AccessibilityService failures usually come from the system: the service being unbound, the toggle being reset after an update, the app being killed in the background, or the user never having enabled it

One more known gotcha. UiAutomation and a running accessibility service can interfere with each other, because UiAutomation connects through the same accessibility machinery. Android added a flag, FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES, in API 24 so a test can keep other services running, and by default other services may be suppressed during a UiAutomation session. If your test rig has a screen reader or another accessibility tool installed, check this before blaming your selectors.

Winner on reliability: it depends on which failure you can live with. For unattended on-device work, AccessibilityService has fewer links to break. For supervised test runs, UiAutomation is easier to debug.

speed

Both are fast enough that your app’s own rendering and your wait strategy dominate.

What does differ is round trips. Through Appium, each command is an http call from your test to the server on the phone, and that call is a network hop if the phone is remote. An AccessibilityService running on the phone has no such hop, because the logic lives on the device. If your script does hundreds of small queries per screen, moving the logic onto the device can matter. If it does a handful, it will not.

Event-driven code also helps. An AccessibilityService gets pushed events when windows change, so it does not have to poll. UiAutomator wait helpers poll or listen depending on the call, and idle-waiting behaviour has its own settings in the driver.

Winner on speed: AccessibilityService for chatty on-device logic, tie otherwise.

cost

This replaces “pricing per GB”. Neither api has a price. Both are part of Android, and as of October 2026 there is no fee to use either one.

The real costs are elsewhere:

  • UiAutomation: the host machine, the usb hub or wifi setup, and your time maintaining the driver stack. see host limits when running many android phones for where those limits show up
  • AccessibilityService: the engineering time to build and sign an apk, the consent flow, and any store policy review if you publish

If you already use Appium, UiAutomation is the cheaper path because you inherit a mature driver. If you are writing a product that lives on the phone, AccessibilityService is the cheaper path because you inherit nothing you do not need.

Winner on cost: UiAutomation for test teams, AccessibilityService for on-device products.

session persistence

How long does the automation stay alive?

An instrumentation session lasts as long as the host keeps it alive. Close the laptop lid, drop the adb link, and the session is gone.

An enabled AccessibilityService persists across app restarts and, normally, reboots. It stays enabled until the user or a shell command turns it off, or the system disables it, for example after the app is updated or crashes repeatedly.

If you need something that survives without a host, AccessibilityService wins. If you need a clean, disposable session per test, UiAutomation wins, because disposable is what you want in testing.

concurrent runs

This replaces “concurrent connections”. On the device itself, you get one UiAutomation connection at a time per device in practice, and the system does not let multiple instrumentation sessions fight over the same device cleanly. Scaling means more devices, each with its own adb serial and its own driver port.

AccessibilityService also has one enabled instance per app, but multiple different services from different apps can be enabled at once, subject to the suppression behaviour above.

Across many phones, UiAutomation scales with your host: usb ports, cpu, and adb stability. AccessibilityService scales with the phones themselves because each one runs its own logic. That is a real advantage for fleets, at the price of installing and enabling your apk on every device.

Winner on concurrency: AccessibilityService for fleets with no central controller, UiAutomation for fleets with one.

use-case verdicts

regression tests for an app you own

UiAutomation, almost always through UiAutomator2 or Appium. You get host-side control, shell-level setup, and a stack that the test community already debugs in public. If you are choosing between frameworks, I compared the options in Maestro vs Appium on Android. If you are wondering whether the older selector class is still safe to use, read is UiSelector deprecated in Android’s UI Automator.

Winner: UiAutomation.

an assistive tool that helps a user operate their own phone

AccessibilityService. This is what it was designed for, the user consents in Settings, and there is no host. Nothing in a test api is designed for this.

Winner: AccessibilityService.

on-device automation that runs without a laptop attached

This is the grey zone. AccessibilityService can do it, as long as the user enables it and the device is yours or you are authorised to automate it. Be careful about the target apps: automate only apps and devices you own or are authorised to test, and respect each platform’s terms. I will not go into how to get around app protections, and nothing in either api is meant for that.

If the job is just “run my own test suite nightly”, you do not need an on-device service. A small host that launches instrumentation is simpler.

Winner: AccessibilityService, with the caveat that a tiny host plus UiAutomation often ends up less work.

testing on real carrier networks from a remote location

Here the api question gets less important than the device question. If you need a real phone on a real Singapore mobile connection, you need hardware with a Singapore sim, and you need a path to drive it. I covered why this matters in testing apps on real carrier networks.

One option is cloudf.one, which rents real Android phones in Singapore on dedicated hardware, each with a persistent Singapore mobile IP, controlled from the browser. It is Singapore only. To be exact about adb: there is no self-service adb on cloudf.one. adb and Appium reach a rented phone only through a WireGuard tunnel that we set up on request, by emailing [email protected]. So if your plan is UiAutomator2 against a rented phone, that is the route, and it is not instant. If browser control is enough, you do not need it.

Winner: UiAutomation for scripted tests over that tunnel, with the logistics above.

who should pick UiAutomation

  • you are a QA engineer or run a CI pipeline and the app under test is yours
  • you want one driver across many devices, with the host in charge
  • you need shell-level setup before tests, like granting permissions or pushing files. if you move media around, making adb-pushed media files show up in the android gallery is a good companion read
  • you want disposable sessions and clear failure modes
  • you are fine running a host

If you need root, that is a different decision with its own tradeoffs, and I wrote about it in root vs non-root android automation. You often do not need root to get what UiAutomation gives you.

who should pick AccessibilityService

  • you are building a tool for users who need assistive features on their own phone
  • you need logic that runs on the device with no host attached
  • you can accept the consent flow and the store policy that comes with it
  • you are shipping to real users, not testing in a lab
  • you are prepared to handle the service being unbound or disabled and to tell the user clearly when it is

verdict overall

There is no single winner. UiAutomation is the shell-privileged testing api that a host starts through instrumentation, and AccessibilityService is the app-installed api that the user enables in Settings. UiAutomation reaches further and is easier to control from a laptop. AccessibilityService runs by itself and is meant for assistive and on-device use. They share the accessibility machinery underneath, which is why they can get in each other’s way, and why mixing them on one device needs care.

My rule of thumb: if a human will not be holding the phone and you own the app, start with UiAutomation. If the phone is the user’s and the tool lives on it, use AccessibilityService. Everything in between is a judgement call about where you want your moving parts, on the host or on the device.

For the rest of the adb and automation notes on this site, the blog index is the place to start.

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-10-15.

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 →