Skip to content

Surface API 33-37 and 16 KB-page system images#397

Merged
jpcottin merged 1 commit into
google:masterfrom
jpcottin:fix/api-33-and-16k-sysimg
May 15, 2026
Merged

Surface API 33-37 and 16 KB-page system images#397
jpcottin merged 1 commit into
google:masterfrom
jpcottin:fix/api-33-and-16k-sysimg

Conversation

@jpcottin
Copy link
Copy Markdown
Collaborator

Closes #392.

What's broken today

emu-docker list silently caps at API 32 (Android 12L "S"). Two collaborating bugs cause it:

  1. API_LETTER_MAPPING stops at "32": "S". For API ≥ 33 the lookup falls through to self.letter = "A" ("unknown code").
  2. get_images_info filters with info.letter >= MIN_REL_X64 ("O"). String compare "A" < "O" → the entry is dropped silently.

So every API 33+ image is invisible in emu-docker list, and unbuildable via find_image() lookups.

The new 16 KB-page-size axis

Starting at API 36, Google publishes a second variant of each system image with a 16 KB kernel page size. The encoding shows up in three different places that all need handling:

  1. SDK XML (emu_downloads_menu): <tag><id> is not reliable — for released API 36 ps16k images the tag is google_apis (same as the 4 KB variant); for 36.0-Baklava it's page_size_16kb; for CANARY pre-release it's multiple <tag> elements (google_apis, page_size_16kb, ai_glasses_compatible). Only the path's third segment (system-images;<api>;<sort>;<abi>) reliably says whether it's a 16 KB variant (sort ends in _ps16k). The zip lives under sys-img/<sort_base>/ where sort_base strips the suffix.
  2. source.properties inside the zip (SystemImageReleaseZip): SystemImage.TagId is comma-joined for 16 KB variants (e.g. google_apis,page_size_16kb). The previous tag()/short_tag() KeyError'd on the joined string.
  3. Docker image labels: the emulator container layer reads its base sys-img layer's labels for metadata. Without a qemu.is_16k LABEL on the sys-img image, the emulator layer can't detect the variant.

Changes

  • Bump SYSIMG_REPOS to sys-img2-5.xml (matches the SDK Manager; -5 is what newer API entries land in first).
  • Add 33: T, 34: U, 35: V, 36: B, 37: C to API_LETTER_MAPPING.
  • Replace letter-based MIN_REL_* constants with int-based MIN_API_*. Parse api-level (which can be "36", "36.1", "37.0", or "36x" for ext-SDK) into a new api_major int via regex; filter on that.
  • Letter fallback: exact api → api_major lookup → the api string itself. The "A" silent-filter trap is gone.
  • Filter pre-release builds (<codename> set: Baklava, CinnamonBun, CANARY, …) and ext-SDK images (api-level "36x", "33x", …) from the listing to keep it uncluttered.
  • SysImgInfo derives (sort_base, is_16k) from the path; URL directory uses sort_base (with default → android remap).
  • SystemImageReleaseZip.tag() reduces comma-joined SystemImage.TagId to its non-page-size component; new is_16k() reports the variant.
  • image_name(), download_name(), __str__ get a -ps16k / ps16k suffix in three places (SysImgInfo, SystemImageContainer, EmulatorContainer) so 4 KB and 16 KB variants of the same (api, sort, abi) coexist as distinct docker images.
  • list_all_downloads and select_image surface the ps16k variant as a trailing column / suffix.
  • short_tag / short_abi use dict.get() with self-fallback so future tag/abi additions don't crash the listing.
  • Dockerfile.system_image emits a qemu.is_16k LABEL so the emulator layer can detect the variant from base-image labels.
  • Sort key becomes (api_major, api, tag, abi, is_16k) for stable ordering with dotted api versions and ps16k siblings.
  • README: refresh the emu-docker list example output to include modern API levels and a ps16k row; document the suffix and the filtered categories.

