Skip to content

Commit 1a1ef7c

Browse files
authored
feat(android): one persistent automation helper owning snapshot + viewport + canonical injection (#1281)
* feat(android): consolidate touch injection and gesture viewport into the persistent snapshot helper (#1275) One Android automation helper now owns snapshot capture, gesture viewport resolution, and canonical one-/two-pointer plan injection. A live persistent helper session executes gesture/viewport commands over its socket protocol; without a session the same APK runs one-shot via am instrument. The separate one-shot multitouch helper APK is deleted (atomic replacement, no fallback). Touch scheduling/injection is extracted into focused Java classes (TouchPlan, TouchPlanInjector, PointerEventSchedule, GestureViewportReader) instead of growing SnapshotInstrumentation. ADR 0013 amended. * fix(android): stop a structurally-failed helper session before the one-shot viewport retry A structured ok=false viewport response leaves the session process alive, and Android permits only one instrumentation owner of UiAutomation - running the one-shot fallback against a still-live helper contends with it and masks the original structured failure. Stop the session first; regression pins that the one-shot retry only executes once the session is gone. * refactor(android): extract helper touch dispatch into focused classes; split session tests; document helper API v2 (PR #1281 review) Addresses findings 2 and 3 from PR #1281 review (finding 1, viewport session-stop ordering, was already fixed in 5961b92). - Extract SnapshotInstrumentation.java's one-shot/session touch dispatch into TouchCommandHandler.java (viewport/gesture population, UiAutomation- parameterized) and SessionResponseWriter.java (session response encoding), with shared PROTOCOL/HELPER_API_VERSION/OUTPUT_FORMAT constants moved to a tiny HelperProtocol.java. SnapshotInstrumentation.java shrinks from 908 to 803 lines; wire format (header keys/values, error shapes) is unchanged. - Split touch-helper.test.ts (~720 lines) into touch-helper.test.ts (normalize/parse/one-shot gesture+viewport+result envelope) and touch-helper-session.test.ts (persistent-session transport + fake-session harness), moving shared device/plan/install-probe fixtures used by both files into touch-helper.fixtures.ts. - Update android/snapshot-helper/README.md to document helper API v2: the one-shot viewport/gesture modes, the android-touch-plan-v1 payload shape, and the persistent session's socket command/response contract. * fix(android): invalidate helper session after APK replacement; recycle viewport windows; align ADR 0002 (PR #1281 re-review) - prepareAndroidTouchHelper now mirrors the snapshot path: when ensureAndroidSnapshotHelper replaces the APK (install.installed), the persistent session started against the previous binary is stopped before any touch command, so gestures run one-shot against the fresh install instead of a dead/stale session socket. Regression: 'an APK replacement stops the stale session and the gesture runs one-shot' drives a live fake session through an outdated-install probe (new outdatedVersionAdb fixture) and asserts the session socket receives no gesture, the one-shot instrumentation path executes, and the session is gone. - GestureViewportReader.read no longer leaks AccessibilityWindowInfo: a single pass copies the active/focused and first-application bounds into locals, every window is recycled in a finally, and the existing precedence (active/focused app bounds, root-in-active-window, fallback app bounds, IllegalStateException) is applied afterwards, unchanged. - ADR 0002's touch-synthesis paragraph is amended (2026-07, issue #1275) to the shared-helper model, consistent with ADR 0013: a live persistent helper session executes touch commands directly, one-shot otherwise; the old stop-before-gesture requirement is kept as historical context. * fix(android): resolve touch helper artifact from the ADB provider like snapshots do (PR #1281 re-review) - prepareAndroidTouchHelper now uses the same artifact precedence as snapshot capture: the scoped adbProvider's snapshotHelperArtifact when present, otherwise the bundled resolver (whose strict unavailable error is preserved). The provider artifact drives both the install decision and the instrumentationRunner used for one-shot commands, so an ADB-backed provider that supplies a helper artifact but no native touch override runs snapshots and gestures against the same single helper (issue #1275). Regression: 'a provider-supplied snapshotHelperArtifact overrides the bundled artifact for touch' pins the provider packageName on the install probe, the provider apkPath on the install call, the provider instrumentationRunner on the am instrument args, and that the bundled resolver is never invoked. - ADR 0002 now states explicitly that one-shot retry applies only to idempotent reads (viewport) after the failed session is stopped; non-idempotent gesture failures surface directly. - Helper README session transport corrected: a persistent process serving one short-lived socket connection per request (the server closes each accepted connection), not a single long-lived connection. * fix(android): guard touch session reuse on helper identity, stop mismatched sessions (PR #1281 re-review) Persistent helper sessions are keyed by device, so touch reuse must also prove the live session runs the helper binary the command selected. The session record now stores its helper identity (packageName, runner, helperVersion, helperVersionCode — the same values that feed the snapshot session identity), and runAndroidSnapshotHelperSessionTouchCommand takes the requesting helper identity: on mismatch (packageName/runner always; version/versionCode when both sides define them) it stops the session and returns undefined, so the touch command runs one-shot against the selected artifact — gestures never start sessions; the next snapshot restarts one with the right artifact. Matching identity reuses the session as before. Snapshot capture identity and behavior are unchanged. Regression: 'a provider artifact that mismatches the live session helper stops it and runs one-shot' — a live fake session from the bundled fixture artifact, then a gesture through an ADB provider supplying an already-current artifact with a distinct packageName/runner (no install): the old session socket receives zero gesture commands, the session is stopped, the one-shot am instrument args end with the provider runner, and helperTransport is 'instrumentation'. * fix(android): include artifact sha in helper session identity; evict stale install memo entries (PR #1281 re-review) Same-version binary replacement changes only the APK sha, so identity guards keyed on package/runner/version/versionCode could not detect a crossover between two artifacts that differ only in bytes: - The artifact sha256 now joins the helper identity end-to-end: AndroidSnapshotHelperCaptureOptions gains helperSha256 (snapshot.ts passes artifact.manifest.sha256 alongside version/versionCode), the session record stores it, createSessionIdentity includes it (making snapshot session reuse sha-aware, consistent with the install path's existing sha check), and the touch identity guard compares it via the same both-defined rule. - ensureAndroidSnapshotHelper's install memo now evicts every other cached decision for the same device+package when it records an install/current decision, so installing B invalidates A's stale 'current' memo and a later command selecting A re-inspects the device instead of skipping the sha check. Regressions: 'a same-version artifact with a different sha stops the live session and runs one-shot' (touch-helper-session.test.ts — B owns the live session, a gesture selecting same-version different-sha A sends zero commands to B's socket, stops it, and completes one-shot) and 'installing a same-version different-sha helper evicts the stale install memo' (snapshot-helper.test.ts — A:current cached, B installed, selecting A re-inspects and reinstalls instead of serving the stale memo). Both verified to fail without their fix.
1 parent b10c8cf commit 1a1ef7c

41 files changed

Lines changed: 2202 additions & 1778 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/android.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ jobs:
5454
- name: Package npm-bundled Android helpers
5555
run: |
5656
pnpm package:android-snapshot-helper:npm
57-
pnpm package:android-multitouch-helper:npm
5857
pnpm package:android-ime-helper:npm
5958
6059
- name: Run Android smoke checks

.github/workflows/perf-nightly.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ jobs:
101101
- name: Package npm-bundled Android helpers
102102
run: |
103103
pnpm package:android-snapshot-helper:npm
104-
pnpm package:android-multitouch-helper:npm
105104
pnpm package:android-ime-helper:npm
106105
107106
- name: Run Android command perf benchmark

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,5 @@ xcuserdata/
3636
.skillgym-results/
3737
android/snapshot-helper/build/
3838
android/snapshot-helper/dist/
39-
android/multitouch-helper/build/
40-
android/multitouch-helper/dist/
4139
android/ime-helper/build/
4240
android/ime-helper/dist/

android/ime-helper/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ buttons -- unlike the real system keyboard (Gboard et al.), which pulls its visi
88
the UIAutomator tree the moment a field is focused.
99

1010
Text is injected through a dynamically-registered `BroadcastReceiver` carrying base64-encoded
11-
UTF-8 Intent extras, the same `payloadBase64` convention `android-multitouch-helper` uses for the
11+
UTF-8 Intent extras, the same `payloadBase64` convention the snapshot helper's gesture mode uses for the
1212
same reason: `adb shell` re-tokenizes raw spaces, and base64 sidesteps that entirely. This makes
1313
Unicode/CJK/emoji round-trip exactly, unlike `adb shell input text`, which is ASCII-only.
1414

15-
The helper is a service, not an instrumentation, so it cannot use `android-snapshot-helper`'s and
16-
`android-multitouch-helper`'s `am instrument` invocation shape. It is installed and its version
15+
The helper is a service, not an instrumentation, so it cannot use the snapshot helper's
16+
`am instrument` invocation shape. It is installed and its version
1717
verified the same way (bundled npm-packaged dist + version-keyed manifest.json), but invoked with
1818
`adb shell ime enable`/`ime set` (lifecycle) and `adb shell am broadcast` (text entry) instead.
1919

android/multitouch-helper/AndroidManifest.xml

Lines changed: 0 additions & 17 deletions
This file was deleted.

android/multitouch-helper/README.md

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)