fix: harden andro (wait timeout, AVD drift, profile mix-up, clean residue, reinstall target) - #2
Merged
Merged
Conversation
anyhow 1.0.102 is flagged unsound by RUSTSEC-2026-0190 (downcast_mut); 1.0.103 fixes it. Refresh the whole lockfile — Cargo.toml is untouched, and cargo audit now reports zero warnings.
wait_ready started with `adb wait-for-device`, which blocks forever when nothing is attached: `andro wait --timeout 3` never returned, and `andro up` hung for good when the emulator crashed at startup. The existing poll loop is enough — `adb shell getprop` fails immediately without a device — so the call is gone. boot() now keeps the spawned emulator handle and try_wait()s it on each turn: a non-zero exit fails fast with the tail of ~/.andro/emulator.log instead of polling a dead process for the full 300s.
`Sdk::command` created `<home>/{home/.android,tmp,avd}` on every call, so any
adb-backed command run after `andro clean` — `status`, `stop` — rebuilt the
tree that clean had just removed, contradicting "nothing left behind".
The dirs are now only created when the home already exists. A missing home has
no adb to run anyway, so those commands fail cleanly ("not running", exit 3),
and provisioning still creates the home in `ensure_jdk` before the first tool
call.
ensure_avd only checked that `<name>.ini` existed, so changing `--api` or `--device` after the first run was silently ignored: an AVD created at android-34 kept booting android-34 even though `andro doctor` announced android-36 and the newer image was installed next to it. The AVD's config.ini is now compared against the requested image and device (`avd_matches`, a pure parser with unit tests). On a mismatch the AVD is deleted and recreated — it is disposable by design, and the SDK image is not re-downloaded. If it is currently running, andro refuses and asks for `andro stop` first rather than pulling the rug from under a live emulator.
`boot` short-circuited on `is_booted`, which only asks whether *something* is booted. With `andro-tv` up, a plain `andro run app.apk` installed and launched the phone app on the TV emulator without a word. `up`/`boot` now ask the console which AVD is attached (`adb emu avd name`, parsed by the pure `parse_avd_name`) and bail with "run `andro stop` first" when it belongs to the other profile. `andro status` reports the running AVD too, in the text output and as `running_avd` in the JSON, so the mismatch is visible before it bites.
Reinstalling an app the emulator already has adds nothing to `pm list packages`, so `run` fell through to `after.last()` and launched an arbitrary third-party package — the second `andro run` of the same APK could start a completely different app. It now reads `dumpsys package packages` and picks the third-party package with the freshest `lastUpdateTime` (`most_recently_updated`, a pure parser with unit tests). `after.last()` stays as the last resort.
"How it works" now says the AVD is recreated when --api/--device change, and that run/up refuse to drive the other profile's running emulator.
maxgfr
force-pushed
the
fix/verify-and-harden
branch
from
September 4, 2026 20:24
b5a04bf to
fa5f4dc
Compare
`adb emu kill` is fire-and-forget and does not always take — a kill sent while the emulator is still early in its boot is acknowledged and ignored. `stop` discarded the result and printed "⏹ emulator stopped" regardless, so it exited 0 with a live emulator still attached and the next `run` landed on it. `clean` had the worse version of the same bug: it deleted `~/.andro` right after the unconfirmed kill, which can leave a running emulator with no adb left to stop it. Both now go through one helper that polls until the device really detaches, asks a second time if it did not, and errors out instead of lying. A normal stop still returns in about two seconds.
maxgfr
force-pushed
the
fix/verify-and-harden
branch
from
September 4, 2026 20:26
fa5f4dc to
1b780a8
Compare
|
🎉 This PR is included in version 1.2.1 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Full pass over the repo: the CI gates were already green on
main, but realruns turned up seven issues. Each is fixed in its own commit, with a pure parser
plus unit tests wherever the logic allows it.
What was broken
anyhow1.0.102 is flagged unsound by RUSTSEC-2026-0190 (downcast_mut)cargo update(lockfile only);cargo auditnow reports zero warningsandro wait --timeout Nnever gave up, andandro uphung forever when the emulator crashed at startup —wait_readybegan withadb wait-for-device, which blocks indefinitely with no device attachedemulator.logstatus/stopaftercleanrecreated~/.andro/{avd,tmp,home/.android}—Sdk::commandcreated them unconditionally--apior--devicedid nothing once the AVD existed —ensure_avdonly checked that the.iniwas there. On my machine theandroAVD sat on android-34 whiledoctorannounced android-36config.iniagainst the requested image and device, and recreate it on a mismatch (refusing while it is running)andro-tvup,andro run app.apkinstalled and launched on the TV emulator —boot()short-circuits onis_bootedwithout checking which AVD is upadb emu avd nameand bail with "runandro stopfirst";statusnow reports the running AVDrunfell back toafter.last()when nothing new appeared inpm list packageslastUpdateTimefromdumpsys package packagesandro stopprinted "emulator stopped" and exited 0 even when the emulator was still attached, andcleandeleted~/.androright after the same unconfirmed kill —adb emu killis fire-and-forget and is ignored when sent during early bootItem 7 was found while verifying the rest, not in the original list.
Verification
CI gates locally, all green:
cargo fmt --check,cargo clippy --all-targets -D warnings,cargo test(51 unit + 12 CLI, 2 e2e ignored),cargo audit(0 warnings),cargo build --release.End to end on a provisioned macOS SDK:
andro upprintedrecreating AVD 'andro' (android-36/google_apis/arm64-v8a on pixel_7 → … on pixel),booted, and
config.inimatched the request afterwards. The same path also moved thepre-existing AVD from android-34 to android-36.
AVD 'andro' is … on pixel_7 but … on pixel was requested — run andro stop first.andro up --tvwith the phone AVD running was refused:emulator 'andro' is running but profile Tv (AVD 'andro-tv') was requested.andro status --jsonreports"running_avd":"andro".andro wait --timeout 3with the real SDK and no device errors out in 3.03s; it used to block forever.clean --yeson a throwaway--home, thenstatus: exit 3 and the directory stays gone.emu killwas ignored,the retry took, and the emulator was really gone (22s). A normal stop takes about 2s.
dumpsys package packagesoutput from the booted android-36 image. The fullinstall-twice loop was not run: no APK was available locally.