Drop API 37 from the E2E matrix and write down why

The android-37.0 emulator image crash-loops surfaceflinger inside its own
gralloc mapper: RegionSamplingThread calls GraphicBuffer::lock, which reaches
GoldfishMapper::readFromHost, which asserts that the host has not negotiated
ReadColorBufferDma. It has, so surfaceflinger aborts, restarts, and aborts
again. Nothing this app does can survive that, and it reproduces on a GitHub
runner under swiftshader_indirect and on a workstation under -gpu host alike.

There is no ATD image at android-37.0 to fall back to, and -feature -GLDMA is
accepted by the emulator but does not prevent the assertion.

Correcting the previous commit, which is already pushed so its message stands:
ram-size was not the cause of that failure. Setting it did move the job from
failing at install to failing during the test run, which is how the real
crash became visible, but at 2560M the guest had 1.5 GB free when it died.
The setting is kept because the emulator's own floor varies by API level --
2048M at 33, 2560M at 34 to 36 -- and pinning it makes the matrix uniform.

Also corrected: a comment claiming this could not be reproduced locally. It
can, and the local crash was the same one all along.

Dropped the dmesg probe. adb shell is not root, so klogctl is denied and it
only ever printed a permission error -- which a later reader would reasonably
misread as "no OOM kills".

