# Automated Android regression capture

This directory contains the repeatable hardware workflow used for
`regression-check.html`.

## What it tests

- Installed standalone PWA cold launch and pull-to-refresh
- Installed fullscreen PWA cold launch and pull-to-refresh
- Off-origin child CCT placement and parent recovery
- Fullscreen backswipe with transient system bars
- Regular `document.requestFullscreen()`
- Three-button navigation contrast over white and black pages
- Dynamic light/dark status-bar icons
- Landscape `viewport-fit` transitions through `cover`, `auto`, `contain`,
  `cover`, and `auto`, including screenshots, viewport geometry, CSS safe-area
  values, and WindowManager state for every stable state
- Keyboard open/close viewport and CSS safe-area values
- Chrome keyboard accessory attachment using an instrumented suggestion
- Machine-readable PASS/FAIL validation in `summary.json`

The page-driven matrix can run against a patched Chromium APK and the phone's
stock Chrome/WebAPK implementation. The accessory integration check runs only
for Chromium because it uses Chromium's test APK to inject accessory data that
public Chromium cannot create through the production saved-password flow.

## Files

- [`scripts/cdp_adb.py`](scripts/cdp_adb.py): Chrome DevTools Protocol over the
  ADB server protocol. This works even when the ADB server is remote and a
  normal `adb forward` port is not reachable from the client machine.
- [`scripts/install_chromium_shortcuts.sh`](scripts/install_chromium_shortcuts.sh):
  installs the six test PWAs as Chromium-hosted shortcuts on the Samsung test
  device.
- [`scripts/install_stock_webapks.py`](scripts/install_stock_webapks.py):
  installs equivalent stock Chrome WebAPKs from separate scopes under `pages/`
  and writes their generated package names to a shell map.
- [`scripts/launch_shortcut.py`](scripts/launch_shortcut.py): locates and launches
  a Samsung launcher shortcut by label.
- [`scripts/capture_matrix.sh`](scripts/capture_matrix.sh): captures screenshots
  and JSON metrics for either Chromium or stock Chrome, optionally runs the
  keyboard accessory integration test, and invokes the validator.
- [`scripts/validate_matrix.py`](scripts/validate_matrix.py): checks captured
  geometry and test output, writes `summary.json`, and exits non-zero if a check
  fails.
- [`pages/`](pages/): separate-scope copies of the sampler pages. Separate scopes
  are required because stock Chrome otherwise routes all pages in the sampler
  directory through the first installed WebAPK.
- [`../automation-407420295.tar.gz`](../automation-407420295.tar.gz): downloadable
  archive containing this README, all scripts, and all separate-scope pages.

## Prerequisites

- Linux host with `adb`, Python 3, and the Python `websocket-client` package.
- Phone unlocked with USB debugging authorized.
- Samsung/One UI launcher for the shortcut installer as currently written.
- Test phone resolution matching the recorded device (`1080x2340`) for the
  fixed Chromium install coordinates.
- The patched arm64 APK built as `chrome_public_apk`.

Set the remote ADB server socket:

```bash
export ADB_SERVER_SOCKET=tcp:127.0.0.1:15037
adb devices -l
```

## Patched Chromium run

```bash
export APK=/path/to/out/Android/apks/ChromePublic.apk
adb install -r "$APK"
adb shell am set-debug-app --persistent org.chromium.chrome
adb shell 'printf "%s\n" \
  "_ --enable-features=WebAppShortEdgesCutoutMode" \
  > /data/local/tmp/chrome-command-line'

./scripts/install_chromium_shortcuts.sh
CHROMIUM_SRC=/path/to/chromium/src \
  CHROMIUM_OUT=out/Android \
  ./scripts/capture_matrix.sh chromium /tmp/regression-chromium
```

`CHROMIUM_SRC` enables
`AutofillKeyboardAccessoryIntegrationTest#testTapInputFieldShowsKeyboardAccessory`.
The capture script invokes `build/android/test_runner.py` directly instead of
the generated wrapper: the wrapper runs through `testing/test_env.py`, which
sets `CHROME_HEADLESS=1`, and with that variable set the test environment
factory silently targets a Fuchsia Cuttlefish emulator on `127.0.0.1:6525`
instead of the attached device.
The test injects an Autofill suggestion and exercises the real Chrome keyboard
accessory without requiring a saved password. If `CHROMIUM_SRC` is omitted, the
page matrix still runs but `summary.json` marks that check `SKIP`.

