https://github.com/maxgfr/andro
Run Android apps (phone & TV) from the macOS command line on a disposable, self-contained Android emulator (Apple Silicon, HVF)
https://github.com/maxgfr/andro
adb andro android android-emulator apple-silicon avd cli devtools emulator hvf macos rust
Last synced: about 2 months ago
JSON representation
Run Android apps (phone & TV) from the macOS command line on a disposable, self-contained Android emulator (Apple Silicon, HVF)
- Host: GitHub
- URL: https://github.com/maxgfr/andro
- Owner: maxgfr
- License: mit
- Created: 2026-06-13T16:32:48.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-13T19:01:07.000Z (about 2 months ago)
- Last Synced: 2026-06-13T21:05:34.412Z (about 2 months ago)
- Topics: adb, andro, android, android-emulator, apple-silicon, avd, cli, devtools, emulator, hvf, macos, rust
- Language: Rust
- Size: 67.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# andro
Run Android apps on phone **or smart TV** — from your macOS command line, on a
fast, **disposable** Android emulator. No Android Studio, nothing installed
system-wide — andro downloads a self-contained JDK + Android SDK into `~/.andro`,
and `andro clean` wipes everything.
```sh
andro run app.apk # boot a phone emulator, install, launch
andro run app.xapk # split-APK bundles (.xapk/.apks/.apkm) and split dirs too
andro run app.aab # Android App Bundles too (built with bundletool on the fly)
andro --tv run tvapp.apk # same, on an Android TV (leanback) emulator
andro clean --yes # kill the emulator and delete ~/.andro
```
The emulator runs **natively on Apple Silicon** via Apple's Hypervisor.framework
(HVF) with **arm64** system images, so it boots in seconds and runs arm64 apps
without translation.
> Why native and not Docker? The Android emulator needs hardware virtualization.
> On a Mac that's HVF, which only a native process can use — a container can't
> (`docker-android` needs Linux + `/dev/kvm`). So `andro` drives the real SDK
> emulator directly.
## Requirements
- **macOS** (Apple Silicon recommended; Intel works with x86_64 images).
- That's it — `andro` downloads a self-contained JDK + Android SDK into
`~/.andro` on first run (~2 GB).
## Install
```sh
brew install maxgfr/tap/andro # Homebrew (macOS) — recommended
# or:
cargo install --path . # build from source
# or grab a prebuilt binary from the GitHub Releases
```
## Usage
```sh
# run & install
andro run # provision (first run) → boot → install → launch
andro --tv run # Android TV profile (TV image + leanback launcher)
andro install # install only (no launch)
andro launch # launch an already-installed package
andro uninstall # remove a package
andro clear # wipe an app's data — fast pristine first-run
andro list # list installed (third-party) packages [--system --json]
andro test # run instrumentation tests (am instrument)
# inspect & interact
andro open # fire a VIEW intent — deep-link / URL-scheme testing
andro screenshot [out.png] # capture the screen (defaults to ./screenshot.png)
andro record [out.mp4] # record the screen [--time , max 180]
andro shell # passthrough to `adb shell` (e.g. andro shell getprop)
andro applog # stream just this app's logcat [--clear --crash]
andro logcat # stream the whole device log [--dump --clear]
andro push # copy a file onto the device
andro pull [local] # copy a file off the device (→ ~/.andro/tmp)
andro reverse # bridge a Mac host port into the emulator [--list --remove-all]
# lifecycle & housekeeping
andro up # provision + boot
andro wait # block until the emulator is ready (scriptable)
andro status # running/booted? exits 0/2/3 [--json]
andro stop # stop the emulator, keep ~/.andro
andro autoclean # reclaim temp/cache/logs [--reset --images --deep --dry-run --yes]
andro clean --yes # kill the emulator and remove ~/.andro
andro doctor # check macOS/Apple Silicon, HVF, paths, adb port
andro completions # print a bash/zsh/fish completion script
```
`` for `run`/`install` is a single `.apk`, a **directory of split APKs**
(installed with `install-multiple`), a **`.xapk` / `.apks` / `.apkm` bundle**
(unzipped, then installed), or an **`.aab`** (Android App Bundle — bundletool builds
the splits matching the emulator, then they're installed). Apps install with
permissions pre-granted (`adb -g`).
### Options (global)
| Flag | Default | Meaning |
|------|---------|---------|
| `--tv` | off | Use the Android TV profile (leanback). |
| `--profile
` | `phone` | Form factor: `phone` or `tv` (shorthand for `--tv`). |
| `--playstore` | off | Use a Play Store image (`google_apis_playstore`); phone only. |
| `--api ` | `36` (Android 16) | Android API level. |
| `--device
` | `pixel` / `tv_1080p` | avdmanager device profile. |
| `--home
` | `$ANDRO_HOME` or `~/.andro` | Self-contained SDK location. |
| `--snapshot` / `--fast` | off | Persist a quickboot snapshot for ~2-5s reboots (state survives until `clean`). |
| `--no-window` | off | Run the emulator headless (e.g. on CI). |
| `--cores ` / `--memory ` | auto | Emulator CPU / RAM overrides. |
| `--api`/`--device` per run | — | also via `ANDRO_API`, `ANDRO_HOME` env. |
`run`/`install` flags: `--downgrade` (allow an older version, `adb -d`).
`run` also takes `--no-launch` (install only) and `--clean-after` (tear down afterwards).
### Config file
Drop a `config.toml` in your andro home (`~/.andro/config.toml`, or
`$ANDRO_HOME/config.toml`) to set defaults. Precedence is **CLI flag > `ANDRO_*`
env > config file > built-in**:
```toml
profile = "tv" # phone | tv
api = 34
device = "tv_4k"
playstore = false
snapshot = false # = --snapshot/--fast
no_window = false
cores = 6
memory = 4096
```
### Shell completions
```sh
andro completions zsh > "${fpath[1]}/_andro" # zsh
andro completions bash > /usr/local/etc/bash_completion.d/andro
andro completions fish > ~/.config/fish/completions/andro.fish
```
## Housekeeping: `autoclean`
`clean` is the nuclear option (removes all of `~/.andro`). `autoclean` reclaims
space **without** re-downloading the SDK/JDK/active image:
```sh
andro autoclean # SAFE: wipe tmp/, caches, stale locks, rotate the log
andro autoclean --dry-run # preview what would be freed, delete nothing
andro autoclean --reset --yes # also FACTORY-RESET the device (wipe userdata; no re-download)
andro autoclean --images --yes # also prune system images no AVD references (re-download on next use)
andro autoclean --deep --yes # = --reset + --images
andro autoclean --legacy --yes # remove pre-containment residue from ~/.android (only if andro-only)
```
It refuses while the emulator is running (`andro stop` first), reports bytes
freed per category, and **never deletes files it didn't create** (e.g. a
screenshot you saved into the home dir is listed and skipped).
## Self-contained — nothing left behind
Everything andro creates lives under `~/.andro` (its own JDK, SDK, AVDs, **and**
the tools' `.android` home and all temp files), so `andro clean` truly leaves
nothing behind. To make that hold even when Android Studio is open, andro runs
its **own adb server on a dedicated port** (so a foreign `adb` server on the
default 5037 can't capture andro's keys). One consequence: andro's emulator
won't show up in a plain `adb devices` on 5037 — use `andro shell` / `andro
status`, and `andro doctor` prints the port. Older installs may have leaked a few
files into `~/.android`; `andro doctor` flags it and `andro autoclean --legacy`
cleans it (only when that dir holds nothing but andro's own residue).
## How it works
On first `run`/`up`, `andro` provisions `~/.andro` end to end:
1. downloads a **Temurin JDK 17** (no brew),
2. downloads the Android **command-line tools**, then installs `platform-tools`,
`emulator` and the chosen **system image** (`google_apis` for phone,
`android-tv` for `--tv`),
3. creates an AVD (`andro` or `andro-tv`),
4. boots the emulator (HVF), waits for `sys.boot_completed`, then waits until
PackageManager actually answers (so the first install can't race the boot).
`run` then installs (`adb install -r -g -t`, or `install-multiple` for split
APKs/bundles — runtime permissions pre-granted), detects the new package (diffing
`pm list packages`), resolves its launchable activity for the profile's category
(`LAUNCHER` / `LEANBACK_LAUNCHER`) and starts it with `am start`.
For an **`.aab`**, andro first fetches a self-contained **bundletool** jar into
`~/.andro` (only on the first `.aab`), reads the booted emulator's device spec, and
builds just the splits that device needs (`build-apks` → `extract-apks`), which then
go through the same `install-multiple` path. The splits are debug-signed by bundletool
— fine for a disposable emulator. Override the bundletool download with
`ANDRO_BUNDLETOOL_URL`.
Everything is contained in `~/.andro`, so cleanup is just removing that folder.
## Known limitations
- **Apps requiring strong Play Integrity / SafetyNet** won't pass attestation on an
emulator even with `--playstore`; that flag only helps apps that check for the
Play Store's *presence* (real GMS), not hardware-attested integrity.
## Development
```sh
cargo test # unit + hermetic CLI tests
cargo clippy --all-targets -- -D warnings
cargo fmt --check
# full end-to-end (needs macOS + ~2GB download):
ANDRO_E2E_APK=/path/app.apk cargo test -- --ignored e2e
```
Releases are cut by **semantic-release** from Conventional Commits; macOS
binaries (arm64 + x86_64) are attached to each GitHub release, and the
[Homebrew tap](https://github.com/maxgfr/homebrew-tap) (`maxgfr/tap/andro`)
auto-updates its formula from the latest release within a day.
## License
MIT © maxgfr