Tests

  • Fix tests/test_emu_downloads_menu.py: a 2023-05-18 merge conflict had been committed unresolved. The "other" half of the conflict (PlatformToolsTestCase, RetrieveEmulators, RetrieveImages) was dead duplicate code that had already been migrated to test_platform_tools.py / test_retrieve_emulators.py / test_retrieve_images.py; the giant lines were old XML fixtures from those classes. Resolved by keeping the HEAD half. The file goes from 244 KB / un-collectable by pytest to 1.5 KB and its two download tests now run.
  • New tests/test_sysimg_info.py with 25 unit tests covering the new parser logic: API_LETTER_MAPPING for 33–37, api_major regex for "36"/"36.1"/"37.0"/"36x", letter fallback for unknown future APIs, is_preview / is_ext_sdk detection, is_16k detection from the path, URL construction for ps16k and the default → android remap, and SystemImageReleaseZip.tag() / is_16k() for every comma-joined SystemImage.TagId shape seen in the SDK XML.

Result

emu-docker list now returns 94 entries (was ~60). Highest API per sort:

  • google_apis: 37.0
  • google_apis_playstore: 37.0
  • android: 36
  • android-tv: 36
  • google_atd: 36

Sample of new lines:

SYSIMG B google_apis x86_64 36   .../sys-img/google_apis/x86_64-36_r07.zip
SYSIMG B google_apis x86_64 36 ps16k .../sys-img/google_apis/x86_64-ps16k-36_r07.zip
SYSIMG B google_apis_playstore x86_64 36 ps16k .../sys-img/google_apis_playstore/x86_64-playstore-ps16k-36_r07.zip
SYSIMG B google_apis x86_64 36.1 .../sys-img/google_apis/x86_64-36.1_r04.zip
SYSIMG C google_apis x86_64 37.0 ps16k .../sys-img/google_apis/x86_64-ps16k-37.0_r04.zip

Test plan

  • emu-docker list: 94 entries; APIs 33–37 visible with correct letters T/U/V/B/C; no codename or ext-SDK leaks.
  • 16 KB-page variants visible alongside the 4 KB ones, distinguished by a ps16k column in the output; every constructed URL HEAD returns 200 (random sample of regular + ps16k + default→android).
  • 4 KB end-to-end: emu-docker create ./emulator-36.6.8.zip ./sys-img-x86_64-36_r07.zip --start produced sys-36-google-x64 (4.97 GB) and 36-google-x64 (6.1 GB); container launched, adb connected, sys.boot_completed=1, ro.build.version.sdk=36, ro.build.version.release=16, ro.product.model=sdk_gphone64_x86_64.
  • 16 KB end-to-end: emu-docker create ./emulator-36.6.8.zip ./sys-img-x86_64-ps16k-36_r07.zip produced sys-36-google-x64-ps16k (4.96 GB) and 36-google-x64-ps16k (6.09 GB); container booted, ro.product.model=sdk_gphone16k_x86_64, getconf PAGE_SIZE=16384 (16 KB confirmed), ro.build.version.sdk=36, ro.build.version.release=16.
  • All 35 tests under tests/ (excluding e2e) pass.

Backwards compatibility

  • image_name() output is unchanged for every existing 4 KB image; only the new 16 KB variants get a -ps16k suffix, so already-built / already-tagged images keep working.

Closes google#392 (Android 16 / SDK 36).

emu-docker list silently dropped every API >= 33. Two collaborating
issues caused it:

1. SysImgInfo set self.letter from API_LETTER_MAPPING which stopped at
   "32": "S"; for any newer API the codename branch fell through to
   self.letter = "A" ("unknown code").
2. get_images_info filtered with `info.letter >= MIN_REL_X64` ("O"),
   so the "A" sentinel made every API 33+ image fail string compare
   and disappear from the listing.

The new system-image axis introduced at API 36 -- 16 KB page-size
variants -- was unhandled in three places:

a. SDK XML (emu_downloads_menu): the <tag><id> element can be either
   google_apis or page_size_16kb, and pre-release CANARY entries carry
   multiple <tag> elements. The path is the only deterministic source
   for (sort, is_16k); the zip lives under sys-img/<sort_base>/ where
   sort_base strips the trailing _ps16k.

b. source.properties parsing (android_release_zip.SystemImageReleaseZip):
   SystemImage.TagId is comma-joined for 16 KB images
   ("google_apis,page_size_16kb"); the previous tag()/short_tag()
   KeyError'd on the joined string.

c. Image naming: 4 KB and 16 KB variants of the same (api, sort, abi)
   need distinct docker image tags; the sys-img Dockerfile carries a
   qemu.is_16k label so the emulator layer can detect the variant
   from the base-image labels.

Changes:
- Bump SYSIMG_REPOS to sys-img2-5.xml (matches what the SDK Manager
  uses; -5 is what newer API entries land in first).
- Extend API_LETTER_MAPPING with 33: T, 34: U, 35: V, 36: B, 37: C.
- Replace MIN_REL_I386 / MIN_REL_X64 (letter strings) with
  MIN_API_I386 / MIN_API_X64 (ints), and filter on a new api_major
  attribute parsed from the api-level string with a regex (handles
  "36", "36.0", "36.1", "37.0", "36x").
- Letter fallback: exact api lookup -> api_major lookup -> the api
  string itself (no more silent "A" filter trap).
- Filter pre-release builds (<codename>: Baklava / CinnamonBun /
  CANARY) and ext-SDK images (api-level "36x" etc.) from the listing
  to keep it uncluttered.
- SysImgInfo derives (sort_base, is_16k) from the path's third segment;
  the URL directory uses sort_base (with default -> android remap).
- SystemImageReleaseZip.tag() reduces a comma-joined SystemImage.TagId
  to its non-page-size component; new is_16k() reports the variant.
- image_name(), download_name(), and __str__ get a -ps16k / ps16k
  suffix for 16 KB variants in three places: SysImgInfo,
  SystemImageContainer (local-zip path), and EmulatorContainer.
- list_all_downloads and select_image surface the ps16k variant as a
  trailing column / suffix so users can tell the two apart at a
  glance.
- short_tag / short_abi tolerate unknown values via dict.get fallback,
  so future tag/abi additions don't crash the listing.
- Dockerfile.system_image emits a qemu.is_16k LABEL so the emulator
  layer (which reads sys-img labels via image_labels()) can detect
  the 16 KB variant.
- Sort key becomes (api_major, api, tag, abi, is_16k) for stable
  ordering with dotted api versions and ps16k siblings.
- README: refresh the emu-docker list example output to include
  modern API levels and a ps16k row, and document the suffix and
  filtered categories.

Tests:
- Fix tests/test_emu_downloads_menu.py: resolve a stale 2023-05-18
  merge conflict (the "other" half was dead duplicate code already
  migrated to test_platform_tools / test_retrieve_emulators /
  test_retrieve_images); the file shrinks from 244 KB / un-collectable
  to 1.5 KB and its two pytest functions now run.
- Add tests/test_sysimg_info.py with 25 unit tests covering the new
  parser logic: API_LETTER_MAPPING for 33-37, api_major regex for
  "36"/"36.1"/"37.0"/"36x", letter fallback for unknown future APIs,
  is_preview/is_ext_sdk detection, is_16k detection from the path,
  URL construction for ps16k and the default->android remap, and
  SystemImageReleaseZip.tag()/is_16k() for every comma-joined
  SystemImage.TagId shape seen in the SDK XML.

End-to-end verified on a podman host:

- emu-docker list: 94 entries (was ~60). google_apis and
  google_apis_playstore go up to 37.0; ps16k variants visible
  alongside the 4 KB ones; no codename or ext-SDK leaks; random
  sample HEAD against the constructed URLs all return 200.
- 4 KB build: emu-docker create ./emulator-36.6.8.zip
  ./sys-img-x86_64-36_r07.zip --start produced sys-36-google-x64
  (4.97 GB) and 36-google-x64 (6.1 GB); container launched, adb
  connected, sys.boot_completed=1, ro.build.version.sdk=36,
  ro.build.version.release=16, ro.product.model=sdk_gphone64_x86_64.
- 16 KB build: emu-docker create ./emulator-36.6.8.zip
  ./sys-img-x86_64-ps16k-36_r07.zip produced sys-36-google-x64-ps16k
  (4.96 GB) and 36-google-x64-ps16k (6.09 GB); container booted,
  ro.product.model=sdk_gphone16k_x86_64, getconf PAGE_SIZE=16384,
  ro.build.version.sdk=36, ro.build.version.release=16.

Backwards compatibility: image_name() output for non-ps16k images is
unchanged. All 35 tests under tests/ (excluding e2e) pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is Android 16 SDK 36 System Image avaliable?

1 participant