Skip to content

fix(android): cold-launch NPE guard + root cause & fix for the #15 mod crashes (runtime fix: N64ModernRuntime#1) - #20

Open
Rival wants to merge 1 commit into
AurelioB:androidfrom
Rival:fix/android-launch-npe
Open

fix(android): cold-launch NPE guard + root cause & fix for the #15 mod crashes (runtime fix: N64ModernRuntime#1)#20
Rival wants to merge 1 commit into
AurelioB:androidfrom
Rival:fix/android-launch-npe

Conversation

@Rival

@Rival Rival commented Aug 29, 2026

Copy link
Copy Markdown

Summary

Small launcher fix, plus the diagnosis + fix for the "every code mod crashes on Android" issue reported in #15 (the actual fixes live in the runtime submodule — see the companion PR).

This PR

applyImmersiveFullscreen() crashed every cold launch of a freshly installed build on ColorOS 16 / Android 16 (Oppo Find N6): PhoneWindow.getInsetsController() throws an internal NPE when called from onCreate() before the DecorView is attached. Guarded with a NullPointerException catch — fullscreen is re-applied from onResume()/onWindowFocusChanged() once the view exists, so nothing is lost, only deferred.

The mod-loading crashes from #15 — root cause + fix

We reproduced and instrumented this on-device. All code mods (.nrm) crashed at startup with SIGSEGV (SEGV_ACCERR)trying to execute non-executable memory — while .rtz texture packs worked. Raw evidence:

Fatal signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x6ff221cf20 (Game Start Thre)
Cause: trying to execute non-executable memory.
x17 = 0x6ff221cf20 = pc
#00 pc 0x8a4f20 libmain.so

Instrumenting librecomp's unprotect()/protect() showed protect() returning EACCES for every hooked page: on Android an untrusted_app process cannot regain PROT_EXEC on a file-backed page after mprotect() dirtied it with PROT_WRITE (SELinux execmod). The page stayed read-write and the first branch into the hooked function faulted. On desktop Linux there is no such policy — which is why the PC builds are unaffected.

Fix (in the N64ModernRuntime submodule): swap each patched page for a fresh anonymous page at the same address via mremap(MREMAP_FIXED) (anonymous PROT_EXEC is what JIT relies on), with an mprotect fallback. Plus two sibling issues found on the way:

  • the 16-byte patch window can cross a page boundary (Android builds don't enforce 16-byte function alignment, unlike PC/macOS) — now handled as a page range;
  • mod manifests with Number config options (TSRStormed-FasterTransitions, FOVSlider) were rejected as Invalid mod — strict get_ptr<int64_t*> vs nlohmann's number_unsigned; cherry-picked the upstream fix.

➡️ Runtime PR: AurelioB/N64ModernRuntime#1 — merge that first, then bump the submodule pointer here.

With both applied, on-device verification: all previously-crashing mod sets install and play (52 hooked pages, all rc=0, zero SIGSEGV).

Build notes (no code changes — for whoever builds the port from source)

  • SDL2 must be exactly 2.32.10: the vendored SDLActivity.java hard-checks the native version and shows a fatal "SDL C/Java version mismatch" otherwise.
  • lib/rt64's file_to_c host tool isn't cross-compile-aware — build a host-native copy and put it on PATH (same pattern as the existing ld.lld symlink).
  • The validateAndroidBuildEnvironment gate only checks that some clang/ld.lld is on PATH: a newer system clang works (needs -Wno-error=incompatible-pointer-types for patches/).
  • The private extra/ inputs repo is only needed for banjo.us.v10.decompressed.z64, which can be reproduced locally from the checksum-verified US 1.0 ROM by building lib/bk-decomp's decompressed.us.v10.z64 target.

Closes #15 (the crash itself; the browser-download quirk some users hit is separate).

On some OEM Android 16 builds (observed on ColorOS 16, Oppo Find N6)
PhoneWindow.getInsetsController() throws an internal NPE when called
from onCreate() before the DecorView is attached to the window,
crashing every cold launch of a freshly installed build:

    java.lang.NullPointerException
      at com.android.internal.policy.PhoneWindow.getInsetsController
      at BanjoSDLActivity.applyImmersiveFullscreen(BanjoSDLActivity.java:284)
      at BanjoSDLActivity.onCreate

Wrap the call in a NullPointerException guard: fullscreen is retried
from onResume()/onWindowFocusChanged() once the decor view exists, so
skipping here only defers it.
@Rival

Rival commented Aug 29, 2026

Copy link
Copy Markdown
Author

Heads-up on the red checks: they all fail in ~5s at the checkout step with

Refusing to check out fork pull request code from a 'pull_request_target' workflow

i.e. this is GitHub's fork-checkout guard inside validate-external.yml's trusted context, not anything in this PR — every external fork PR will hit the same wall until the actions/checkout steps in validate.yml opt in with allow-unsafe-pr-checkout: true (reasonable here since the external environment approval gate already gates fork PRs), or the workflow is restructured.

The change itself is a 10-line Java guard; happy to rebase/retest in any form you prefer.

@Rival Rival changed the title fix(android): guard applyImmersiveFullscreen() against ColorOS 16 DecorView NPE fix(android): cold-launch NPE guard + root cause & fix for the #15 mod crashes (runtime fix: N64ModernRuntime#1) Aug 29, 2026
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.

Certain mods make the game crash

1 participant