Skip to content

Fix Android startup crash: lock activity to SensorLandscape in manifest - #228

Merged
winnerspiros merged 3 commits into
masterfrom
copilot/fix-apk-crash-on-start-again
Apr 21, 2026
Merged

Fix Android startup crash: lock activity to SensorLandscape in manifest#228
winnerspiros merged 3 commits into
masterfrom
copilot/fix-apk-crash-on-start-again

Conversation

Copilot AI commented Apr 21, 2026

Copy link
Copy Markdown
  • Diagnose: fork enabled PublishTrimmed + TrimMode=partial + SuppressTrimAnalysisWarnings + RunAOTCompilation + AndroidEnableProfiledAot on top of upstream. Upstream ppy/osu ships with none of these. Trim- or profiled-AOT-induced TypeLoadException/MissingMethodException early in OsuGame construction crashes before the file logger is up → no osu.log, only a tombstone — exactly the "rotate → black → crash, no logs" pattern.
  • Disable PublishTrimmed, TrimMode, SuppressTrimAnalysisWarnings, RunAOTCompilation, AndroidEnableProfiledAot, AndroidStripILAfterAOT (match upstream)
  • Drop the now-unneeded TrimmerRootAssembly + Microsoft.CSharp items
  • Activity orientation hardening (manifest-declared, not THE fix but free hardening)
  • Harden Microsoft.Maui.ApplicationModel.Platform.Init with try/catch (workload-version drift is a real silent-crash path)
  • Harden the posted SurfaceHolder.AddCallback lambda (race with teardown)
  • Null-guard Host.DrawThread / Host.InputThread in the scheduled thread-affinity lambda, and wrap its body in try/catch (the outer try/catch only covered the Scheduler.Add enqueue, not the deferred lambda body; an NRE there would be unhandled on the update thread)
  • Keep AndroidEnableAssemblyCompression, DebugType=none, AndroidEnableMarshalMethods=false, the FixRuntimePackAssetTypes target, and the ppy.osu.Framework.NativeLibs ExcludeAssets="all" exclusion (all unrelated to the crash, all still needed)
  • PR Fix Android startup crash: lock activity to SensorLandscape in manifest #228 updated — merge to trigger the release workflow and produce a working APK

…untime orientation write on phones

The activity launched in the device's sensor orientation (often portrait on a phone),
then OnCreate assigned RequestedOrientation = SensorLandscape, which triggered an
immediate orientation change. The SurfaceView's underlying ANativeWindow was destroyed
and recreated while SDL's draw thread was concurrently initialising the Vulkan
swapchain → vkCreateAndroidSurfaceKHR raced on a stale ANativeWindow and the process
crashed a few seconds into startup with the user-visible pattern: rotation animation
→ black screen → crash.

Declaring ScreenOrientation in the [Activity] attribute bakes the orientation into the
manifest so Android creates the activity in landscape from the start. The runtime
re-assignment is now applied only on tablets (where FullUser is intentionally more
permissive than the manifest default); on phones it would be redundant and on some
OEMs can still nudge the SurfaceView into a brief recreate cycle.

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/03b83d23-052b-4cd1-9469-9b208e7669d5

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
Copilot AI and others added 2 commits April 21, 2026 21:46
…use of startup crash)

The fork's Release-only PropertyGroup enabled PublishTrimmed + TrimMode=partial +
RunAOTCompilation + AndroidEnableProfiledAot, with SuppressTrimAnalysisWarnings=true
masking the build-time signal. Only Microsoft.CSharp and ppy.Veldrid.SPIRV were rooted
via TrimmerRootAssembly — osu.Game, the rulesets, Realm, Newtonsoft.Json, AutoMapper,
Sentry, OsuTK and other reflection-heavy assemblies were not. A trimmed method/type or
an un-AOT'd profiled-AOT call site throws TypeLoadException / MissingMethodException
early in OsuGame construction, before the file logger is open; the process dies on the
SDLThread / .NET ThreadPool thread with only a tombstone, so the user gets no osu.log
and Logcat shows a bare native crash. Visually: splash → screen rotates into landscape
→ black for a few seconds → crash, exactly as reported.

