dismissASystemErrorDialog can click any AlertDialog: android:id/button1 hit DocumentsUI's own save dialog #271

Open
opened 2026-09-07 22:08:41 +00:00 by JMR-dev · 1 comment
JMR-dev commented 2026-09-07 22:08:41 +00:00 (Migrated from github.com)

Found while tracing #102's MainActivity-destroyed mode. Recorded rather than acted on there,
because it is a separate decision from the one that PR makes.

What it is

SafPickerRoundTripTest.ERROR_DIALOG_BUTTONS is the list dismissASystemErrorDialog walks:

val ERROR_DIALOG_BUTTONS = listOf(
    "android:id/aerr_wait",
    "android:id/aerr_close",
    "android:id/button1",
)

Its KDoc says the ids are used rather than the button text because the text is localised, and by id
rather than "the first button in the system window" "because that would click whatever system
window happened to be there"
. The first two ids are app-error-dialog specific and hold that line.
android:id/button1 does not: it is the framework's generic AlertDialog positive button, present
on every AlertDialog in every app on the device. The KDoc's own reason for it —
"catches the plainer BaseErrorDialog shapes that have no aerr_ ids" — is true, and so is the
other half nobody wrote down.

It has actually clicked something else

Measured on the API 35 gating leg of run 34161043035 attempt 1, in the per-test logcat for
aFailedSaveDeletesTheDocumentItCouldNotWrite:

20:59:35.668  UiDevice: Retrieving node ... [RES='android:id/aerr_wait'].
20:59:35.680  UiDevice: Retrieving node ... [RES='android:id/aerr_close'].
20:59:35.686  UiDevice: Retrieving node ... [RES='android:id/button1'].
20:59:35.689  UiObject2: Clicking on (927, 2274).
20:59:36.028  MainActivity RESTARTED / STARTED / RESUMED
20:59:36.350  VRI[PickActivity]: visibilityChanged oldVisibility=true newVisibility=false

Both aerr_ ids missed and button1 hit. Three hundred milliseconds later the save picker
returned and MainActivity came back
, so what was clicked was a button in DocumentsUI's own
create-document flow — not a system error dialog. dismissASystemErrorDialog completed a save
dialog it believed it was dismissing an ANR from.

On that run it did no harm and arguably helped: the walk had already timed out. There is no
reason it always would.
The same click on a "Discard"/"Cancel"/"Replace?" button is a silent
change to what the test did, and the test would then assert against a document it did not mean to
create — the failure mode this class is least able to notice, because everything downstream still
looks like a save.

Why it is a decision rather than an obvious delete

button1 was added for a real reason and deleting it narrows what can be dismissed. The three
candidate answers, in the order I would try them:

  1. Scope it to system windows. dismissASystemErrorDialog exists for dialogs owned by
    system_server, which show as package android. By.res("android:id/button1").pkg("android")
    keeps the BaseErrorDialog case and cannot match DocumentsUI. Cheapest, and it is the KDoc's
    stated intent expressed in the selector.
  2. Drop it. Establish first whether any recorded failure was ever dismissed only by
    button1 on a genuine error dialog. If none was, it is carrying risk for a case that has not
    happened.
  3. Leave it and say so. Legitimate if 1 turns out not to work on some image, but then the KDoc
    has to name the hazard rather than claim the opposite.

Done means

Either the selector cannot match a non-system dialog, or the KDoc stops saying it will not click
"whatever system window happened to be there" while the last entry does exactly that. A one-line
@Suppress-style hand-wave is not it: the sentence in the KDoc is currently false, and that is the
part that has to change either way.

Related: #102 (docs/ci-failure-modes.md, mode 6), #93 which is where the dismissal came from.

Found while tracing #102's `MainActivity`-destroyed mode. Recorded rather than acted on there, because it is a separate decision from the one that PR makes. ## What it is `SafPickerRoundTripTest.ERROR_DIALOG_BUTTONS` is the list `dismissASystemErrorDialog` walks: ```kotlin val ERROR_DIALOG_BUTTONS = listOf( "android:id/aerr_wait", "android:id/aerr_close", "android:id/button1", ) ``` Its KDoc says the ids are used rather than the button text because the text is localised, and by id rather than "the first button in the system window" **"because that would click whatever system window happened to be there"**. The first two ids are app-error-dialog specific and hold that line. `android:id/button1` does not: it is the framework's generic `AlertDialog` positive button, present on every `AlertDialog` in every app on the device. The KDoc's own reason for it — "catches the plainer `BaseErrorDialog` shapes that have no `aerr_` ids" — is true, and so is the other half nobody wrote down. ## It has actually clicked something else Measured on the API 35 gating leg of run `34161043035` attempt 1, in the per-test logcat for `aFailedSaveDeletesTheDocumentItCouldNotWrite`: ``` 20:59:35.668 UiDevice: Retrieving node ... [RES='android:id/aerr_wait']. 20:59:35.680 UiDevice: Retrieving node ... [RES='android:id/aerr_close']. 20:59:35.686 UiDevice: Retrieving node ... [RES='android:id/button1']. 20:59:35.689 UiObject2: Clicking on (927, 2274). 20:59:36.028 MainActivity RESTARTED / STARTED / RESUMED 20:59:36.350 VRI[PickActivity]: visibilityChanged oldVisibility=true newVisibility=false ``` Both `aerr_` ids missed and `button1` hit. Three hundred milliseconds later the **save picker returned and `MainActivity` came back**, so what was clicked was a button in DocumentsUI's own create-document flow — not a system error dialog. `dismissASystemErrorDialog` completed a save dialog it believed it was dismissing an ANR from. On that run it did no harm and arguably helped: the walk had already timed out. **There is no reason it always would.** The same click on a "Discard"/"Cancel"/"Replace?" button is a silent change to what the test did, and the test would then assert against a document it did not mean to create — the failure mode this class is least able to notice, because everything downstream still looks like a save. ## Why it is a decision rather than an obvious delete `button1` was added for a real reason and deleting it narrows what can be dismissed. The three candidate answers, in the order I would try them: 1. **Scope it to system windows.** `dismissASystemErrorDialog` exists for dialogs owned by `system_server`, which show as package `android`. `By.res("android:id/button1").pkg("android")` keeps the `BaseErrorDialog` case and cannot match DocumentsUI. Cheapest, and it is the KDoc's stated intent expressed in the selector. 2. **Drop it.** Establish first whether any recorded failure was ever dismissed *only* by `button1` on a genuine error dialog. If none was, it is carrying risk for a case that has not happened. 3. **Leave it and say so.** Legitimate if 1 turns out not to work on some image, but then the KDoc has to name the hazard rather than claim the opposite. ## Done means Either the selector cannot match a non-system dialog, or the KDoc stops saying it will not click "whatever system window happened to be there" while the last entry does exactly that. A one-line `@Suppress`-style hand-wave is not it: the sentence in the KDoc is currently false, and that is the part that has to change either way. Related: #102 (`docs/ci-failure-modes.md`, mode 6), #93 which is where the dismissal came from.
JMR-dev commented 2026-09-07 22:18:45 +00:00 (Migrated from github.com)

