Skip to content

[pull] main from etchdroid:main - #57

Open
pull[bot] wants to merge 48 commits into
Mu-L:mainfrom
etchdroid:main
Open

pull[bot] wants to merge 48 commits into
Mu-L:mainfrom
etchdroid:main

Conversation

@pull

@pull pull Bot commented Aug 12, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

depau added 12 commits August 12, 2026 15:18
…phemeral

Bliss OS is x86_64-only, so the e2e VM couldn't run on Apple Silicon at
all: there is no hardware virt for x86 guests, and QEMU disables MTTCG for
a strongly-ordered guest on a weakly-ordered host, so it would be
single-threaded TCG regardless of -smp.

Switch to LineageOS 23.2 packaged for upstream QEMU, which ships the same
image for arm64 and x86_64. vm-config.sh now derives the qemu binary,
machine, accel and cpu from VM_GUEST_ARCH (default: the host's, the only
case that gets hardware acceleration) and locates UEFI firmware across
Homebrew, Debian and Fedora layouts. The guest boots from disk over UEFI,
so kernel/initrd/cmdline are gone.

The VM is now ephemeral: run-vm.sh branches fresh qcow2 overlays off the
pristine bases on every start, so guest state never persists and tests
cannot leak into each other. Note efi_vars.fd is itself a sparse qcow2
whose virtual size matches the pflash unit, so it must be attached as
qcow2 rather than raw.
An ephemeral VM always boots unprovisioned, and Android 16 then runs adbd
in trade-in mode: the transport looks healthy and advertises shell_v2, but
every command except `tradeinmode` is refused with "error: closed".

wait-vm-startup.sh now waits for a usable transport rather than merely a
connected one, detects the condition, and escapes it with the single
command trade-in mode permits -- `tradeinmode evaluate` -- before marking
the device provisioned, which is what actually turns trade-in mode off.
All steps are idempotent, so it is a no-op on a provisioned guest.
@pull pull Bot locked and limited conversation to collaborators Aug 12, 2026
@pull pull Bot added ⤵️ pull merge-conflict Resolve conflicts manually labels Aug 12, 2026
depau added 15 commits August 13, 2026 00:51
…n macOS

The display backend defaulted to `none`, which meant a local run showed
nothing at all. The options genuinely differ per platform, so default them
rather than making everyone remember: Linux has `sdl`, which opens its own
window and takes gl=on; macOS QEMU from Homebrew has neither sdl nor gtk,
and `cocoa` needs OpenGL that build lacks -- under it SurfaceFlinger
SIGABRTs in a loop and nothing ever draws -- so macOS uses VNC and this
opens Screen Sharing against it automatically. GL follows the same
reasoning: on for local Linux, off under CI and on macOS.

Stopping the viewer needed the final QEMU invocation to stop being an
`exec`, which replaced the shell and left nothing able to clean up
afterwards. QEMU now runs as a child that is waited on, so the EXIT trap
fires on a normal exit and on Ctrl-C alike while still propagating QEMU's
status.

Screen Sharing is one shared app for every session and exposes no
scriptable window list, so closing just our window is not possible. It is
quit only when this run is what launched it, leaving sessions the user
already had open alone. VM_VIEWER=0 opts out entirely.
qemu-kvm-action always builds -cpu, -m, -smp and -enable-kvm from its own
inputs, so a flags list that also carries them produces a command line with
each specified twice, where which one wins depends on argument order.

Separates them into VM_ACCEL_FLAGS, which run-vm.sh passes and CI leaves to
the action's inputs, from VM_QEMU_FLAGS, which is the shared device list and
is safe for both. -machine stays in the shared list because the action has
no input for it.
Creating the throwaway overlays lived inside run-vm.sh, but that script is
only the local entry point: in CI qemu-kvm-action starts QEMU itself, so it
was handed disk paths that nothing had created. Moves the logic into
vm_reset_disks in vm-config.sh, which both entry points already source.

The reset has to run after the guest images are in place, and must not end
up in any cache: the overlays are throwaway, so restoring them would carry
stale guest state into the next run and quietly undo the ephemerality the
suite depends on.
virtio-vga is virtio-gpu plus VGA compatibility and exists only on x86;
arm's virt machine has none, so it needs the plain PCI device. Using
virtio-gpu-pci on x86 left the display frozen from the moment the kernel
took over scanout from UEFI, so CI recordings stopped at the GRUB handoff
even though the guest booted and the tests ran.
depau added 9 commits August 15, 2026 23:58
release the USB device before broadcasting errors; never leak connections;
reject concurrent jobs

