#!/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 -gpu swiftshader_indirect) fi else WINDOWED=0 # Headless has no X display, so -gpu host/GLX can't init EGL ("Failed to get EGL # display") and the emulator dies on launch — fall back to software rendering. ACCEL+=(-no-window -gpu swiftshader_indirect) 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 sh -c 'cat ~/.android/adbkey.pub' > /tmp/jtube-adbkeys/agent-container.pub mkdir -p /tmp/jtube-adbkeys VKEYS=/tmp/jtube-adbkeys-combined { cat "$HOME/.android/adbkey.pub" # Optional extra keys — must not fail the script under `set -e` when absent. shopt -s nullglob for k in /tmp/jtube-adbkeys/*.pub; do cat "$k"; done shopt -u nullglob } > "$VKEYS" chmod 600 "$VKEYS" echo "Booting AVD '$AVD' …" ADB_VENDOR_KEYS="$VKEYS" setsid nohup "$EMU" -avd "$AVD" -port "$PORT" "${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 # adb exits non-zero while the device is still booting — don't let # pipefail+set -e kill the wait loop. ok=$( ("$ADB" -s "emulator-$PORT" shell getprop sys.boot_completed 2>/dev/null || true) | tr -d '\r') if [ "$ok" = "1" ]; then echo "BOOTED"; break; fi 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 <