jtube-tv/scripts/start-emulator.sh
Jeena 40a9d1e2c6 docs: Agent container emulator workflow
Document how the host emulator is driven over adb from the agent
container: the start script now boots headless by default and trusts
remote adb keys via ADB_VENDOR_KEYS at launch, since the TV system
image is a user build that blocks pushing keys to adb_keys after boot.
AGENTS.md points to the new doc and fixes the stale SDK path.
2026-10-12 09:42:08 +00:00

126 lines
No EOL
4.5 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# start-emulator.sh — boot the JTube TV test emulator on the HOST (not inside the
# agent container), with GPU acceleration, headless by default (drive it via
# adb/scrcpy). JTUBE_EMU_MODE=windowed shows the Qt window.
#
# Boots on console :5600 (adbd :5601); the agent container bridges
# rutherford:5556 -> 127.0.0.1:5601 with socat, so after this script the emulator is
# reachable from anywhere as:
#
# adb connect rutherford:5556 && scrcpy -s rutherford:5556
#
# An RSA authorization popup appears on the emulator window on first connect —
# click "Always allow"/OK.
set -euo pipefail
SDK="${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}"
EMU="$SDK/emulator/emulator"
ADB="$SDK/platform-tools/adb"
SDKM="$SDK/cmdline-tools/latest/bin/sdkmanager"
AVDM="$SDK/cmdline-tools/latest/bin/avdmanager"
AVD="${1:-jtubesc}"
PORT=5600
IMAGE="system-images;android-36;android-tv;x86_64"
[ -x "$EMU" ] || { echo "emulator not found at $EMU — install Android SDK cmdline-tools first"; exit 1; }
# --- bootstrap: java, system image, AVD -------------------------------------
if ! command -v java >/dev/null; then
echo "no java on PATH — required by sdkmanager/AVD tools"
echo " install it (Arch): sudo pacman -S --noconfirm jdk21-openjdk"
echo " then re-run this script"
exit 1
fi
if ! "$SDKM" --list_installed 2>/dev/null | grep -q "$IMAGE"; then
echo "installing $IMAGE (~1.5 GB, once)…"
yes | "$SDKM" "$IMAGE" > /dev/null
echo "installed"
fi
if [ ! -f "$HOME/.android/avd/$AVD.ini" ]; then
echo "creating AVD '$AVD'…"
echo no | "$AVDM" create avd -n "$AVD" -k "$IMAGE" -d tv_1080p > /dev/null
fi
# --- free stale emulator instances holding our port --------------------------
for pid in $(ps aux | awk '/[q]emu-system-x86_64/ {print $2}' 2>/dev/null); do
if tr '\0' ' ' < "/proc/$pid/cmdline" 2>/dev/null | grep -q "\-port $PORT"; then
echo "Killing stale emulator pid $pid"
kill -9 "$pid" 2>/dev/null || true
fi
done
sleep 1
MODE="${JTUBE_EMU_MODE:-headless}" # headless (default) | windowed
WINDOWED=1
ACCEL=()
if [ "$MODE" = windowed ]; then
if [ -n "${WAYLAND_DISPLAY:-}${DISPLAY:-}" ]; then
echo "windowed (JTUBE_EMU_MODE=windowed) — emulator window + GPU host"
else
echo "JTUBE_EMU_MODE=windowed but no display detected — falling back to headless"
WINDOWED=0
ACCEL+=(-no-window)
fi
else
WINDOWED=0
ACCEL+=(-no-window)
echo "headless (default; JTUBE_EMU_MODE=windowed for a window) — no emulator window"
fi
if [ -e /dev/kvm ]; then
ACCEL+=(-accel on)
echo "/dev/kvm found — hardware acceleration on"
fi
# --- build combined adb vendor-keys file ------------------------------------
# The TV system image is a user build: adb push to /data/misc/adb/adb_keys is
# blocked, so remote keys are trusted via ADB_VENDOR_KEYS at launch instead.
# Drop extra public keys into /tmp/jtube-adbkeys/ (one per file), e.g. from the
# agent container: docker exec <ctr> sh -c 'cat ~/.android/adbkey.pub' > /tmp/jtube-adbkeys/agent-container.pub
VKEYS=/tmp/jtube-adbkeys-combined
{
cat "$HOME/.android/adbkey.pub"
cat /tmp/jtube-adbkeys/*.pub 2>/dev/null
} > "$VKEYS"
chmod 600 "$VKEYS"
echo "Booting AVD '$AVD' …"
ADB_VENDOR_KEYS="$VKEYS" setsid nohup "$EMU" -avd "$AVD" -port "$PORT" -gpu host "${ACCEL[@]:-}" \
-no-snapshot > /tmp/jtube-emu.log 2>&1 < /dev/null &
echo "emulator PID $! — log: /tmp/jtube-emu.log"
for i in $(seq 1 90); do
ok=$("$ADB" -s "emulator-$PORT" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
[ "$ok" = "1" ] && { echo "BOOTED"; break; }
sleep 5
done
# --- the bridge: advertise adbd on the LAN as :5556 ---------------------------
if ! ss -tln 2>/dev/null | grep -q ":5556"; then
if command -v socat > /dev/null; then
setsid nohup socat TCP-LISTEN:5556,bind=0.0.0.0,fork,reuseaddr TCP:127.0.0.1:$((PORT+1)) > /tmp/jtube-socat.log 2>&1 < /dev/null &
echo "socat bridge listening on :5556 (pid $!)"
else
echo "install socat for the remote bridge: sudo pacman -S --noconfirm socat"
fi
fi
# Keep the JTube TV app current + running so scrcpy lands on it.
APK="$HOME/Projects/jtube-tv/app/build/outputs/apk/debug/app-debug.apk"
if [ -f "$APK" ]; then
echo "Installing JTube TV…"
"$ADB" -s "emulator-$PORT" install -r "$APK" >/dev/null && echo "installed"
"$ADB" -s "emulator-$PORT" shell am start -n net.jeena.jtubetv/.MainActivity && echo "app started"
fi
cat <<EOF
Ready. From any machine that can reach rutherford:
adb connect rutherford:5556
scrcpy -s rutherford:5556
If scrcpy says "unauthorized", click the RSA dialog on the emulator window.
EOF