A fully green run ends with a summary similar to:

```json
{"status":"PASS","passed":8,"failed":0,"skipped":0}
```

The cold-launch toolbar hairline check is a real assertion: the fix
`Hide toolbar hairline capture for fully hidden browser controls` parks the
toolbar capture fully offscreen when browser controls rest hidden at the zero
min-height boundary, which previously left a 1 dp strip of theme surface color
at the top of every web app cold launch.

One upstream artifact is reported as an informational `KNOWN-ISSUE` entry
instead of a failure because it reproduces with the
`WebAppShortEdgesCutoutMode` feature disabled and with all local edge-to-edge
changes removed from the build:

- **Dynamic system-bar appearance.** With the edge-to-edge base layout active,
  webapp `theme-color` changes no longer toggle `APPEARANCE_LIGHT_STATUS_BARS`,
  so status icons stay light over a white page. The validator records the
  status-bar `AppearanceRegion` from `dumpsys window` for both page colors as
  evidence.

The latest published hardware result is available as
[`latest-summary.json`](latest-summary.json). The corresponding instrumented
accessory output is in
[`keyboard-accessory-test.txt`](keyboard-accessory-test.txt).

Chromium shortcuts are used because the public Chromium build does not use the
stock WebAPK installation service on this phone.

## Stock Chrome control run

Stop Chromium first so stock Chrome owns `@chrome_devtools_remote`:

```bash
adb shell am force-stop org.chromium.chrome
export STOCK_WEBAPK_MAP=/tmp/stock-webapks.txt
./scripts/install_stock_webapks.py
./scripts/capture_matrix.sh stock /tmp/regression-stock "$STOCK_WEBAPK_MAP"
```

Stock Chrome may show its translation prompt over English pages. Metrics are
collected through CDP and are unaffected by that prompt.

## DevTools socket selection

When both browsers are alive, Android may rename one socket to a suffixed name
such as `chrome_devtools_remote_7698`. Prefer stopping the inactive browser.
If needed, select a socket explicitly:

```bash
export CHROME_DEVTOOLS_SOCKET=chrome_devtools_remote_7698
./scripts/cdp_adb.py --list
```

## Temporary device changes

`capture_matrix.sh` records the original navigation and rotation settings
before changing them. It locks the baseline cases to portrait, temporarily
locks the device to landscape for the `viewport-fit` matrix, and enables
gesture navigation for the IME and backswipe cases. The EXIT trap restores all recorded values even when a capture or
validation step fails.

The script installs test apps and writes the Chromium command-line file. It
does not change the default browser, keyboard, display density, resolution, or
screen timeout.

To clear the persistent debug-app setting after a run:

```bash
adb shell am clear-debug-app
```

To remove stock control WebAPKs:

```bash
cut -d= -f2 /tmp/stock-webapks.txt | xargs -n1 adb uninstall
```

Chromium launcher shortcuts can be removed from the launcher after the report
has been archived.

## Interpreting the landscape viewport-fit matrix

The matrix captures these states in order:

```text
cover-1 -> auto-1 -> contain -> cover-2 -> auto-2
```

For patched Chromium, both `cover` captures must occupy the complete landscape
screen width and expose a non-zero left or right CSS safe-area inset. `auto` and
`contain` must use a narrower fitted viewport with zero CSS side inset. The
second `cover` and `auto` metrics must exactly match their first captures,
which detects stale padding or ownership after dynamic transitions.

Each state produces a PNG, JSON metrics, and a `dumpsys window windows` record
under the selected output directory. Stock Chrome is checked for stable repeat
geometry without requiring the experimental edge-to-edge behavior.

## Interpreting the standalone metrics

With three-button navigation, the patched implementation intentionally reports:

```text
safe-area-inset-top: 29px
safe-area-inset-bottom: 0px
screen.height - innerHeight: 48px
```

The 48px difference is the opaque browser-controlled navigation fallback, not
a fitted status bar. The standalone sampler allows up to 64 CSS px for this
case while still requiring a non-zero top safe area.
