Take the emulator half out of run-android.sh
Which AVD this checkout means, creating it, booting it headless and refusing to start one the machine has no room for is the same sequence in ai-app, ai-app-2 and dev-updater. It now lives once, in ~/repos/emulator-tools, and this script is what is actually specific to this project: a build, an install and a launch. Three copies of "boot an emulator" was three places for the memory check none of them had -- starting one at 2.8 GB available invoked the OOM killer, and what it took first was another session's emulator. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1d843f20f2
commit
558095520d
2 files changed
+39
-101
No files matched your search
@@ -225,16 +225,19 @@ first if a remote spawn ever mangles an argument.
|
|||||||
The emulator app reaches it at `https://10.0.2.2:8443`; enroll it with
|
The emulator app reaches it at `https://10.0.2.2:8443`; enroll it with
|
||||||
`adb -s "$SERIAL" shell "am start -a android.intent.action.VIEW -d 'aiapp://enroll?host=10.0.2.2&port=8443&token=…'"`
|
`adb -s "$SERIAL" shell "am start -a android.intent.action.VIEW -d 'aiapp://enroll?host=10.0.2.2&port=8443&token=…'"`
|
||||||
(quote so the device shell doesn't eat the `&`s).
|
(quote so the device shell doesn't eat the `&`s).
|
||||||
- **Name the device on every `adb` call.** This checkout runs its own
|
- **The emulator is `~/repos/emulator-tools`' business, not this repo's.**
|
||||||
emulator — `run-android.sh` derives the AVD name from the directory, so
|
`emu up` creates and boots the AVD named after this checkout (`ai-app-2`),
|
||||||
a second clone gets a second AVD rather than queueing for one shared
|
refusing when the machine has no room for one; `emu list` says what is
|
||||||
machine-wide `tdep`. With more than one attached, a bare `adb shell` or
|
attached and what it costs; `emu down` stops it. `run-android.sh` is that
|
||||||
`adb get-state` fails with `more than one device/emulator` and a bare
|
plus a build and an install. Run that repo's `install.sh` once if `emu` is
|
||||||
`adb shell pm list packages` comes back **empty**, which reads as the app
|
missing.
|
||||||
having been uninstalled rather than as the question being ambiguous.
|
The `adb` on `PATH` after sourcing `android-env.sh` is that repo's wrapper,
|
||||||
Get the serial the way `run-android.sh` does — match `adb -s <serial> emu
|
which fills in `-s` from the same rule — so a bare `adb shell` reaches this
|
||||||
avd name` against the AVD — or export `ANDROID_SERIAL`, which every
|
checkout's emulator and refuses to reach another one's. That defaulting is
|
||||||
`adb` call honours without `-s`.
|
what makes the old advice unnecessary rather than wrong: with two attached
|
||||||
|
and no `-s`, a bare `adb shell pm list packages` comes back **empty**,
|
||||||
|
which reads as the app having been uninstalled rather than as the question
|
||||||
|
being ambiguous.
|
||||||
|
|
||||||
## Where things run (host vs this VM)
|
## Where things run (host vs this VM)
|
||||||
|
|
||||||
|
|||||||
+26
-91
@@ -1,7 +1,13 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Builds and runs this app on an emulator, creating/booting the AVD first if
|
# Builds this app and runs it on this checkout's emulator.
|
||||||
# it isn't already up. Same flow as dev-updater's run-android.sh; see that
|
#
|
||||||
# script for the reasoning behind the avd handling.
|
# The emulator half of this -- which AVD this checkout means, creating it,
|
||||||
|
# booting it headless, and refusing to start one the machine has no room for
|
||||||
|
# -- lives in ~/repos/emulator-tools and is shared with every other Android
|
||||||
|
# checkout here. This script kept its own copy of that sequence until
|
||||||
|
# 2026-08-30, as did dev-updater's and ai-app's, and three copies of "boot an
|
||||||
|
# emulator" is three places for the memory check that was missing from all of
|
||||||
|
# them.
|
||||||
#
|
#
|
||||||
# Environment setup (SDK location, PATH, ...) lives in ./android-env.sh,
|
# Environment setup (SDK location, PATH, ...) lives in ./android-env.sh,
|
||||||
# which can also be sourced directly for one-off commands.
|
# which can also be sourced directly for one-off commands.
|
||||||
@@ -9,105 +15,34 @@ set -eu
|
|||||||
|
|
||||||
APP_ID="com.example.aiapp"
|
APP_ID="com.example.aiapp"
|
||||||
|
|
||||||
DEVICE_PROFILE="${DEVICE_PROFILE:-pixel_10}"
|
|
||||||
SYSTEM_IMAGE="${SYSTEM_IMAGE:-system-images;android-36;google_apis;x86_64}"
|
|
||||||
|
|
||||||
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||||
cd "$SCRIPT_DIR"
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
# One AVD per checkout, named after it -- so two clones of this repo, or a
|
|
||||||
# clone and a worktree, each get their own rather than fighting over one.
|
|
||||||
# This used to default to a machine-wide "tdep", which made the emulator the
|
|
||||||
# one thing here that could not be worked on in parallel: taking it meant
|
|
||||||
# asking whoever had it, waiting, and handing it back, and installing onto a
|
|
||||||
# running one steals the foreground from whatever they were looking at.
|
|
||||||
# Derived rather than written down, so neither clone names the other's.
|
|
||||||
# Override with AVD_NAME=... to share one deliberately.
|
|
||||||
AVD_NAME="${AVD_NAME:-$(basename "$(dirname "$SCRIPT_DIR")")}"
|
|
||||||
|
|
||||||
# shellcheck source=./android-env.sh
|
# shellcheck source=./android-env.sh
|
||||||
. ./android-env.sh
|
. ./android-env.sh
|
||||||
|
|
||||||
# Prints the adb serial of a running instance of AVD "$1", or nothing.
|
if ! command -v emu >/dev/null 2>&1; then
|
||||||
avd_serial() {
|
echo "run-android.sh: no 'emu' command." >&2
|
||||||
for s in $(adb devices | awk '$2 == "device" {print $1}'); do
|
echo " It comes from ~/repos/emulator-tools; run that repo's ./install.sh." >&2
|
||||||
if [ "$(adb -s "$s" emu avd name 2>/dev/null | head -n1 | tr -d '\r')" = "$1" ]; then
|
exit 127
|
||||||
echo "$s"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "==> Ensuring emulator system image is installed"
|
|
||||||
android sdk install emulator "$SYSTEM_IMAGE" || echo " (non-fatal: see above)"
|
|
||||||
|
|
||||||
if [ ! -f "$ANDROID_AVD_HOME/$AVD_NAME.ini" ]; then
|
|
||||||
echo "==> Creating AVD '$AVD_NAME' ($DEVICE_PROFILE, $SYSTEM_IMAGE)"
|
|
||||||
echo no | avdmanager create avd \
|
|
||||||
-n "$AVD_NAME" \
|
|
||||||
-k "$SYSTEM_IMAGE" \
|
|
||||||
--device "$DEVICE_PROFILE" \
|
|
||||||
--sdcard 512M
|
|
||||||
else
|
|
||||||
echo "==> Reusing existing AVD '$AVD_NAME'"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Host keyboard into the emulator -- this app has text fields.
|
# Prints the serial, having created and booted the AVD if it had to. Named
|
||||||
CONFIG_INI="$ANDROID_AVD_HOME/$AVD_NAME.avd/config.ini"
|
# after the checkout, so this cannot land on another session's emulator --
|
||||||
if [ -f "$CONFIG_INI" ]; then
|
# and refuses rather than starting one when the machine is short of memory,
|
||||||
grep -v '^hw\.keyboard=' "$CONFIG_INI" >"$CONFIG_INI.tmp"
|
# because what an OOM kills is somebody else's work rather than the emulator
|
||||||
echo "hw.keyboard=yes" >>"$CONFIG_INI.tmp"
|
# that asked for the memory.
|
||||||
mv "$CONFIG_INI.tmp" "$CONFIG_INI"
|
echo "==> Emulator"
|
||||||
fi
|
SERIAL=$(emu up)
|
||||||
|
export ANDROID_SERIAL="$SERIAL"
|
||||||
SERIAL=$(avd_serial "$AVD_NAME")
|
|
||||||
if [ -n "$SERIAL" ]; then
|
|
||||||
echo "==> Emulator '$AVD_NAME' already running ($SERIAL)"
|
|
||||||
else
|
|
||||||
# Clean up a stray/crashed process for this AVD, if any. The bracketed
|
|
||||||
# first character keeps the pattern from matching the shell running
|
|
||||||
# this script -- unbracketed, this kills that shell mid-run.
|
|
||||||
pkill -f "[e]mulator.*-avd $AVD_NAME" >/dev/null 2>&1 || true
|
|
||||||
|
|
||||||
EMU_LOG="/tmp/$AVD_NAME-emulator.log"
|
|
||||||
: >"$EMU_LOG"
|
|
||||||
if [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; then
|
|
||||||
echo "==> Starting emulator '$AVD_NAME' with GPU acceleration (-gpu host)"
|
|
||||||
emulator -avd "$AVD_NAME" -gpu host -no-audio >"$EMU_LOG" 2>&1 &
|
|
||||||
else
|
|
||||||
echo "==> No display -- starting emulator '$AVD_NAME' headless (-gpu swiftshader_indirect)"
|
|
||||||
emulator -avd "$AVD_NAME" -gpu swiftshader_indirect -no-audio -no-window \
|
|
||||||
>"$EMU_LOG" 2>&1 &
|
|
||||||
fi
|
|
||||||
EMU_PID=$!
|
|
||||||
|
|
||||||
i=0
|
|
||||||
booted=""
|
|
||||||
while [ "$i" -lt 150 ]; do
|
|
||||||
if ! kill -0 "$EMU_PID" 2>/dev/null; then
|
|
||||||
echo "Emulator process exited unexpectedly. Log output:" >&2
|
|
||||||
cat "$EMU_LOG" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
SERIAL=$(avd_serial "$AVD_NAME")
|
|
||||||
if [ -n "$SERIAL" ]; then
|
|
||||||
booted=$(adb -s "$SERIAL" shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')
|
|
||||||
[ "$booted" = "1" ] && break
|
|
||||||
fi
|
|
||||||
i=$((i + 1))
|
|
||||||
sleep 2
|
|
||||||
done
|
|
||||||
if [ "$booted" != "1" ]; then
|
|
||||||
echo "Emulator did not finish booting in time. Log output:" >&2
|
|
||||||
cat "$EMU_LOG" >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "==> Building debug APK"
|
echo "==> Building debug APK"
|
||||||
./gradlew :androidApp:assembleDebug
|
./gradlew :androidApp:assembleDebug
|
||||||
|
|
||||||
APK="androidApp/build/outputs/apk/debug/androidApp-debug.apk"
|
APK="androidApp/build/outputs/apk/debug/androidApp-debug.apk"
|
||||||
echo "==> Installing and launching $APK"
|
echo "==> Installing and launching $APK"
|
||||||
adb -s "$SERIAL" install -r "$APK"
|
# ANDROID_SERIAL above is what aims these; the adb wrapper would work it out
|
||||||
adb -s "$SERIAL" shell am start -n "$APP_ID/.MainActivity"
|
# from the checkout anyway, but a script that says which device it means does
|
||||||
|
# not depend on being run from the right directory.
|
||||||
|
adb install -r "$APK"
|
||||||
|
adb shell am start -n "$APP_ID/.MainActivity"
|
||||||
Reference in new issue
Block a user