Files
irisandClaude Opus 5 e379edfc1b Aim at the right emulator, and measure the right process
Two corrections from driving these against a live machine. A bare adb call
made from a checkout whose own emulator is down was still handed through to
whatever single device was attached -- which is another session's emulator,
installing over the app they are looking at and taking their foreground, and
looking like it worked. It now refuses and says which serials belong to whom.
A physical phone belongs to no checkout and is left alone, which is why this
asks for AVD names rather than counting devices.

`emu list` was reporting 6 MB for a 3.8 GB emulator: it matched on a -port
that is not in the process's command line at all, and the line it did match
was the shell running the ps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-30 13:09:46 -04:00

145 lines
6.1 KiB
Bash
Executable File

#!/bin/bash
# adb, aimed at this checkout's own emulator, with screenshots scaled down
# before anything reads them.
#
# This is a wrapper, not a separate screenshot command, because a separate
# command is a rule to remember and this has to hold for every agent on
# this machine without anybody opting in. `adb exec-out screencap -p` is
# the normal way to take a screenshot here, so that is the thing that has
# to come back small.
#
# What it costs to read one is set by pixels, not bytes: an image is
# charged at about (width x height) / 750 tokens, after being scaled to fit
# 1568px on its long edge. So a 1080x2424 phone screenshot is ~1460 tokens
# however well the PNG is compressed, and the only lever that moves is the
# size. At 800px on the long edge it is ~380 -- and a 356x800 frame is
# still legible enough to check layout, contrast, alignment and small
# labels, which is what these are looked at for. Raise it for one call with
# ADB_SCREENCAP_MAX_EDGE if something genuinely needs the detail.
#
# The other half is which device a call means. Several agent sessions share
# this machine, each with an emulator named after its checkout, and a bare
# `adb shell` with two of them attached does not fail cleanly: `adb shell pm
# list packages` comes back *empty*, which reads as the app having been
# uninstalled rather than as the question being ambiguous. So when a call
# does not name a device and this checkout's own emulator is running, this
# aims at it. Nothing is guessed when it is not running: adb's own error is
# better than a wrapper picking somebody else's phone.
#
# Everything that is not a screencap is handed straight through, unchanged,
# including its exit status -- a wrapper that breaks `adb install` to save
# tokens on screenshots would not survive the week.
set -euo pipefail
. "$(dirname "$(readlink -f "$0")")/../lib/project-avd.sh"
# See `android_adb`: found without consulting PATH, which is how this script
# was called and would be how it called itself for ever.
real=$(android_adb) || {
echo "adb: no platform-tools under any known SDK; set ANDROID_HOME" >&2
exit 127
}
# Aim at this checkout's emulator, unless the caller has already said which
# device they mean or is asking something that is not about one.
#
# `-s` and friends are only valid before the subcommand, so the scan stops at
# the first word that is not one of adb's own options -- otherwise
# `adb shell echo -s` would look like a device selection.
target=()
if [ -z "${ANDROID_SERIAL:-}" ]; then
subcommand=""
named=false
for arg in "$@"; do
case "$arg" in
-s | -d | -e | -t | -H | -P | -L)
named=true
break
;;
-*) ;;
*)
subcommand="$arg"
break
;;
esac
done
case "$subcommand" in
# Not about one device: `devices` lists them all, and the rest talk to
# the adb server itself. Passing -s to these is at best ignored.
devices | start-server | kill-server | version | help | connect | disconnect) ;;
*)
if [ "$named" = false ]; then
avd=$(project_avd)
serial=$(avd_serial "$real" "$avd" || true)
if [ -n "$serial" ]; then
target=(-s "$serial")
else
# This checkout's emulator is not running, and something
# else's is. Handing the call through would drive their
# device -- installing over the app they are looking at,
# or taking their foreground -- and it would look like it
# worked. A physical phone is not somebody's checkout and
# is left alone, which is why this asks for the AVD name
# rather than counting devices.
others=$(running_avds "$real" | awk -F'\t' -v mine="$avd" '$2 != mine')
if [ -n "$others" ]; then
{
echo "adb: '$avd' is not running, and these emulators belong to other checkouts:"
echo "$others" | while IFS=$'\t' read -r s n; do
printf ' %-16s %s\n' "$s" "$n"
done
echo
echo " emu up start this checkout's own"
echo " adb -s SERIAL ... or say which you meant"
echo " ANDROID_SERIAL=SERIAL for a run of them"
} >&2
exit 1
fi
fi
fi
;;
esac
fi
screencap=false
for arg in "$@"; do
if [ "$arg" = screencap ]; then
screencap=true
break
fi
done
# ffmpeg missing is a reason to hand over the full-size image, not to fail:
# the caller asked for a screenshot and is entitled to one.
if [ "$screencap" != true ] || ! command -v ffmpeg >/dev/null 2>&1; then
exec "$real" "${target[@]}" "$@"
fi
max=${ADB_SCREENCAP_MAX_EDGE:-800}
tmp=$(mktemp -t adb-screencap.XXXXXX.png)
trap 'rm -f "$tmp"' EXIT
status=0
"$real" "${target[@]}" "$@" >"$tmp" || status=$?
# Only a PNG that actually arrived on stdout is ours to shrink. `adb shell
# screencap -p /sdcard/x.png` writes to the device and produces nothing
# here, and feeding that to ffmpeg would turn a working command into a
# failing one; so anything that is not a PNG goes back out byte for byte,
# with the real adb's exit status.
if [ "$status" -ne 0 ] || [ ! -s "$tmp" ] ||
[ "$(head -c 8 "$tmp" | od -An -tx1 | tr -d ' \n')" != "89504e470d0a1a0a" ]; then
cat "$tmp"
exit "$status"
fi
# Never enlarge (min(1,...)), keep the aspect ratio, and keep both edges
# even, which some encoders insist on. If ffmpeg cannot make sense of it,
# the original still goes out: a smaller screenshot is worth having, a
# missing one is not.
scale="min(1,$max/max(iw,ih))"
if ! ffmpeg -y -loglevel error -i "$tmp" \
-vf "scale='trunc(iw*$scale/2)*2':'trunc(ih*$scale/2)*2':flags=area" \
-f image2pipe -c:v png -; then
cat "$tmp"
fi