Commit Graph

3 Commits

Author SHA1 Message Date
SoftwareApp ddc7fb44b0 docs: record the QEMU clock-dilation lead for the FGS video
Chasing the video on the emulator found one lever that actually attacks the failure, and it is
worth writing down even though it did not hold:

  emulator -avd phone ... -qemu -icount shift=1,sleep=off

Without -icount the guest clock is tied to the host's wall clock, so Android's five-second
startForegroundService() deadline means five real seconds however slowly TCG executes - which is
what kills the app here every time. With -icount the guest clock advances per executed
instruction, and it measured 2.30x slow (30.9 s host to 13.4 s guest uptime). On that boot a scan
start succeeded: the app's notification appeared five seconds after the tap and there was no
am_crash, the only success recorded on this machine.

It then stopped reproducing - later attempts produced neither a notification nor a crash. Two
possibilities, and the second is an app-side diagnosability gap: either the tap never reached the
app, or ScanService.start() swallowed a ForegroundServiceStartNotAllowedException, which it does
silently (it catches IllegalStateException and does nothing with it, so logcat shows nothing at
all). Notes say so, and point at SETTLE_SECONDS / WARM_SECONDS for scaling the waits, because a
dilated guest is slower at everything - uiautomator dumps included.

The script also retries a tap (up to three times, re-reading the coordinates) before falling back
to a cold restart, since on a slow emulator the window can still be settling while the
accessibility tree already lists the button - and a restart costs another cold start. The
recommendation stands: record the take on the phone.
2026-10-14 12:25:02 +02:00
SoftwareApp 179bc2cc33 fix: prove the FGS video's scan start by notification, not by the service record
The script could report "scan started" on runs where the app was being killed, which sent me
chasing a "mid-scan death" that never happened. The mistake was the signal: dumpsys activity
services lists a service *record* from the moment startForegroundService() is called, so it says
nothing about whether startForeground() was reached - and that gap is exactly what the platform's
deadline measures. logcat's events buffer shows what really happened on every attempt:

  am_crash: [..., android.app.ForegroundServiceDidNotStartInTimeException,
             Context.startForegroundService() did not then call
             Service.startForeground(): .../ScanService]
  am_proc_died: [...]

No am_kill, no low-memory record: not memory pressure, and not a crash in the app's own code. The
script now waits for the app's notification, which only exists once startForeground() has been
called, and prints that am_crash line when the service never starts, so a failure is never
mistaken for a success again.

Also in the script:

- let the emulator settle before the run (first-boot work competes for the same CPU) and give the
  process time to load its classes before asking it to start a service inside the budget, both
  overridable as SETTLE_SECONDS and WARM_SECONDS,
- give up with a non-zero exit and an explicit "record this on a real device" message rather than
  carrying on to record a video whose scan never started,
- read the events buffer rather than the main one when reporting the reason.

Verdict for the docs, corrected: this capture cannot be done on a KVM-less emulator, and the
previous "second failure mode" was this one misread. The last crash landed on a process that had
been alive and idle for over a minute, so no amount of warming fixes it - while on the phone the
same build scans 5,363 photos in 20 s with the notification up the whole time. The take belongs on
the phone; the script is the checklist for what it has to show.
2026-10-14 11:39:49 +02:00
SoftwareApp 1ce2f2e1f4 feat: record the foreground-service video Play's declaration asks for
The App content > Foreground service permissions form does not just want the justification that
was already written down. Per declared type it asks for four things, one of which is "a link to
a video demonstrating each foreground service feature. The video should demonstrate the steps
the user needs to take in your app in order to trigger the feature."

tools/record-fgs-demo.sh records exactly that journey on a device or emulator: the app on its
intro, the user tapping Start scan, Home, the ongoing notification visible in the shade while
the scan keeps running, and back into the app for the results. It loads the generated demo
library first so no real photo is in frame, writes a checkpoint PNG at each milestone so the
recording can be checked without decoding it, and takes the library off the device afterwards.
The output is git-ignored, like the screenshots.

Four things the script has to get right, each learned by getting it wrong first:

- bounds="[x1,y1][x2,y2]" cannot be parsed by stripping everything but digits: the "][ "
  between the corners goes too, which glued y1 to x2 and produced coordinates like
  "976 723061" - a tap that lands nowhere and reports no error at all.
- No single text matcher works: an exact match never finds the results card ("2 duplicate
  groups"), while a substring match lets "Scan" match both "Start scan" and "Scanned in
  39.7 s". Both matchers exist and each call site uses the right one.
- A finished scan leaves the app on its results screen, where the intro's button no longer
  exists - tapping those stale coordinates silently does nothing.
- `set -e` plus start="$(failing command)" aborts a run with no message whatsoever, which
  cost a whole debugging cycle.

Checklist step 5 now carries all four fields with paste-ready text: the description, the user
impact if the scan is deferred or interrupted (a scan is not deferrable work - the user is
waiting for a result, so there is no WorkManager/JobScheduler fallback), the use case
(Local processing: Other, the only dataSync option that describes a local, user-initiated
scan), and the video link.

Recording the take on this machine is not reliable, and the docs say so rather than pretending
otherwise, because both failures are platform-level rather than app-level:

- a cold app can miss Android's 5-second startForeground deadline and be killed, giving
  ForegroundServiceDidNotStartInTimeException -> Force finishing activity, i.e. the launcher
  where the scan should have started. The warm-up phase fixes it (a process that has started
  the service once never misses), but the fallback path of restarting the app gives the warmth
  back again, which is why the recorded scan is now triggered from the results screen's top-bar
  "Scan" instead of the intro's button.
- the app also died mid-scan on the emulator, with the launcher on screen afterwards and no
  exception of its own anywhere in logcat. No amount of warming fixes that one.

Neither reproduces on the real test device, where the same 22-photo library scans without
incident, so the take belongs on the phone. The script is the checklist for what it must show.
2026-10-14 11:13:21 +02:00