docs/api-37-emulator-crash.md carries the evidence, the ruled-out fixes, the
reproduction, and how to file it upstream, so re-adding the row later starts
from what is already known rather than from scratch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-21 21:35:04 -05:00
co-authored by Claude Opus 5
parent 2fe1aa9f9c
commit 4e6fe6b75a
2 changed files with 212 additions and 27 deletions
+185
View File
@@ -0,0 +1,185 @@
# API 37 is not tested in CI: a crash in Google's `android-37.0` emulator image
**Status:** open upstream, worked around by removing API 37 from the E2E matrix
**Last verified:** 2026-08-21, against emulator `37.1.11.0` and system image revision 6
`minSdk` is 33 and `targetSdk` is 37, and the E2E matrix in
[`status_check.yml`](../.github/workflows/status_check.yml) runs API 33 through 36.
API 37 is deliberately absent. This is why.
## Summary
The `android-37.0` emulator system image crashes `surfaceflinger` in a loop. The app
under test never gets a working framework, so every instrumented test fails regardless
of what the app does. The bug is in the emulator image, not in this project.
The crash is an assertion inside the emulator's own gralloc implementation:
```
Executable: /system/bin/surfaceflinger
signal 6 (SIGABRT), code -1 (SI_QUEUE), tid: RegionSampling
Abort message: 'Assertion failed: !rcEnc->featureInfo()->hasReadColorBufferDma'
#03 mapper.ranchu.so GoldfishMapper::readFromHost(cb_handle_t const&) const
#04 mapper.ranchu.so GoldfishMapper::GoldfishMapper()::'lambda'(...)::__invoke
#05 libui.so android::Gralloc5Mapper::lock(...)
#06 libui.so android::GraphicBufferMapper::lock(...)
#07 libui.so android::GraphicBuffer::lockAsync(...)
#08 libui.so android::GraphicBuffer::lock(...)
#09 surfaceflinger android::RegionSamplingThread::threadMain()
```
`RegionSamplingThread` is SystemUI's navigation-bar luma sampling. It calls
`GraphicBuffer::lock`, which routes into `GoldfishMapper::readFromHost`, which asserts
that the host has *not* negotiated the `ReadColorBufferDma` capability. On this image
the host has, so the assertion fails and `surfaceflinger` aborts. It restarts and
aborts again.
## Impact
The failure surfaces in two different ways depending on how far the job gets, which is
why it took several rounds to identify:
| Guest RAM | Where it dies | What CI reports |
|---|---|---|
| 1536 MB | during APK install | `Unknown failure: cmd: Can't find service: package` |
| 2560 MB | during the test run | `There were failing tests` — all of them |
At 2560 MB the install succeeds and the tests actually execute, then fail wholesale.
The first failure in the report is misleading:
```
kotlin.UninitializedPropertyAccessException: lateinit property output has not
been initialized
at Media3EngineTest.tearDown(Media3EngineTest.kt:53)
java.lang.IllegalStateException: WorkManager is not initialized properly.
You have explicitly disabled WorkManagerInitializer in your manifest, ...
```
Neither is a real defect in this project. `tearDown` throws because `setUp` never got
far enough to assign `output`, and WorkManager's `InitializationProvider` never runs
because content-provider installation fails on a framework whose `surfaceflinger` is
crash-looping. The same tests pass at API 33, 34, 35, and 36 in the same CI run, and
the first `surfaceflinger` abort is timestamped *before* the test results are reported.
That last point is inference rather than proof: the definitive check would be running
the suite against a healthy API 37 device, which does not exist yet. If a fixed image
ships and these tests still fail, treat the WorkManager error as a real finding.
## Environment
Reproduced identically in two unrelated environments, so it is not specific to a host
GPU, driver, or CI runner.
| | GitHub Actions | Local workstation |
|---|---|---|
| Host | `ubuntu-latest`, no GPU | Fedora, Intel Iris Xe (TGL GT2) |
| Emulator | `37.1.11.0` (build 15917651) | `37.1.11.0` (build 15917651) |
| GPU mode | `swiftshader_indirect` | `host` |
| Result | boots, aborts during tests | aborts before boot completes |
System image: `system-images;android-37.0;google_apis;x86_64`, `Pkg.Revision=6`,
`AndroidVersion.ApiLevel=37.0`, `AndroidVersion.ExtensionLevel=22`
```
Build fingerprint: google/sdk_gphone64_x86_64/emu64xa:17/CE2A.260420.019/15611780:userdebug/dev-keys
Kernel Release: 6.12.58-android16-6-gccafb60de224-ab14828483
```
## What was ruled out, and how
Each of these was tested rather than reasoned about, because the first three attempts
at this bug were plausible fixes that turned out to address earlier, unrelated failures.
**Guest memory.** The emulator raises an undersized guest to a minimum on its own, but
only for API levels it recognises, and it does not recognise `"37.0"`. API 33 bumps to
2048 MB and 34/35/36 to 2560 MB, while API 37 logged no bump at all and ran at the
`pixel_6` default of 1536 MB. Setting `ram-size: 2560M` explicitly fixed that asymmetry
and did change the outcome — the job got past install and into the test run — but it is
not the underlying bug. At the moment of failure the guest reported `MemTotal 2527392
kB` with `MemAvailable 1507104 kB`: 1.5 GB free, and no OOM kills.
**GPU mode.** Both `swiftshader_indirect` and `host` crash, with the same assertion and
the same frames. The crash is in the gralloc mapper, below the renderer.
**Disabling the DMA feature.** `GLDMA` is the host feature that most plausibly backs the
guest's `hasReadColorBufferDma`. Launching with `-feature -GLDMA` was accepted by the
emulator — the log confirms `Feature 'GLDMA' (51) is overridden to 'disabled'` — and
`surfaceflinger` still aborted 13 times and the device never finished booting. Whatever
sets that guest capability, it is not this flag.
**An ATD image.** `google_atd` / `aosp_atd` images are built for automated testing and
ship without the SystemUI package set, which is what drives `RegionSamplingThread` in
the first place. That would likely sidestep the bug class entirely, but **no ATD image
exists for `android-37.0`** — only `google_apis`, `google_apis_playstore`, the `ps16k`
16 KB-page variants, and Wear OS. Check again when revisiting; if an ATD image appears,
try it before anything else here.
## Reproducing it
Locally, with `-gpu host` so the emulator itself does not segfault on Intel graphics:
```bash
sdkmanager --install "system-images;android-37.0;google_apis;x86_64"
echo no | avdmanager create avd -n api37_repro \
-k "system-images;android-37.0;google_apis;x86_64" -d pixel_6 --force
$ANDROID_HOME/emulator/emulator -avd api37_repro \
-no-window -gpu host -noaudio -no-boot-anim -camera-back none -no-snapshot &
# Boot never completes. Count the aborts:
adb logcat -d -b crash | grep -c hasReadColorBufferDma
```
`sys.boot_completed` never reaches `1`, `pgrep -f system_server` stays empty, and
`keystore2`'s watchdog logs `await_boot_completed ... Overdue` indefinitely.
To see the CI-side form instead, restore the API 37 row in the E2E matrix of
`status_check.yml` (`api-level: "37.0"` — a bare `37` fails earlier still, during SDK
setup, because there is no `platforms;android-37`).
## Filing this upstream
Not yet filed. To file it:
1. Go to <https://issuetracker.google.com/> and sign in with a Google account.
2. Choose **Report an issue**, then pick the component for the Android emulator — search
the component picker for "Emulator"; it sits under the Android Studio component tree.
If the picker is unclear, Android Studio's **Help → Submit Feedback** opens the same
tracker with the component preselected, and the emulator's own **Extended controls →
Help → File a bug** does likewise.
3. Title it for the mechanism, not the symptom, so it is searchable — for example:
`surfaceflinger aborts in GoldfishMapper::readFromHost (hasReadColorBufferDma) on
android-37.0 google_apis x86_64`.
4. Paste the assertion and backtrace from the top of this document, the environment
table, and the reproduction steps above. State explicitly that it reproduces on two
unrelated hosts under both GPU modes — that is the detail that stops it being closed
as a local graphics problem.
5. List what was ruled out. Bugs that arrive with `-feature -GLDMA` already eliminated
tend not to bounce back asking for it.
6. Attach:
- the guest tombstone, via `adb pull /data/tombstones` (or the `pbtombstone` output
the crash log names)
- `adb logcat -d -b crash > crash.txt`
- the emulator's own stdout log, captured by redirecting the launch command
- the AVD's `config.ini`
- a link to a failing CI job, which shows it on hardware you do not control:
<https://github.com/JMR-dev/LibreMediaConverter/actions/runs/32545625459/job/96963461184>
Record the issue number here once filed.
## When to revisit
Re-add the API 37 row when any of these happens:
- a new `android-37.0` system image revision ships (this was revision 6)
- an ATD image appears for API 37
- the upstream issue is marked fixed
Until then the coverage gap is narrow but real. `targetSdk` is 37, so the app is still
compiled and unit-tested against it, and the API-dependent behaviour this matrix exists
to exercise — the foreground service type, which is absent below 34, `dataSync` at 34,
and `mediaProcessing` from 35 — is fully covered at 35 and 36. What is *not* covered is
any behaviour Android 17 changes relative to 36. Verify that on a physical device before
release.