diff --git a/docs/PERFORMANCE.md b/docs/PERFORMANCE.md index 93c9e62..ed8070d 100644 --- a/docs/PERFORMANCE.md +++ b/docs/PERFORMANCE.md @@ -101,7 +101,10 @@ reads the button and taps it), and it does **not** work for the one that matters - It is `FLAG_SECURE`: a `screencap` taken while `mCurrentFocus` was the installer shows the home screen behind it, which is why screenshots of "the dialog" show nothing. - A tap on it, when a run did stay up long enough for the script to read and tap `Installieren`, still - ended with `INSTALL_FAILED_USER_RESTRICTED: Install canceled by user`. + ended with `INSTALL_FAILED_USER_RESTRICTED: Install canceled by user`. The same happened later with the + script answering *two* dialogs in one run from a measured button position, each logged with the + installer window still focused: the install was refused anyway. MIUI is closing its own dialog when the + install session ends, so the tap arrives after the outcome has been decided. - `pm install` started from an *app* uid does not even reach the gate: MIUI's `XSpaceManagerServiceImpl` throws `SecurityException: You either need MANAGE_USERS or CREATE_USERS permission to: query users`. So the rule's install runs through the shell, MIUI intercepts it as an "Install via USB" request, and diff --git a/tools/benchmark-watch-install.sh b/tools/benchmark-watch-install.sh index 6f83470..1e5f30f 100755 --- a/tools/benchmark-watch-install.sh +++ b/tools/benchmark-watch-install.sh @@ -1,36 +1,37 @@ #!/usr/bin/env bash # -# Answers MIUI's "Install via USB" dialog while a benchmark run is in flight. +# Answers MIUI's "Install via USB" dialog while installs are happening on the device. # -# ./tools/benchmark-watch-install.sh # watch for ten minutes, poll every 5 s -# ./tools/benchmark-watch-install.sh --interval 2 # poll more often -# ./tools/benchmark-watch-install.sh --dry-run # report, never tap +# ./tools/benchmark-watch-install.sh # ten minutes, poll every 2 s +# ./tools/benchmark-watch-install.sh --interval 5 # poll less often when idle +# ./tools/benchmark-watch-install.sh --burst 30 # keep watching fast for 30 s after a dialog +# ./tools/benchmark-watch-install.sh --dry-run # report, never tap # -# Why this exists: macrobenchmark installs its target before measuring it, and on this phone MIUI puts -# that install behind `com.miui.securitycenter/.permcenter.install.AdbInstallActivity`. The test cannot -# answer it itself — `CompilationMode.installPackageFromPaths` builds `pm install -t …` and runs it -# through `Shell.executeScriptCaptureStdout`, so the call blocks until the child exits and no test code -# is running while the dialog is up. This script runs beside the Gradle run instead. +# Run this for *every* install, not just the first: a benchmark run installs the test APK and then, per +# test, the app under test. Measured the hard way — the first version of this script spent three to four +# seconds reading one dialog, and the next dialog opened and closed inside that window. # -# **It never taps blindly.** A dialog is only answered after its Install button has been read off the -# screen and its own bounds are known; if the button is not there, nothing is tapped. That costs a -# `uiautomator dump` per dialog (3-4 s on this device) but it cannot press whatever happens to sit at a -# guessed coordinate. +# How it answers a dialog: # -# Measured on the Xiaomi phone (Android 12, MIUI 14), and both numbers matter: +# 1. If the Install button's position for this dialog was measured before (and saved), tap it — but +# only after re-checking, immediately beforehand, that an installer window is still focused. That +# check is what stops a tap from landing on the home screen once the dialog closes. +# 2. Otherwise read the screen (`uiautomator dump`), find the button by label — refusing anything that +# means "no" — verify the installer is still focused, then tap its own bounds and save the position +# for the dialogs that cannot be read in time. +# 3. If no Install button can be read and no position is known, tap nothing and say so. # -# * for an install started from adb, the dialog appears after about 1.7 s and its deny button counts -# down from about ten seconds — so a 5 s poll still leaves time to read the screen and tap; -# * for the install macrobenchmark starts from inside the test, the window is focused for well under -# a second (`13:51:27.70 AdbInstallActivity` → `13:51:28.38 null`), which is shorter than one -# `uiautomator dump`. Those dialogs will not be caught, by this script or by a human. +# After every dialog the script watches in "burst" mode (every 0.4 s instead of the idle interval), +# because installs arrive in pairs and the second one used to slip through while the first was read. # -# So this is honest about its limit: it makes adb-driven installs unattended, and it will not rescue a -# reinstall that MIUI refuses instantly. `--interval` and `--wait` are there for tuning per device. +# Measured on the Xiaomi phone (Android 12, MIUI 14): an adb-started install raises the dialog after +# about 1.7 s with a ten second deny countdown, so there is time for one dump and one tap; the same +# dialog is FLAG_SECURE, so a screencap of it shows whatever is behind it. set -euo pipefail seconds=600 -interval=5 +interval=2 +burst=30 wait=10 dry_run=0 @@ -38,10 +39,11 @@ while [ $# -gt 0 ]; do case "$1" in --seconds) seconds=$2; shift 2 ;; --interval) interval=$2; shift 2 ;; + --burst) burst=$2; shift 2 ;; --wait) wait=$2; shift 2 ;; --dry-run) dry_run=1; shift ;; -h|--help) sed -n '2,26p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;; - *) echo "usage: $0 [--seconds N] [--interval N] [--wait N] [--dry-run]" >&2; exit 2 ;; + *) echo "usage: $0 [--seconds N] [--interval N] [--burst N] [--wait N] [--dry-run]" >&2; exit 2 ;; esac done @@ -52,9 +54,47 @@ device_dump=/sdcard/.benchmark-watch.xml local_dump=$(mktemp) trap 'rm -f "$local_dump"' EXIT -# Prints "