jtube-tv/docs/agent-container.md
Jeena a71de07969 emu: Stop silent early exits in the emulator start script
The script's set -euo pipefail made it die without any visible error in
two places: an empty /tmp/jtube-adbkeys/ failed the glob cat (the error
itself was discarded, so the output just stopped mid-script — this hits
any host after a reboot), and the boot-wait loop was killed on its first
iteration because adb exits non-zero while the device is still booting.
Both were swallowing the whole && chain (adb connect, scrcpy), making it
look like the emulator was broken when it was fine or about to be.

The stale bridge-emulator.sh duplicate (different port, broken vendor
keys path) is removed in the same spirit — keeping it invites running
the wrong script. Docs updated to match the one script.
2026-10-13 08:14:15 +09:00

3.4 KiB

Agent container ↔ host emulator setup

How the testing loop between the JTube TV agent container and the host emulator works, and what needs to exist again after a rebuild.

Topology

  • Emulator: runs on the host (/opt/android-sdk, launched by scripts/start-emulator.sh). Headless is the default; set JTUBE_EMU_MODE=windowed if you want the Qt window (e.g. for desktop interaction testing). View it via scrcpy either way.
  • Only the host can start the emulator (by design — no SSH from the container to the host, no SDK/KVM inside the container). If the agent finds no emulator, it should tell the user to run ./scripts/start-emulator.sh on the host rather than try to work around it. Reachability check: adb connect rutherford:5556 then adb -s rutherford:5556 shell getprop sys.boot_completed → 1.
  • Agent container: image agent-container:latest, container name pattern ac-<project>-<hash>, launched with --network host — it shares the host's network namespace. This is load-bearing: see below.

Why host networking makes adb trivial

With --network host, the container's localhost is the host's localhost. So inside the container:

  • adb connect 127.0.0.1:5556 → hits the host's socat bridge → emulator adbd.
  • The adb server running on 127.0.0.1:5037 is also shared with the host, so the container inherits whatever authorization the host adb server has. No separate RSA handshake is needed. (Caveat: whoever's adb server grabs port 5037 first owns it; all clients share it.)

What must be (re)installed after a container rebuild

  1. android-tools in the container (provides adb; installed into the running container with sudo pacman -S --noconfirm android-tools, but that is ephemeral). To make rebuilds self-sufficient, bake it into the agent-container image at build time.
  2. Nothing else is needed for adb: key handling is inherited from the shared host adb server (see above). If the container is ever switched to bridge networking, use adb connect rutherford:5556 — remote keys are handled by the emulator's ADB_VENDOR_KEYS (next section).

Emulator-side facts (host)

  • Ports: console 5600, adbd 5601 (-port 5600 ⇒ console+1), LAN bridge socat TCP-LISTEN:5556 → 127.0.0.1:5601 (0.0.0.0, so remote machines can adb connect rutherford:5556).
  • Remote key whitelisting: the TV system image is a user build — adb push/shell writes to /data/misc/adb/adb_keys are blocked. The script therefore builds /tmp/jtube-adbkeys-combined (host key + every /tmp/jtube-adbkeys/*.pub) and launches the emulator with ADB_VENDOR_KEYS pointing at it. To trust a new client: put its ~/.android/adbkey.pub into /tmp/jtube-adbkeys/ before launching.
  • Logs: /tmp/jtube-emu.log (QEMU), /tmp/jtube-socat.log (bridge), /tmp/jtube-adbkeys-combined (rebuilt each launch).
  • Viewing: scrcpy -s emulator-5600 on the host, or from any LAN machine: adb connect rutherford:5556 && scrcpy -s 127.0.0.1:5556.

Known quirks

  • Emulator Qt toolbar (back/home buttons) is a detached XWayland window that misbehaves on Hyprland — drive the emulator via scrcpy or adb instead.
  • The TV image is a user build: no adb root.
  • Under the Android TV AVD, keyboard input in the Qt window works, but the reliable inputs are scrcpy (b = back, h = home) or adb shell input keyevent.