feat(wear): key watch allow-list on ro.serialno, not adb transport serial
The ALLOWED_SERIALS guard matched the adb transport serial, which over WiFi is a rotating ip:port (changes on every wireless-debugging restart) and over USB is the bare hardware serial. Pinning the transport serial meant every port rotation broke the deploy until the profile was hand-edited. Resolve the device's stable ro.serialno (getprop) after target resolution and match that against ALLOWED_SERIALS instead. The transport serial still drives adb/gradle/ANDROID_SERIAL; only the wrong-watch guard switches to the stable identity, so it survives port rotation and is identical over WiFi/USB. Also: 'devices' now prints 'transport -> ro.serialno' per device to make the value for ALLOWED_SERIALS easy to read off. Help text and the example profile are updated to the ro.serialno form. closes #6
This commit is contained in:
@@ -17,9 +17,12 @@ SERVER_URL="http://minerva.lan:3000"
|
||||
# Generate with: openssl rand -base64 24
|
||||
API_KEY="REPLACE_ME_WITH_PLAINTEXT_API_KEY"
|
||||
|
||||
# Bash array of ADB serials (one per attached watch this profile may target).
|
||||
# Find a watch's serial with: ./run.sh devices
|
||||
# Bash array of stable hardware serials (ro.serialno) — one per attached watch
|
||||
# this profile may target. NOT the adb transport serial (the rotating ip:port
|
||||
# you see over WiFi): the guard reads ro.serialno so it survives port rotation
|
||||
# and is identical over WiFi and USB.
|
||||
# Find a watch's ro.serialno with: ./run.sh devices (prints "transport -> ro.serialno").
|
||||
# Brummel's watch can appear in multiple profiles; Krey's watch only in hers.
|
||||
ALLOWED_SERIALS=(
|
||||
"adb-XXXXXXXX-yyyy._adb-tls-connect._tcp"
|
||||
"XXXXXXXXXXXXXX"
|
||||
)
|
||||
|
||||
+53
-11
@@ -13,9 +13,10 @@
|
||||
# Real Pixel Watch over ADB-WiFi. <profile> is the basename of
|
||||
# a file in profiles.d/<profile>.sh that holds SERVER_URL,
|
||||
# API_KEY, and ALLOWED_SERIALS (see profiles.d/*.sh.example).
|
||||
# The build refuses to proceed if the attached watch's ADB
|
||||
# serial is not listed in the profile's ALLOWED_SERIALS — this
|
||||
# protects against installing the wrong key on the wrong watch.
|
||||
# The build refuses to proceed if the attached watch's stable
|
||||
# hardware serial (ro.serialno) is not listed in the profile's
|
||||
# ALLOWED_SERIALS — this protects against installing the wrong
|
||||
# key on the wrong watch.
|
||||
# emulator Wear OS AVD on this host. No profile — uses the hardcoded
|
||||
# emulator-loopback URL (10.0.2.2:3000) and the build's
|
||||
# MISSING_API_KEY sentinel.
|
||||
@@ -278,10 +279,30 @@ resolve_watch_serial() {
|
||||
RESOLVED_SERIAL="${PICKED%% *}"
|
||||
}
|
||||
|
||||
# Reads the stable hardware serial (ro.serialno) of the device addressed by the
|
||||
# adb transport serial $1. The transport serial depends on the connection:
|
||||
# ip:port over WiFi (rotates on every wireless-debugging restart) or the bare
|
||||
# hardware serial over USB. ro.serialno is burned into the device and constant
|
||||
# across both — the right identity for the wrong-watch guard. Writes the result
|
||||
# to the global RESOLVED_HW_SERIAL (not stdout+$(), so a `die` here aborts the
|
||||
# main shell rather than being swallowed by a subshell — same rationale as
|
||||
# RESOLVED_SERIAL above). Aborts if the device can't be reached.
|
||||
RESOLVED_HW_SERIAL=""
|
||||
resolve_hardware_serial() {
|
||||
detect_adb
|
||||
local transport="$1"
|
||||
local hw
|
||||
hw=$("$ADB" -s "$transport" shell getprop ro.serialno 2>/dev/null | tr -d '\r\n' || true)
|
||||
[[ -n "$hw" ]] || die "Could not read ro.serialno from '$transport'. Is the watch attached and authorized? Check './run.sh devices'."
|
||||
RESOLVED_HW_SERIAL="$hw"
|
||||
}
|
||||
|
||||
# --- Profile loading (watch target only) -----------------------------------
|
||||
# Profiles live in profiles.d/<name>.sh and define SERVER_URL, API_KEY, and
|
||||
# ALLOWED_SERIALS as a bash array. The .sh files are gitignored — see
|
||||
# profiles.d/example-profile.sh.example for the format.
|
||||
# ALLOWED_SERIALS as a bash array. ALLOWED_SERIALS holds stable hardware serials
|
||||
# (ro.serialno), NOT adb transport serials — see resolve_hardware_serial above
|
||||
# for why. The .sh files are gitignored — see profiles.d/example-profile.sh.example
|
||||
# for the format.
|
||||
|
||||
PROFILE_NAME=""
|
||||
SERVER_URL=""
|
||||
@@ -308,16 +329,18 @@ load_profile() {
|
||||
PROFILE_NAME="$name"
|
||||
}
|
||||
|
||||
# Aborts (via die) if $1 is not present in ALLOWED_SERIALS. The error message
|
||||
# names BOTH the attached serial and the allow-list, so the user can see at a
|
||||
# glance whether they grabbed the wrong watch or typed the wrong profile.
|
||||
# Aborts (via die) if $1 is not present in ALLOWED_SERIALS. The caller passes
|
||||
# the device's stable hardware serial (ro.serialno), never the rotating adb
|
||||
# transport serial. The error message names BOTH the hardware serial and the
|
||||
# allow-list, so the user can see at a glance whether they grabbed the wrong
|
||||
# watch or typed the wrong profile.
|
||||
verify_serial_allowed() {
|
||||
local serial="$1"
|
||||
local s
|
||||
for s in "${ALLOWED_SERIALS[@]}"; do
|
||||
[[ "$s" == "$serial" ]] && return 0
|
||||
done
|
||||
die "Watch serial '$serial' is NOT in ALLOWED_SERIALS for profile '$PROFILE_NAME'.
|
||||
die "Watch hardware serial (ro.serialno) '$serial' is NOT in ALLOWED_SERIALS for profile '$PROFILE_NAME'.
|
||||
Allowed: ${ALLOWED_SERIALS[*]}
|
||||
This refusal protects against installing the wrong key on the wrong watch
|
||||
(e.g. Brummel-key on Krey's watch → patient data goes to dev server)."
|
||||
@@ -334,13 +357,18 @@ Available profiles: $(cd "$SCRIPT_DIR/profiles.d" 2>/dev/null && ls *.sh 2>/dev/
|
||||
resolve_watch_serial
|
||||
ADB_SERIAL="$RESOLVED_SERIAL"
|
||||
fi
|
||||
verify_serial_allowed "$ADB_SERIAL"
|
||||
# ADB_SERIAL stays the transport serial (adb addressing, gradle,
|
||||
# ANDROID_SERIAL). The wrong-watch guard checks the stable hardware
|
||||
# serial instead, so it survives WiFi port rotation and matches the
|
||||
# same value whether connected over WiFi or USB.
|
||||
resolve_hardware_serial "$ADB_SERIAL"
|
||||
verify_serial_allowed "$RESOLVED_HW_SERIAL"
|
||||
GRADLE_PROPS+=(
|
||||
"-Pdoctate.serverUrl=$SERVER_URL"
|
||||
"-Pdoctate.apiKey=$API_KEY"
|
||||
"-Pdoctate.profileName=$DOCTATE_PROFILE"
|
||||
)
|
||||
info "Target: watch (profile=$DOCTATE_PROFILE, serial=$ADB_SERIAL, server=$SERVER_URL)"
|
||||
info "Target: watch (profile=$DOCTATE_PROFILE, transport=$ADB_SERIAL, hw=$RESOLVED_HW_SERIAL, server=$SERVER_URL)"
|
||||
;;
|
||||
emulator)
|
||||
if [[ -z "$ADB_SERIAL" ]]; then
|
||||
@@ -514,6 +542,20 @@ cmd_devices() {
|
||||
detect_adb
|
||||
info "Attached devices:"
|
||||
"$ADB" devices -l | sed '1d;/^$/d' | sed 's/^/ /'
|
||||
# Map each transport serial to its stable hardware serial (ro.serialno) —
|
||||
# the value that belongs in a profile's ALLOWED_SERIALS. Over WiFi the
|
||||
# transport serial is a rotating ip:port; over USB it already equals the
|
||||
# hardware serial. getprop makes the right value explicit either way.
|
||||
local -a transports
|
||||
mapfile -t transports < <("$ADB" devices | awk 'NR>1 && $2=="device"{print $1}')
|
||||
if [[ "${#transports[@]}" -gt 0 ]]; then
|
||||
info "Hardware serials (ro.serialno — use these in ALLOWED_SERIALS):"
|
||||
local t hw
|
||||
for t in "${transports[@]}"; do
|
||||
hw=$("$ADB" -s "$t" shell getprop ro.serialno 2>/dev/null | tr -d '\r\n' || true)
|
||||
printf " %s -> %s\n" "$t" "${hw:-<unreachable>}"
|
||||
done
|
||||
fi
|
||||
if [[ -n "$ADB_SERIAL" ]]; then
|
||||
info "Pinned by current target: $ADB_SERIAL"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user