Upstream ppy/osu's osu.Android.props enables NEITHER trimming NOR AOT — we now match
that baseline. The remaining release-only knobs (AndroidEnableAssemblyCompression,
DebugType=none, DebugSymbols=false) and the Microsoft.CSharp PackageReference / both
TrimmerRootAssembly entries are all removed since they only existed to support the
trim/AOT pipeline.

Untouched: AndroidEnableMarshalMethods=false (still needed), the
FixRuntimePackAssetTypes target (still needed for the Linux libbass.so leak), and the
ppy.osu.Framework.NativeLibs ExcludeAssets="all" exclusion (still needed for the same).
The earlier orientation-in-manifest tweak in OsuGameActivity.cs is kept as defensive
hardening but its comment is updated to be honest that it is not the actual fix.

Re-enabling trimming/AOT in the future requires: rooting every reflection-using
assembly explicitly, and running with SuppressTrimAnalysisWarnings=false to surface the
gaps before shipping.

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/dbc925d8-a57f-47c5-827b-5141f9eaa0d6

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
1. OsuGameActivity.OnCreate: wrap Microsoft.Maui.ApplicationModel.Platform.Init in
   try/catch. MAUI Essentials pulls in workload-version-sensitive initialisation
   that can throw TypeLoadException/MissingMethodException on the UI thread before
   the managed logger is up if the build-machine workload drifts from the device
   runtime — users would see only a native tombstone with no osu.log, matching
   the "rotate → black → crash" pattern.

2. OsuGameActivity.OnCreate: wrap the SurfaceHolder.AddCallback registration that
   is posted onto the UI thread message queue. A later race with activity teardown
   can make the Post'd lambda execute after the SurfaceView is gone, throwing an
   unhandled exception on the UI thread.

3. OsuGameAndroid.LoadComplete: the outer try/catch around the thread-affinity
   setup only covers the Scheduler.Add *call* (which enqueues the lambda), not the
   lambda body, which runs later on the update thread. Null-guard Host.DrawThread
   and Host.InputThread (via ?. chaining) and wrap the lambda body in its own
   try/catch so an NRE (if Host or its threads are null during a teardown race)
   can't escape as an unhandled update-thread exception and kill the framework.

All three are defensive — none of them replace the AOT/trimming fix from the
previous commit, but together they close three real "silent crash with no logs"
paths that the fork introduced on top of upstream.

Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/dbc925d8-a57f-47c5-827b-5141f9eaa0d6

Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
@winnerspiros
winnerspiros marked this pull request as ready for review April 21, 2026 21:57
Copilot AI review requested due to automatic review settings April 21, 2026 21:57
@winnerspiros
winnerspiros merged commit f9787ec into master Apr 21, 2026
14 of 18 checks passed
@gitar-bot

gitar-bot Bot commented Apr 21, 2026

Copy link
Copy Markdown

Important

You are using the Gitar free plan. Upgrade to unlock code review, CI analysis, auto-apply, custom automations, and more.

Gitar

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Disables Android Release trimming/profiled-AOT settings and adds Android startup hardening (manifest-declared landscape orientation + defensive exception guards) to prevent early, no-log startup crashes.

Changes:

  • Remove Release-time trimming/AOT configuration (and related rooting/package items) from osu.Android.props.
  • Declare ScreenOrientation=SensorLandscape in OsuGameActivity manifest attribute and avoid redundant runtime orientation writes on phones.
  • Add defensive try/catch + null-guards around MAUI Platform.Init, surface callback registration, and deferred thread-affinity scheduling.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
osu.Android/OsuGameAndroid.cs Hardens deferred scheduling for render/input thread affinity pinning to avoid update-thread unhandled exceptions.
osu.Android/OsuGameActivity.cs Locks initial orientation via manifest attribute; adds defensive guards around MAUI init and SurfaceHolder callback registration.
osu.Android.props Removes Release trimming/profiled-AOT configuration and related rooting, keeping only safe release packaging knobs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +230 to 233
try
{
try
Host?.DrawThread?.Scheduler.Add(() =>
{
// NRE from Host.DrawThread/Host.InputThread being null (or a Host
// teardown race during startup) can't escape as an unhandled update-
// thread exception and kill the framework.
Debug.WriteLine($"[osu!] Failed to enqueue thread-affinity pinning for render/input threads: {e.Message}");
}
catch (Exception e)
{
Debug.WriteLine($"[osu!] MAUI Platform.Init failed (non-fatal): {e.Message}");
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.

3 participants