Merge pull request 'Android: install and launch the package the APK declares' (#163) from fix/159-android-task-app-id into main
CI / check (push) Skipped
CI / e2e (push) Skipped
Build & publish the Android APK / apk (push) Successful in 1m27s
Build & publish Arch package / arch-package (push) Successful in 2m43s
Attach the desktop build to the release / linux (push) Successful in 57s
Sync Homebrew formula / sync-formula (push) Successful in 6s

This commit was merged in pull request #163.
This commit is contained in:
2026-08-20 19:46:35 +00:00
6 changed files with 514 additions and 83 deletions
@@ -258,20 +258,25 @@ one.
`build/android/Taskfile.yml` ships more than the Makefile wraps, and
they are the right thing to reach for when you want something one-off:
> **Do not run `android:run:device` or `android:deploy-device`
> against a device that has the released app on it (#159).** Both begin
> with `adb uninstall {{.APP_ID}}`, and `APP_ID` defaults to
> `app.yellowjacket` — the **release** id — while `run:device` builds
> the **debug** variant, whose id is `app.yellowjacket.dev`. So it
> uninstalls the user's app, taking the library with it, installs a
> different package, and then fails to launch the one it removed. This
> is "the identity is declared twice" (below) cashing out. The safe
> sequence is at the end of this section.
> **These four were unsafe until #159 and are now the way in.** All of
> them began with `adb uninstall {{.APP_ID}}`, where `APP_ID` defaulted
> to `app.yellowjacket` — the **release** id — while `run` and
> `run:device` build the **debug** variant, whose id is
> `app.yellowjacket.dev`. So they uninstalled the user's app, taking
> the library with it, installed a different package, and then failed
> to launch the one they had removed.
>
> They share `scripts/android-deploy.sh` now, which **never**
> uninstalls (`install -r`, and a changed signing certificate is
> reported with the command rather than acted on), reads the package id
> back out of the built APK, and refuses a target that is not the kind
> the task names. There is nothing left to avoid; the manual sequence
> below is kept because it is still the smallest thing that works.
```
wails3 task android:run # debug build + emulator install + launch
wails3 task android:run:device # UNSAFE, see #159
wails3 task android:deploy-device # UNSAFE, see #159
wails3 task android:run:device # debug build + install + launch on a phone
wails3 task android:deploy-device # release build, same
wails3 task android:bundle:fat # AAB, for a Play Store upload
wails3 task android:studio # open build/android/ in Android Studio
wails3 task android:device:list
@@ -279,6 +284,16 @@ wails3 task android:logs:all
wails3 task android:clean
```
**`run` and `deploy-emulator` mean the emulator, and now say so to
adb.** They used a bare `adb install`, which with exactly one device
attached picks that device whatever it is — so with a phone plugged in
and no emulator running, the task whose summary reads "in the Android
Emulator" installed on the phone. They pass `--target emulator` and
refuse with `make android-emulator` as the remedy.
**`DEVICE_ID=<serial>` still names a device, and several attached
devices is now an error rather than a silent pick of the first.**
Two are deliberately **not** wrapped. `android:logs` greps logcat for
`(Wails|yellowjacket)`, which catches the `WailsBridge` tag but misses
the app's own process tag (`app.yellowjacket` — lowercase, so `Wails`
@@ -288,16 +303,51 @@ instead. And `ensure-emulator` boots whatever `-list-avds | tail -1`
returns, with no pidfile and no boot wait, so it cannot be stopped or
sequenced.
## The identity is declared twice
## The identity is read back from the APK
It used to be **declared twice**, and that is what #159 was.
`applicationId` in `build/android/app/build.gradle` is what Gradle
installs. `APP_ID` in `build/android/Taskfile.yml` is what every
adb-driven task uninstalls, launches and filters. **Nothing enforces
that they agree**, and `ANDROID.md`'s advice to set `APP_ID` in
`build/config.yml` does not work in beta.8 — `wails3 task` never reads
that file (verified with `--dry`), and even when set it feeds only the
adb commands, never Gradle. Change both or the official `run`/`deploy`
tasks address a package that is not installed.
installs; `APP_ID` in `build/android/Taskfile.yml` was what every
adb-driven task uninstalled, launched and filtered, and nothing
enforced that they agree. They did not: the debug buildType carries
`applicationIdSuffix ".dev"`, so every task that assembles a debug APK
addressed the release id. This file flagged the hazard for five phases
and it cashed out twice — once as a wrong `am start`, once as an
uninstall of the user's library.
**`scripts/android-pkgid.sh` is the one answer now.** It prints the
package id an APK declares (`aapt2 dump packagename`, falling back to
`aapt dump badging`), and the deploy path installs and launches *that*.
The APK is the authority because the task that installs it has just
built it: whatever Gradle resolved the applicationId to, suffixes and
flavours included, is in the file, and no default can disagree with it.
An APK it cannot read is a hard failure, never a fallback to a written
down default — guessing is the bug.
**`APP_ID` survives as an assertion, not a setting**, and has no
default. `wails3 task android:run APP_ID=app.yellowjacket` says "this
build had better declare that id" and is refused, naming both, *before*
anything is installed or a device is even chosen. It could never have
been a setting: `ANDROID.md`'s advice to put it in `build/config.yml`
does not work in beta.8 — `wails3 task` never reads that file (verified
with `--dry`) — and even when set it fed only the adb commands, never
Gradle.
`scripts/android-emulator.sh` derives `PKG` the same way, from
`bin/yellowjacket.apk` when one is built, so `make android-install`,
`android-launch`, `android-logs` and `android-smoke` follow whichever
variant is actually in `bin/`. `YJ_ANDROID_PKG` still overrides, and
the old literal survives only for a tree with no APK built yet.
**The uninstall is gone and is not coming back.** It existed to make
the bare `install` on the next line work at all — without `-r` Android
refuses an install over an existing package — so `install -r` removes
the *reason* for it rather than merely removing it. What is left is the
one case an uninstall really is the remedy, a changed signing
certificate, and that is exactly the case where performing it silently
costs the user their library. So it is named and not done, which is the
answer `scripts/android-emulator.sh` had already reached for
`make android-install`.
Related, and it will bite once: the launcher activity is
`com.wails.app.MainActivity` and the applicationId is
@@ -306,8 +356,10 @@ resolves the leading dot against the *applicationId* and fails with a
class-not-found that reads like a broken build. Always the
fully-qualified form.
**The safe way to put a debug build on a real device**, which is what
#52 used and what #159 exists to make unnecessary:
**`wails3 task android:run:device` is the way to put a debug build on a
real device**, since #159. What #52 used, before it was safe, was the
longer form, and it is still the smallest thing that works if you want
no script between you and adb:
```bash
wails3 task android:build ARCH=arm64 && wails3 task android:assemble:apk
@@ -315,9 +367,15 @@ adb install -r bin/yellowjacket.apk # -r, never uninstall
adb shell am start -n app.yellowjacket.dev/com.wails.app.MainActivity
```
`YJ_ANDROID_PKG=app.yellowjacket.dev` points `scripts/android-emulator.sh`
— and therefore `make android-smoke`, `android-logs`, `android-launch`
— at the debug id, which is otherwise `app.yellowjacket`.
The id in that last line is the one thing to keep an eye on by hand —
`./scripts/android-pkgid.sh bin/yellowjacket.apk` is what the tasks ask,
and it is a good habit before any `am start` written out in full.
`YJ_ANDROID_PKG=app.yellowjacket.dev` still overrides what
`scripts/android-emulator.sh` — and therefore `make android-smoke`,
`android-logs`, `android-launch` — addresses, but it is rarely needed
now: that default is read from `bin/yellowjacket.apk`, so it already
follows whichever variant was built last.
## What only a device can answer
+87
View File
@@ -4155,3 +4155,90 @@ This is the hazard that file already names — "The identity is declared
twice ... **Nothing enforces that they agree**" — reached by a second
route: the two ids differ not because someone edited one, but because
the debug buildType suffixes it.
## The uninstall was there to make a bare `install` work (measured 2026-08-20)
Fixing #159 turned up *why* the `adb uninstall` was in all four tasks,
which the issue does not say and which decides whether it can simply be
deleted. The line under it was `adb install`, with **no `-r`** — and
Android refuses an install over an existing package without it. So the
uninstall was not a deliberate clean-slate step; it was the price of
the missing flag, paid on every run, and `install -r` removes the
reason for it rather than merely removing it.
That matters because "should the uninstall go at all" looked like a
trade — drop it and a signing-certificate change fails with
`INSTALL_FAILED_UPDATE_INCOMPATIBLE` instead of being handled. It is
not a trade: nothing else was relying on it. The certificate case is
reported with the command to run, which is what
`scripts/android-emulator.sh` already did for `make android-install`,
so this is one existing judgement applied consistently rather than a
new one.
## `wails3 task android:run` installs on a phone (measured 2026-08-20)
The emulator tasks (`run`, `deploy-emulator`) used a bare `adb install`
with no `-s`. adb with exactly one device attached uses that device
whatever kind it is, so with a phone plugged in and no emulator
running, the task whose summary reads "in the Android Emulator"
installed on the phone — and, before #159 was fixed, ran
`adb uninstall app.yellowjacket` against it first. The reported data
loss was reachable from the *emulator* task, which is not what the
issue describes and is worse, because nothing in the name warns you.
Measured after the fix, phone attached and emulator stopped:
```
$ ./scripts/android-deploy.sh --apk bin/yellowjacket.apk --target emulator
android-deploy: no emulator target is online.
LP3LHMA531900746 device
Start one with: make android-emulator
```
The general form: **a task that names a target has to say so to adb.**
The device tasks always filtered on `$1 !~ /^emulator-/`; the emulator
tasks filtered on nothing at all.
## The package id can be read back, and costs nothing (2026-08-20)
`aapt2 dump packagename <apk>` answers in one word and ~40 ms, from
`$ANDROID_HOME/build-tools/*/aapt2` (versioned, so resolved not
pinned); `aapt dump badging` is the fallback for older build-tools and
is what #159's own measurement used. That is cheap enough to do on
every deploy, which is what makes "the two ids agree by construction"
affordable rather than aspirational — the alternative considered was
giving the debug-flavoured tasks `APP_ID` + `.dev`, which is one line
and leaves the class of bug alive for the next flavour or suffix.
The guard runs **before** a target is chosen, deliberately: it is a
question about the artifact, so it can be exercised with nothing
plugged in, and a build whose id is wrong should be refused whether or
not there is anything to install it onto. That is what let the negative
test run safely with the user's phone attached:
```
$ ./scripts/android-deploy.sh --apk bin/yellowjacket.apk \
--target device --expect app.yellowjacket
android-pkgid: refusing to act on a package this APK does not declare.
the APK declares: app.yellowjacket.dev
the task expects: app.yellowjacket
rc=2
```
That is exactly #159's configuration — debug APK, release id, real
device — refused with no adb call made.
## `make android-emulator`'s boot wait can be satisfied by a phone (2026-08-20)
Noticed while booting the emulator for #159's verification, with a
phone also attached. `scripts/android-emulator.sh start` reported
`waiting for boot ok / android 14` about **eight seconds** after
launching the emulator, which had not appeared in `adb devices` yet —
`pick_device`'s last resort is "exactly one device online", and at that
moment the one online device was the phone. So it waited for the
phone's boot, found it long since booted, and returned. The emulator
took another ~10 s to come up.
Harmless here (the emulator was up before anything used it) and a
straightforward race otherwise: `start` should wait for a device that
is an emulator, not for whatever `pick_device` returns. Filed as #162.
+25 -55
View File
@@ -4,18 +4,28 @@ includes:
common: ../Taskfile.yml
vars:
# The *installed* package name, which every adb-driven task below uses
# to uninstall, launch and filter. It must agree with `applicationId`
# in app/build.gradle, and nothing enforces that.
# APP_ID is an *assertion*, not a setting, and it has no default.
#
# ANDROID.md says to set this in build/config.yml. That does not work
# in beta.8, checked both ways: `wails3 task` builds its var set from
# CLI KEY=VALUE arguments and the Taskfile tree only -- nothing reads
# config.yml -- and even when set it feeds only these adb commands,
# never Gradle. So the identity is declared twice, here and in
# build.gradle, and a change to one alone means the official run and
# deploy tasks address a package that is not installed.
APP_ID: '{{.APP_ID | default "app.yellowjacket"}}'
# It used to be the id every adb-driven task below uninstalled,
# launched and filtered, defaulting to "app.yellowjacket". It could
# never have been a setting: `wails3 task` builds its var set from CLI
# KEY=VALUE arguments and the Taskfile tree only -- nothing reads
# build/config.yml, contrary to ANDROID.md, checked with --dry -- and
# even when set it fed only the adb commands, never Gradle. So the
# identity was declared twice, here and as `applicationId` in
# app/build.gradle, with nothing enforcing that they agree.
#
# They did not agree. The debug buildType carries
# `applicationIdSuffix ".dev"`, so the tasks that assemble a debug APK
# addressed the *release* id -- on a device, the user's installed app
# and their library (#159).
#
# The id is now read back from the built APK by scripts/android-
# pkgid.sh, so the thing installed and the thing launched agree by
# construction. Passing APP_ID= says "this build had better declare
# that id", and the deploy refuses before touching anything if it does
# not -- which is the check that would have caught #159 statically.
APP_ID: '{{.APP_ID | default ""}}'
MIN_SDK: '21'
TARGET_SDK: '35'
# The emulator runs the host architecture; physical devices are arm64
@@ -372,9 +382,7 @@ tasks:
ARCH: '{{.ARCH | default .HOST_ARCH}}'
cmds:
- task: ensure-emulator
- '"{{.ADB}}" uninstall {{.APP_ID}} 2>/dev/null || true'
- '"{{.ADB}}" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"'
- '"{{.ADB}}" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity'
- './scripts/android-deploy.sh --apk "{{.BIN_DIR}}/{{.APP_NAME}}.apk" --target emulator{{if .APP_ID}} --expect "{{.APP_ID}}"{{end}}'
run:
summary: Build, install and launch a debug build in the Android Emulator
@@ -383,9 +391,7 @@ tasks:
- task: build
cmds:
- task: assemble:apk
- '"{{.ADB}}" uninstall {{.APP_ID}} 2>/dev/null || true'
- '"{{.ADB}}" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"'
- '"{{.ADB}}" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity'
- './scripts/android-deploy.sh --apk "{{.BIN_DIR}}/{{.APP_NAME}}.apk" --target emulator{{if .APP_ID}} --expect "{{.APP_ID}}"{{end}}'
device:list:
summary: Lists connected Android devices and emulators (serials)
@@ -400,25 +406,7 @@ tasks:
ARCH: arm64
cmds:
- task: assemble:apk
- |
DEVICE='{{.DEVICE_ID | default ""}}'
if [ -z "$DEVICE" ]; then
DEVICE="${DEVICE_ID:-}"
fi
if [ -z "$DEVICE" ]; then
DEVICE=$("{{.ADB}}" devices | awk 'NR > 1 && $2 == "device" && $1 !~ /^emulator-/ { print $1; exit }')
fi
if [ -z "$DEVICE" ]; then
echo "Error: no connected physical Android device found."
echo "Pass DEVICE_ID=<serial> to target a device explicitly."
echo "Find connected device serials with: {{.ADB}} devices"
exit 1
fi
echo "Deploying {{.BIN_DIR}}/{{.APP_NAME}}.apk to device $DEVICE..."
"{{.ADB}}" -s "$DEVICE" uninstall {{.APP_ID}} 2>/dev/null || true
"{{.ADB}}" -s "$DEVICE" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"
"{{.ADB}}" -s "$DEVICE" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity
- './scripts/android-deploy.sh --apk "{{.BIN_DIR}}/{{.APP_NAME}}.apk" --target device{{if .DEVICE_ID}} --serial "{{.DEVICE_ID}}"{{end}}{{if .APP_ID}} --expect "{{.APP_ID}}"{{end}}'
preconditions:
- sh: '[ -x "{{.ADB}}" ] || command -v adb'
msg: "adb not found. Install the Android SDK platform-tools (or set ANDROID_HOME)"
@@ -430,25 +418,7 @@ tasks:
vars:
ARCH: arm64
cmds:
- |
DEVICE='{{.DEVICE_ID | default ""}}'
if [ -z "$DEVICE" ]; then
DEVICE="${DEVICE_ID:-}"
fi
if [ -z "$DEVICE" ]; then
DEVICE=$("{{.ADB}}" devices | awk 'NR > 1 && $2 == "device" && $1 !~ /^emulator-/ { print $1; exit }')
fi
if [ -z "$DEVICE" ]; then
echo "Error: no connected physical Android device found."
echo "Pass DEVICE_ID=<serial> to target a device explicitly."
echo "Find connected device serials with: {{.ADB}} devices"
exit 1
fi
echo "Deploying {{.BIN_DIR}}/{{.APP_NAME}}.apk to device $DEVICE..."
"{{.ADB}}" -s "$DEVICE" uninstall {{.APP_ID}} 2>/dev/null || true
"{{.ADB}}" -s "$DEVICE" install "{{.BIN_DIR}}/{{.APP_NAME}}.apk"
"{{.ADB}}" -s "$DEVICE" shell am start -n {{.APP_ID}}/com.wails.app.MainActivity
- './scripts/android-deploy.sh --apk "{{.BIN_DIR}}/{{.APP_NAME}}.apk" --target device{{if .DEVICE_ID}} --serial "{{.DEVICE_ID}}"{{end}}{{if .APP_ID}} --expect "{{.APP_ID}}"{{end}}'
preconditions:
- sh: '[ -x "{{.ADB}}" ] || command -v adb'
msg: "adb not found. Install the Android SDK platform-tools (or set ANDROID_HOME)"
+196
View File
@@ -0,0 +1,196 @@
#!/usr/bin/env bash
#
# Install a built APK onto an Android target and launch it, under the
# package id the APK itself declares.
#
# This is the whole body of build/android/Taskfile.yml's four adb-driven
# tasks — deploy-emulator, run, run:device, deploy-device — which were
# three lines each, written out four times, and wrong in two ways in all
# four (#159):
#
# adb uninstall app.yellowjacket # the RELEASE id, unconditionally
# adb install bin/yellowjacket.apk
# adb shell am start -n app.yellowjacket/com.wails.app.MainActivity
#
# **The uninstall is not here and does not come back.** It was there to
# make the bare `install` on the next line work at all — without -r,
# Android refuses an install over an existing package — so `install -r`
# removes the reason for it rather than merely removing it. What is
# left is the one case an uninstall really is the remedy, a changed
# signing certificate, and that is exactly the case where performing it
# silently costs the user their library. So it is *named* and not done:
# an error message carrying the command is a decision the person at the
# keyboard gets to make, which is the same answer scripts/android-
# emulator.sh already reached for `make android-install`.
#
# **The id is read back from the artifact**, never defaulted, so the
# thing installed and the thing launched cannot disagree — see
# scripts/android-pkgid.sh for why that is by construction rather than
# by discipline.
#
# **The target is checked against the task's own name.** The emulator
# tasks used a bare `adb`, which with one device attached picks that
# device whatever it is — so `wails3 task android:run`, whose summary
# says "in the Android Emulator", installed on the phone when a phone
# was the only thing plugged in. A task addressing something other than
# what it says is the same fault as the package id, one level up.
#
# Usage:
# android-deploy.sh --apk <path> --target emulator|device|any \
# [--expect <id>] [--serial <s>] [--no-launch]
set -euo pipefail
cd "$(dirname "$0")/.."
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
ADB="$(command -v adb || echo "$SDK/platform-tools/adb")"
# **Not "$PKG/.MainActivity".** A leading-dot activity is resolved
# against the applicationId, and the scaffold's activity lives in the
# Java package com.wails.app, which is deliberately not it. The short
# form fails with a class-not-found that reads like a broken build.
ACTIVITY="${YJ_ANDROID_ACTIVITY:-com.wails.app.MainActivity}"
APK=""
TARGET="any"
EXPECT=""
SERIAL="${ANDROID_SERIAL:-${DEVICE_ID:-}}"
LAUNCH=1
die() { echo "android-deploy: $*" >&2; exit 1; }
while [ $# -gt 0 ]; do
case "$1" in
--apk) APK="${2:-}"; shift 2 ;;
--target) TARGET="${2:-}"; shift 2 ;;
--expect) EXPECT="${2:-}"; shift 2 ;;
--serial) SERIAL="${2:-}"; shift 2 ;;
--no-launch) LAUNCH=0; shift ;;
*) die "unknown option $1" ;;
esac
done
[ -n "$APK" ] || die "--apk is required"
[ -f "$APK" ] || die "no such APK: $APK
Build one first: wails3 task android:assemble:apk (debug)
wails3 task android:package (release)"
[ -x "$ADB" ] || command -v adb >/dev/null ||
die "adb not found. Install the Android SDK platform-tools (or set ANDROID_HOME)"
case "$TARGET" in
emulator | device | any) ;;
*) die "--target must be emulator, device or any (got '$TARGET')" ;;
esac
# ---------------------------------------------------------------- #
# Which package
# ---------------------------------------------------------------- #
# This runs *before* a target is chosen, deliberately: the guard is a
# question about the artifact, so it can be answered — and exercised —
# with nothing plugged in, and a build whose id is wrong should be
# refused whether or not there is anything to install it onto.
#
# An unreadable APK, or an id that is not the one the caller named, is a
# hard stop before anything is installed or launched. Spelled as two
# calls rather than one with a conditional argument: an empty array under
# `set -u` is an unbound variable in bash 3.2, which is what macOS ships.
if [ -n "$EXPECT" ]; then
PKG="$(./scripts/android-pkgid.sh "$APK" --expect "$EXPECT")"
else
PKG="$(./scripts/android-pkgid.sh "$APK")"
fi
# ---------------------------------------------------------------- #
# Which target
# ---------------------------------------------------------------- #
# An emulator serial is "emulator-<port>"; anything else online is a
# physical device. That is the same test the device tasks already made,
# and the emulator tasks did not make at all.
online_matching() {
case "$TARGET" in
emulator) "$ADB" devices | awk 'NR > 1 && $2 == "device" && $1 ~ /^emulator-/ { print $1 }' ;;
device) "$ADB" devices | awk 'NR > 1 && $2 == "device" && $1 !~ /^emulator-/ { print $1 }' ;;
any) "$ADB" devices | awk 'NR > 1 && $2 == "device" { print $1 }' ;;
esac
}
if [ -z "$SERIAL" ]; then
matches="$(online_matching)"
count="$(printf '%s' "$matches" | grep -c . || true)"
if [ "$count" -eq 0 ]; then
echo "android-deploy: no ${TARGET/any/attached} target is online." >&2
"$ADB" devices | sed '1d;/^$/d;s/^/ /' >&2 || true
if [ "$TARGET" = "emulator" ]; then
echo " Start one with: make android-emulator" >&2
elif [ "$TARGET" = "device" ]; then
echo " Plug a phone in and authorise the adb key." >&2
fi
exit 1
fi
# Several is ambiguous, and picking the first silently is how a
# build lands on a target nobody named. The old run:device did
# exactly that.
if [ "$count" -gt 1 ]; then
echo "android-deploy: several $TARGET targets are online — name one." >&2
printf '%s\n' "$matches" | sed 's/^/ /' >&2
echo " Pass DEVICE_ID=<serial>, or set ANDROID_SERIAL." >&2
exit 1
fi
SERIAL="$matches"
fi
# ---------------------------------------------------------------- #
# Install
# ---------------------------------------------------------------- #
echo "android-deploy: $APK ($PKG) -> $SERIAL"
if ! out="$("$ADB" -s "$SERIAL" install -r "$APK" 2>&1)"; then
printf '%s\n' "$out"
case "$out" in
*INSTALL_FAILED_UPDATE_INCOMPATIBLE* | *"signatures do not match"*)
cat >&2 <<EOF
The copy of $PKG already installed was signed with a different key, and
Android never allows that as an update.
The only way forward is an uninstall — **which deletes that app's data**,
and for this app that is the user's library, irreversibly. So it is not
done for you. If the installed copy is disposable:
$ADB -s $SERIAL uninstall $PKG
If it is not — if this is a released build with a real library on it —
install the debug variant instead, which carries applicationIdSuffix
".dev" and so sits beside it rather than replacing it:
wails3 task android:assemble:apk
EOF
;;
*INSTALL_FAILED_VERSION_DOWNGRADE*)
cat >&2 <<EOF
The installed copy of $PKG has a higher versionCode than this build.
A bare 'make android' builds versionCode 1; a versioned one builds e.g.
10301. Either build with a version:
YJ_VERSION=1.3.1 YJ_VERSION_CODE=10301 make android
or, if the installed copy is disposable, remove it:
$ADB -s $SERIAL uninstall $PKG
EOF
;;
esac
exit 1
fi
printf '%s\n' "$out"
[ "$LAUNCH" -eq 1 ] || exit 0
"$ADB" -s "$SERIAL" shell am start -n "$PKG/$ACTIVITY"
+27 -4
View File
@@ -34,7 +34,21 @@ cd "$(dirname "$0")/.."
AVD="${YJ_AVD:-yj-test}"
SDK="${ANDROID_SDK_ROOT:-${ANDROID_HOME:-$HOME/Android/Sdk}}"
PKG="${YJ_ANDROID_PKG:-app.yellowjacket}"
# The third declaration of the app's identity, and the one #159 did not
# cash out in -- but the same hazard, so it is derived rather than
# written down too. The APK in bin/ is what `make android-install` is
# about to install and what `android-launch`, `logs` and `smoke` are
# about to address, so it is the authority; whatever Gradle resolved the
# applicationId to, suffix included, is in the file.
#
# The literal survives only as the answer for a tree with no APK built
# yet, where these commands are asking about whatever is already on the
# device and there is nothing to read. YJ_ANDROID_PKG still overrides.
PKG="${YJ_ANDROID_PKG:-}"
if [ -z "$PKG" ] && [ -f bin/yellowjacket.apk ]; then
PKG="$(./scripts/android-pkgid.sh bin/yellowjacket.apk 2>/dev/null || true)"
fi
PKG="${PKG:-app.yellowjacket}"
# Where `make android-inspect` forwards the WebView's devtools socket.
CDP_PORT="${YJ_ANDROID_CDP_PORT:-9222}"
# **Not "$PKG/.MainActivity".** A leading-dot activity is resolved
@@ -281,15 +295,24 @@ cmd_inspect() {
need_sdk
pick_device || die "no device -- plug a phone in (USB debugging on) or run 'make android-emulator'"
local pkg pid
local pkg pid candidates
pid=""
for pkg in "$PKG.dev" "$PKG"; do
# Debug sibling first, release second, whichever way round $PKG was
# resolved -- it is read from the built APK now, so it is already the
# .dev id whenever a debug build is what is in bin/, and appending a
# second ".dev" to it would probe a package that cannot exist.
case "$PKG" in
*.dev) candidates="$PKG ${PKG%.dev}" ;;
*) candidates="$PKG.dev $PKG" ;;
esac
for pkg in $candidates; do
pid=$("$ADB" shell pidof "$pkg" 2>/dev/null | tr -d '\r' | awk '{print $1}')
[ -n "$pid" ] && break
done
[ -n "$pid" ] || die "neither $PKG.dev nor $PKG is running; launch it first"
[ -n "$pid" ] || die "none of: $candidates is running; launch it first"
"$ADB" forward --remove-all >/dev/null 2>&1 || true
"$ADB" forward "tcp:$CDP_PORT" "localabstract:webview_devtools_remote_$pid" >/dev/null \
+97
View File
@@ -0,0 +1,97 @@
#!/usr/bin/env bash
#
# Print the package id an APK actually declares — and, given --expect,
# refuse when that is not the id the caller was about to act on.
#
# This exists because the identity is declared twice and nothing made
# the two agree. `applicationId` in build/android/app/build.gradle is
# what Gradle installs; `APP_ID` in build/android/Taskfile.yml was what
# every adb-driven task uninstalled, launched and filtered. They differ
# for a reason nobody has to get wrong: the debug buildType carries
# `applicationIdSuffix ".dev"`, so a debug build is app.yellowjacket.dev
# while the default was app.yellowjacket — the *release* id, and on a
# real phone the released app with the user's library on it (#159).
#
# So the id is read back from the artifact rather than written down a
# third time. The APK is the authority because the task that installs
# it has just built it: whatever Gradle resolved the applicationId to,
# suffixes and flavours included, is in the file, and no default can
# disagree with it.
#
# Usage:
# android-pkgid.sh <apk> [--expect <id>]
#
# Exit codes: 0 printed the id; 1 could not read it; 2 --expect failed.
set -euo pipefail
die() { echo "android-pkgid: $*" >&2; exit 1; }
APK=""
EXPECT=""
while [ $# -gt 0 ]; do
case "$1" in
--expect) EXPECT="${2:-}"; shift 2 ;;
-*) die "unknown option $1" ;;
*) APK="$1"; shift ;;
esac
done
[ -n "$APK" ] || die "usage: android-pkgid.sh <apk> [--expect <id>]"
[ -f "$APK" ] || die "no such APK: $APK"
# aapt2 lives under build-tools/<version>/, which is versioned, so it is
# resolved rather than pinned. PATH first, so a system aapt2 (Arch ships
# one) works without an SDK layout at all.
find_aapt() {
local sdk name
for name in "$@"; do
command -v "$name" 2>/dev/null && return 0
done
sdk="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
for name in "$@"; do
ls "$sdk"/build-tools/*/"$name" 2>/dev/null | sort -V | tail -1 | grep . && return 0
done
return 1
}
pkg=""
# `aapt2 dump packagename` answers in one word and is the cheapest of
# the three. aapt1 is the fallback because it is what older build-tools
# carry and what the issue's own measurement used.
if AAPT2="$(find_aapt aapt2)"; then
pkg="$("$AAPT2" dump packagename "$APK" 2>/dev/null | head -1 | tr -d '\r')" || true
fi
if [ -z "$pkg" ] && AAPT="$(find_aapt aapt)"; then
pkg="$("$AAPT" dump badging "$APK" 2>/dev/null |
sed -n "s/^package: name='\([^']*\)'.*/\1/p" | head -1)" || true
fi
# Guessing here is the bug this file exists to prevent, so an unreadable
# APK is a hard failure and never a fallback to a written-down default.
if [ -z "$pkg" ]; then
die "could not read a package name from $APK.
Install the SDK build-tools (aapt2), or set ANDROID_HOME to an SDK
that carries them: sdkmanager 'build-tools;34.0.0'"
fi
if [ -n "$EXPECT" ] && [ "$EXPECT" != "$pkg" ]; then
cat >&2 <<EOF
android-pkgid: refusing to act on a package this APK does not declare.
the APK declares: $pkg
the task expects: $EXPECT
APK: $APK
These must agree, and when they do not it is the *expectation* that is
wrong: the APK is what Gradle built. A debug build carries
applicationIdSuffix ".dev" (app/build.gradle), so a task that assembles
a debug APK and then addresses the unsuffixed id is addressing the
released app — which on a real device is the user's install, with their
library in it (#159).
EOF
exit 2
fi
printf '%s\n' "$pkg"