Why uiautomator2's init needs internet access to set up a device
if you have used the python library uiautomator2 (the openatx project, not Google’s own UI Automator framework), you have probably typed python -m uiautomator2 init at some point. it prints a few lines about pushing files to your phone, and then it works. the catch is that on a lot of setups it only works when the machine has internet access, and it fails in a confusing way when it doesn’t.
i run phones for automation work from Singapore, and this question comes up whenever someone builds a locked-down lab. this article explains what init actually puts on the device, why it fetches things instead of shipping them, and what your options are when the network is not there. the short version of the question raised in issue 218 on the uiautomator2 repo is: can init work offline? yes, but you have to supply the files yourself, and the details depend on which version you run.
what it is
uiautomator2 is a python wrapper that lets a script on your computer drive an Android device: tap, swipe, read the screen, find elements. the python library cannot do any of that alone, because the actual work has to happen on the phone. so the library needs a small set of helper programs installed on the device first.
init is the command that installs them. it is a one-time provisioning step per device (and again after some upgrades).
what gets installed depends on the version:
- two APKs,
com.github.uiautomatorandcom.github.uiautomator.test: the first is a small app and the second is an instrumentation package. together they run the UI Automator test framework on the device and expose it over a local HTTP/JSON-RPC server (port 9008 in the versions i have used). - atx-agent: a small Go binary pushed to
/data/local/tmp. in the 2.x line it supervises the uiautomator server, restarts it when it dies, and offers extras like file transfer and a port (7912) for the python side to talk to. - minicap and minitouch (older setups): native helpers for fast screenshots and touch injection, taken from the openstf project, with a different build per CPU architecture and Android version.
one important note on versions. the 3.x releases of uiautomator2 changed this design. as far as i know, 3.x dropped atx-agent and ships the device-side pieces inside the python package, so the download step mostly disappears. i am describing the 2.x behaviour in most of this article because that is where the internet requirement bites. check the release notes in the uiautomator2 repository for the version you actually have, as of October 2026.
how it works
here is the sequence in the 2.x flow, which is the one that needs the network:
- the python side calls
adb devicesand picks a device (by--serialif you pass one). - it asks the device for its CPU architecture (
ro.product.cpu.abi) and Android SDK level, which decide which binary builds are needed. - it downloads the matching atx-agent build from a release page, plus the two APKs and any minicap/minitouch files. by default these URLs point at GitHub releases. that is the internet dependency.
- it pushes the binary to
/data/local/tmpover adb, marks it executable, and installs the APKs with the equivalent ofadb install. - it starts atx-agent as a daemon on the device and checks that the uiautomator server answers.
all of that device interaction rides on adb, the Android Debug Bridge. the internet is only used in step 3, on the computer side. the phone itself does not need a connection for init, only a working adb link to the host.
why download instead of bundling? mostly because of how the project grew. the device-side pieces are built separately (a Java server project, a Go agent, third-party native helpers) and have their own release cycles. fetching them at init time kept the pip package small. it suits a developer laptop, and it is annoying for a rack of phones behind a firewall.
there is a second, quieter failure mode: GitHub release downloads are sometimes slow or blocked, and the timeouts look like device problems. the init command has a mirror option for that case, but a mirror is still internet access.
why it matters
- offline and air-gapped farms: if your test lab has no outbound internet, a default init simply fails. you need a plan before you rack the first device. my write-up on what an android device farm is covers why farms end up isolated like this.
- reproducibility: downloading “whatever is latest” at provisioning time means two phones set up a month apart can end up with different agent versions. pinning your own copies fixes this.
- containers and ci: provisioning inside a clean container on every run means re-downloading every time, which is slow and fragile. i go through that setup in running uiautomator2 device automation inside a docker container.
- supply chain awareness: you are pushing a binary onto a phone and running it. knowing exactly where it came from, and keeping a checked copy, is better than trusting a live download each time.
options for an offline or air-gapped setup
here is what works, roughly from easiest to most manual. as of October 2026, verify each against your installed version.
- upgrade to 3.x if you can. since the device-side pieces ship inside the package, you can fetch the wheel once on a connected machine and install it offline. with pip that is
pip download uiautomator2 -d wheels/on the connected side, thenpip install --no-index --find-links wheels/ uiautomator2on the isolated side. see the pip download documentation. i have not confirmed that every 3.x release is fully self-contained, so test it with the network cable pulled before you trust it. - host your own mirror (2.x): download the release files once, serve them from an internal HTTP server or file share, and point init at it with the mirror option. you keep the stock
initcommand and only change where the bytes come from. this scales best for a farm, because every new device provisions the same way. - push the pieces by hand (2.x): on a connected machine, download the atx-agent build for your architecture and the two APKs from the project’s release pages. then, on the isolated host, use plain adb:
adb installfor each APK,adb pushthe atx-agent binary to/data/local/tmp,adb shell chmod 755on it, and start it with its server command. it works, but you will want to script it for more than a few phones. - bake it into a device image or a provisioning script: once you have a known-good set of files, wrap the manual steps in one script that takes a serial number. treat that script as your real init.
whichever route you pick, store the exact file versions in your own repository or artifact store, and write the versions down.
one more practical point: if you do not have hardware, or your office network is the obstacle rather than a security policy, you can skip the local setup. cloudf.one rents real Android phones in Singapore on dedicated hardware, controlled from the browser, each with a persistent Singapore mobile IP. it is Singapore-only. there is no self-service adb on it: adb and Appium reach a rented phone only through a WireGuard tunnel that we set up on request (email [email protected]). so it suits manual and browser-driven work well, and scripted work only after that tunnel exists. it does not help with a true air-gapped lab, since by definition that has no outside connection.
common misconceptions
- “the phone needs internet for init”: usually not. the download happens on the computer running python. the phone only needs adb connectivity over usb or tcp.
- “init installs one app”: it is more than that. the APKs, the agent binary and sometimes the native helpers are separate pieces, so a half-failed init can leave a device where some things work and others do not. rerunning init usually repairs it.
- “this is the same as Google’s UI Automator”: it is related, not identical. uiautomator2 drives the same underlying framework through its own server, but the python API, the agent and the install flow belong to the openatx project. for the framework itself, see Google’s UI Automator documentation. and if you are comparing the two layers, i wrote about uiautomation versus accessibilityservice.
- “i need root to do this”: no. init works with ordinary adb shell permissions, because
/data/local/tmpis writable by the shell user and the APKs install normally. root is a separate topic, covered in root vs non-root android automation.
a related side note: only run this on phones you own or are authorised to test. init puts a service on the device that can read and control the screen.
where to go from here
- read the blog index for the other adb and automation explainers on this site.
- if you plan to run provisioning in containers, start with running uiautomator2 device automation inside a docker container.
- if you are scaling beyond a few phones, what is an android device farm covers the physical and network side.
- if your scripts later fail to find elements in newer apps, why jetpack compose elements don’t show up in the uiautomator accessibility tree is the next thing worth knowing.
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-21.