Two things I can add now, one of which upgrades this from "a button in DocumentsUI" to
"almost certainly its Save button".

The coordinate says which button

The click was at (927, 2274). The emulator is 1080 wide, and the other UiObject2 clicks in
the same test's logcat give the scale — the fixture list item is at
boundsInScreen: Rect(196, 1035 - 497, 1086), and the launcher-ANR dismissal that did find
aerr_wait clicked (540, 1359), i.e. horizontally centred. (927, 2274) is bottom-right of the
screen, which is where a Material dialog's positive action sits and is nowhere near a centred
system error dialog's button. Combined with what happened 339 ms later — MainActivity resumed
and PickActivity's window went away — the reading is that this clicked DocumentsUI's own
save/confirm button
, not merely "something in DocumentsUI".

It is still an inference from geometry plus effect rather than from a node dump, and I would keep
it labelled that way.

It is the same incident as #102's destroyed Activity, not a separate finding

I filed these as two, and on re-reading the trace they are one. dismissASystemErrorDialog
falling through to button1 is what closed the picker on that run:

20:59:35.680  Node not found ... [RES='android:id/aerr_wait']
20:59:35.686  Node not found ... [RES='android:id/aerr_close']
20:59:35.686  Retrieving node ... [RES='android:id/button1']
20:59:35.689  UiObject2: Clicking on (927, 2274).
20:59:36.033  MainActivity RESUMED
20:59:36.350  VRI[PickActivity]: visibilityChanged ... newVisibility=false

From 36.033 there was nothing left to back out of — and dismissThePicker then spent two more
back presses, the second of which finished MainActivity. So this ticket is not only a latent
hazard; on the one occurrence anybody has traced it is the first step of the failure #272 fixes.

That does not change what this ticket asks for, and it does not make #272 depend on it: #272 stops
the presses regardless of what closed the picker. It does mean the "it did no harm on that run"
line in the body above is too generous — what it did was make the app focused while the loop still
believed it was not, which is exactly the state #272 now re-reads for.

**Two things I can add now, one of which upgrades this from "a button in DocumentsUI" to "almost certainly its Save button".** ## The coordinate says which button The click was at **(927, 2274)**. The emulator is 1080 wide, and the other `UiObject2` clicks in the same test's logcat give the scale — the fixture list item is at `boundsInScreen: Rect(196, 1035 - 497, 1086)`, and the launcher-ANR dismissal that *did* find `aerr_wait` clicked (540, 1359), i.e. horizontally centred. (927, 2274) is bottom-right of the screen, which is where a Material dialog's positive action sits and is nowhere near a centred system error dialog's button. Combined with what happened 339 ms later — `MainActivity` resumed and `PickActivity`'s window went away — the reading is that this clicked **DocumentsUI's own save/confirm button**, not merely "something in DocumentsUI". It is still an inference from geometry plus effect rather than from a node dump, and I would keep it labelled that way. ## It is the same incident as #102's destroyed Activity, not a separate finding I filed these as two, and on re-reading the trace they are one. `dismissASystemErrorDialog` falling through to `button1` is **what closed the picker** on that run: ``` 20:59:35.680 Node not found ... [RES='android:id/aerr_wait'] 20:59:35.686 Node not found ... [RES='android:id/aerr_close'] 20:59:35.686 Retrieving node ... [RES='android:id/button1'] 20:59:35.689 UiObject2: Clicking on (927, 2274). 20:59:36.033 MainActivity RESUMED 20:59:36.350 VRI[PickActivity]: visibilityChanged ... newVisibility=false ``` From `36.033` there was nothing left to back out of — and `dismissThePicker` then spent two more back presses, the second of which finished `MainActivity`. So this ticket is not only a latent hazard; on the one occurrence anybody has traced it is the *first* step of the failure #272 fixes. That does not change what this ticket asks for, and it does not make #272 depend on it: #272 stops the presses regardless of what closed the picker. It does mean the "it did no harm on that run" line in the body above is too generous — what it did was make the app focused while the loop still believed it was not, which is exactly the state #272 now re-reads for.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMediaConverter#271