a job started while another holds the drive steals the USB interface claim
(USBDEVFS_DISCONNECT_CLAIM), killing the other's transfers; failed inits
leaked claimed connections, making recovery permanently impossible (#449).
The LineageOS guest kernels ship no uhci_hcd (neither x86_64 nor
arm64), so uhci-attached devices never enumerate and the slow-bus
tests fail before the app ever sees the drive. Use an EHCI controller
instead, which both kernels support.

The default stick moves to ehci too: its moderate emulated throughput
keeps UI interaction windows wide. QEMU_USB_BUS must match the bus the
stick boots on, or the replug tests re-add the device on a different
controller than they removed it from, and the guest takes tens of
seconds to notice the new device.
Speed-scaled test images (next commit) reach multiple GB, so the 2G
stick is too small. The qcow2 is sparse and recreated on every VM
start, so the size costs nothing up front; discard=unmap and
detect-zeroes=unmap keep the mostly-zero test images from allocating
real host disk space, which previously filled the disk and made QEMU
pause the VM on ENOSPC.
Emulated USB speed varies by orders of magnitude between hosts, and
fixed image sizes made the timing-sensitive tests flaky: on a fast
host the write finished before the harness could click the skip
verification button.

Measure the drive's throughput once per session with a small
app-driven speedtest write, timed from the app's own logcat
timestamps so UI latency cannot skew it, and size each generic test's
image so the write phase lasts TARGET_WRITE_SECONDS (env-tunable).
The calibration flow retries once, absorbing a known first-boot file
picker flake that would otherwise error every test in the session.
…ta on-device

Apply the measured-speed image sizing to the unplug tests, and
replace the fixed 10 MB pushed payload with dd from /dev/urandom on
the device: pushing speed-scaled amounts of data through Appium as
base64 would take longer than the test itself. Written data is now
verified via sha256 instead of a byte-for-byte payload comparison,
and the host-side raw target image is sized to match (sparse: a
zeroed target cannot false-match a random source).
…r replug

Re-enumeration and the system permission dialog can lag several
seconds while the guest is under heavy write I/O; the previous 1s
wait made the unplug tests flaky.
… open

The tap is occasionally swallowed when a USB hot-plug recomposes the
device list under it, leaving the harness waiting for a picker that
never opened.
depau and others added 12 commits August 16, 2026 16:20
…sitions

The USB permission tap is dropped if it lands while the confirmation
activity is still settling, leaving the app with no permission request
at all and the test staring at a screen that will never change.

The lay-flat skip button needs a retry on stale for the same reason:
the sheet moves under the tap while animating in, and on CI no gravity
sensor ever fires the sheet's onReady, so nobody else will.
…tick

Poll dumpsys usb after a hot-plug instead of sleeping a fixed 2s: the
framework's view is what the app sees, and a fixed sleep loses the race
on slow hosts, so the flow started while the detach had been processed
but not the attach.

Restore the original drive when a setup or reconnect step fails. A
fixture that fails during setup never runs its teardown, so one failure
left every later test with no USB drive at all.

Also drop the over-sized unplug images: both unplugs happen in
different phases (one during the write, one during verification), so
one window is enough, and the bigger job pushed wait_for_success past
its 120s cap because each resume replays work.
Mirrors the boot-time attach in scripts/vm-config.sh: without them a
re-plugged drive allocates its zero writes for real, and the qcow2
balloons until the host disk fills and QEMU pauses the VM.
Android often grants USB permission silently, so waiting out the dialog
timeout on every attempt burned ~150s per test on CI and left the
confirmation screen in a state where the next tap was dropped, so the
job never started at all. Keep the retry, but make the dialog wait
short: the grant button disappearing is the real signal that permission
landed, however it was granted.

Verify the 'write image' tap took as well, by waiting for what must
follow it -- the lay-flat sheet, or the progress screen when the sheet
auto-proceeds on a device with a working gravity sensor.
A fixture imported into two test modules becomes two definitions with
separate caches, so the session-scoped calibration write ran once per
module -- about 70s wasted on CI. Re-export it from conftest instead.
On CI the sheet auto-proceeds -- the emulated gravity sensor reads flat
and the job starts on its own -- so waiting the full timeout for a
button that is already gone burned 15-30s of a ~28s job. The test then
went looking for a write phase that had already finished, which is why
the unplug tests failed while their job ran to a clean FINISHED.

Wait briefly instead: confirm_write_image already established that the
sheet was up, so the button is either there now or the job is running.
…ion dialog

Returning from the system dialog recomposes the banner, so the saved
element reference is stale by the time it is clicked again.
…ounded

onPause unregisters the progress receiver, but the silence watchdog kept
running, so any time away from the progress screen longer than
LAST_NOTIFICATION_TIMEOUT was read as the service having died and raised
a fatal error on a job that was writing perfectly well.

A system permission dialog, the notification settings screen, or simply
leaving the app during a long write all trip it -- writes take minutes,
so backgrounding is normal. In CI it is reliably fatal: the notification
tests spend more than 11s in the system permission dialog.

Only run the watchdog while the screen is actually receiving updates,
and restart its countdown on the way back in.
Job updates are not replayed, so a progress screen that only listens
while resumed misses everything that happens while the user is in a
system dialog or another app -- including FINISHED. Nothing re-syncs
state on the way back, so the screen went on claiming the write was
still running long after it had completed, with no way forward.

Writes take minutes, so leaving the app during one is ordinary. Register
the receiver for the life of the activity instead.
Two races, same cause: the tests had to complete a slow detour through
system UI before the job outran them. The banner only exists while the
job runs, and the skip-verification button only during verification, so
on CI the job was often over first.

Give both jobs room to outlast the detour, and drop the skip click from
these two -- they are about the banner, and test_skip_verification
already covers the button, so waiting for success removes the second
race entirely.

Lower the image size cap as well: with seconds-based windows, a fast
host turns a longer window into gigabytes of pointless I/O, while CI
stays far below the cap.
A tap that lands while a dialog is still animating in is dropped, and
the click call succeeds either way, so the test walked on believing the
dialog was dismissed. It was not: the recording shows the notification
permission dialog still covering a success screen the test then spent
two minutes failing to find, because a system dialog on top is the only
window in the accessibility tree.

Click these until the dialog is actually gone.
Currently translated at 100.0% (134 of 134 strings)

Translation: EtchDroid/EtchDroid
Translate-URL: https://hosted.weblate.org/projects/etchdroid/app/ro/
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

⤵️ pull merge-conflict Resolve conflicts manually

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant