Skip to content

build(android): target API 36 (Android 16) for Play compliance - #581

Merged
pdfinn merged 2 commits into
masterfrom
fix/android-target-sdk-36
Sep 11, 2026
Merged

build(android): target API 36 (Android 16) for Play compliance#581
pdfinn merged 2 commits into
masterfrom
fix/android-target-sdk-36

Conversation

@pdfinn

@pdfinn pdfinn commented Sep 1, 2026

Copy link
Copy Markdown
Member

Summary

Google Play requires a target API within one year of the latest Android release — from Nov 1, 2026 an app targeting API 35 can no longer be updated. The store artifact (io.infernode, last uploaded android-v0.1.1 / versionCode 3001) targets 35. This moves it to 36 (Android 16).

The toolchain has to move with the target: AGP 8.7 was only tested to compileSdk 35, so building against 36 on it means either an unsupported-compileSdk warning on every build or papering over it with android.suppressUnsupportedCompileSdk. AGP 8.13.2 needs Gradle 8.13+; JDK 17 still satisfies both, so the CI setup-java pin is unchanged.

Behaviour changes audited for targetSdk 36

Change Impact
Edge-to-edge mandatory (windowOptOutEdgeToEdgeEnforcement ignored) No-op — InfernodeSDLActivity has applied its own insets since INFR-115
Predictive back on by default Would have broken us. See below
16 KB page sizes Already compliant under NDK r29
Orientation/aspect-ratio ignored on ≥600dp screens We set none
elegantTextHeight, MediaStore, health perms, strict intent matching, Mali IOCTLs N/A

Predictive back is the one that bites. At API 36 the platform stops dispatching KEYCODE_BACK and stops calling onBackPressed(), and the vendored org/libsdl/app/SDLActivity.java routes back through exactly those hooks (onBackPressed() honours SDL_ANDROID_TRAP_BACK_BUTTON, dispatchKeyEvent consumes mouse BUTTON_BACK). Without an opt-out the system would finish() the activity out from under the emu thread — no nativeQuit, no clean Inferno shutdown. Opted out with android:enableOnBackInvokedCallback="false", which is still honoured at 36; revisit when the SDL3 vendor drop migrates to OnBackInvokedCallback.

Not addressed here: Android 16's local-network permission (NEARBY_WIFI_DEVICES) is still opt-in, but once enforced it will gate the LAN 9P export and /mnt/llm mounts to RFC1918 hosts. That is a Play permission declaration and wants its own decision.

Changes

  • compileSdk / targetSdk 35 → 36
  • AGP 8.7.0 → 8.13.2, Kotlin 2.0.20 → 2.1.21, Gradle wrapper 8.10.2 → 8.13
  • android:enableOnBackInvokedCallback="false" on <application>
  • CI: platforms;android-36 build-tools;36.0.0 in both Android workflows
  • CI: smoke-test emulator API 34 → 36, so the gate exercises the level we now target
  • Docs/comments: android-app/README.md, tools/check-android-safearea.sh (Android 16 removed the edge-to-edge opt-out), and a stale READINESS.md note claiming the store is at versionCode 3 (it's 3001 since the first automated upload)

Testing

Built locally on Linux/amd64 against this branch head (JDK 17, NDK r29, SDK platform 36 / build-tools 36.0.0):

  • ./gradlew assembleDebug bundleRelease — both BUILD SUCCESSFUL, lintVitalRelease clean
  • APK badging: targetSdkVersion:'36', compileSdkVersion='36', native-code arm64-v8a + x86_64
  • Merged release manifest: targetSdkVersion="36" + the back opt-out, with the SMS and foreground-service strips from src/release/AndroidManifest.xml intact
  • zipalign -c -P 16 passes on every .so in the APK; readelf confirms 0x4000 LOAD alignment on libemu.so and libSDL3.so (both ABIs)
  • tools/check-android-safearea.sh and tools/check-android-syscalls.sh — OK

Caveat: the native libs and assets/inferno-root/ used for the local build were the already-staged (gitignored) artefacts rather than a fresh NDK cross-build — the targetSdk bump doesn't touch native code, and CI stages them fresh anyway. The android-apk gate plus the API 36 emulator smoke test is the real validation.

  • Tests pass (/tests/runner.dis -v) — not applicable, no Limbo/Dis code touched
  • New tests added (if applicable) — n/a; the existing smoke test now runs on API 36
  • Tested on: Linux/amd64 host build + APK/AAB inspection. Not yet on a handset — worth an internal-track install on the A55 to confirm the back gesture and safe-area insets on a real Android 16 device before promoting to production.

Checklist

  • Code follows the existing style of the file(s) modified
  • No .dis build artifacts committed from appl/ or tests/
  • Documentation updated (if applicable)
  • No secrets, API keys, or credentials included

After merge

Tag android-v0.1.2android-release.yml builds the signed AAB and uploads to the internal track (versionCode = commit count, ~3343, vs 3001 live). Promote internal → production in the console to actually clear Google's compliance warning — the CI service account is scoped to testing tracks only.

🤖 Generated with Claude Code

https://claude.ai/code/session_01HtLpTF1uJN5AatKYCNhxXx

pdfinn and others added 2 commits September 1, 2026 09:22
Google Play requires a target API within one year of the latest Android
release: from Nov 1, 2026 an app targeting API 35 can no longer be
updated. The store artifact was targeting 35.

Toolchain moves with the target — AGP 8.7 was only tested to compileSdk
35, so building against 36 on it means an unsupported-compileSdk warning
on every build or papering over it with suppressUnsupportedCompileSdk.
Neither belongs on a Play-shipping artefact. AGP 8.13.2 needs Gradle
8.13+; JDK 17 still satisfies both, so the CI setup-java pin is
unchanged.

Behaviour changes audited for targetSdk 36:

  * Edge-to-edge is now mandatory (windowOptOutEdgeToEdgeEnforcement is
    deprecated and ignored). No-op for us — InfernodeSDLActivity has
    applied its own window insets since INFR-115.
  * Predictive back is on by default and stops dispatching KEYCODE_BACK
    and calling onBackPressed(). The vendored SDL3 activity still routes
    back through exactly those hooks, so without an opt-out the system
    would finish() the activity out from under the emu thread with no
    nativeQuit. Opted out via enableOnBackInvokedCallback="false", which
    is still honoured at 36.
  * 16 KB page sizes — already compliant; libemu.so and libSDL3.so build
    with 16 KB-aligned LOAD segments under NDK r29, and zipalign -P 16
    passes on every .so in the APK.
  * Orientation/aspect-ratio restrictions ignored on >=600dp screens —
    we set none.

Not addressed here: Android 16's local-network permission
(NEARBY_WIFI_DEVICES) is still opt-in, but once enforced it will gate
the LAN 9P export and /mnt/llm mounts to RFC1918 hosts. That is a Play
permission declaration and wants its own decision.

The smoke-test emulator moves 34 -> 36 so the gate actually exercises
the level we now target.

Verified: assembleDebug and bundleRelease both succeed; APK badging
reports targetSdkVersion 36 / compileSdkVersion 36, the merged release
manifest carries targetSdkVersion 36 plus the back opt-out (SMS and
foreground-service strips intact), lintVitalRelease is clean, and both
android lints pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HtLpTF1uJN5AatKYCNhxXx
@pdfinn
pdfinn merged commit df596a4 into master Sep 11, 2026
22 checks passed
@pdfinn
pdfinn deleted the fix/android-target-sdk-36 branch September 11, 2026 03:10
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.

1 participant