Skip to content

Commit b519f0c

Browse files
authored
Merge pull request #241 from winnerspiros/fix/android-surface-loss-crash-690584439121913596
Fix Android 'Swapchain lost' crash and mitigate log flooding
2 parents 223ed3f + 078611b commit b519f0c

2 files changed

Lines changed: 26 additions & 5 deletions

File tree

osu.Android/CrashDiagnostics.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
using Android.Content;
1010
using Debug = System.Diagnostics.Debug;
1111
using osu.Android.Native;
12+
using System.Collections.Concurrent;
1213

1314
namespace osu.Android
1415
{
@@ -38,6 +39,7 @@ internal static class CrashDiagnostics
3839

3940
private static string? internalDir;
4041
private static string? externalDir;
42+
private static readonly ConcurrentDictionary<string, int> exceptionCounts = new ConcurrentDictionary<string, int>();
4143
private static string? sentinelPath;
4244
private static string? installedLogPath;
4345
private static bool sentinelWritten;
@@ -285,6 +287,13 @@ public static void WriteInstallState()
285287

286288
private static void writeManagedException(string source, Exception? ex)
287289
{
290+
if (ex is EntryPointNotFoundException && ex.Message.Contains("CFStringCreateWithCharacters"))
291+
return;
292+
293+
string key = $"{source}_{ex?.GetType().Name}_{ex?.StackTrace?.GetHashCode() ?? 0}";
294+
if (exceptionCounts.AddOrUpdate(key, 1, (_, count) => count + 1) > 10)
295+
return;
296+
288297
try
289298
{
290299
string block =

osu.Android/OsuGameActivity.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace osu.Android
2727
// frame — the SurfaceView is sized correctly on creation and there is no orientation-change
2828
// event during startup. This is defensive hardening alongside the main fix in osu.Android.props
2929
// (disabling trimming + profiled AOT, which was the actual cause of the startup crash).
30-
[Activity(ResizeableActivity = true, ScreenOrientation = ScreenOrientation.SensorLandscape, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode | ConfigChanges.SmallestScreenSize | ConfigChanges.ScreenLayout | ConfigChanges.ColorMode | ConfigChanges.Density | ConfigChanges.Touchscreen | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.Navigation, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
30+
[Activity(ResizeableActivity = true, ScreenOrientation = ScreenOrientation.Landscape, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode | ConfigChanges.SmallestScreenSize | ConfigChanges.ScreenLayout | ConfigChanges.ColorMode | ConfigChanges.Density | ConfigChanges.Touchscreen | ConfigChanges.Keyboard | ConfigChanges.KeyboardHidden | ConfigChanges.Navigation, Exported = true, LaunchMode = DEFAULT_LAUNCH_MODE, MainLauncher = true)]
3131
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osz", DataHost = "*", DataMimeType = "*/*")]
3232
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osk", DataHost = "*", DataMimeType = "*/*")]
3333
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osr", DataHost = "*", DataMimeType = "*/*")]
@@ -83,6 +83,9 @@ public OsuGameActivity()
8383

8484
protected override void OnCreate(Bundle? savedInstanceState)
8585
{
86+
// Force orientation immediately to prevent unnecessary surface recreation on startup.
87+
RequestedOrientation = ScreenOrientation.Landscape;
88+
8689
// Crash diagnostics first. The native handler write target is internal storage
8790
// (FilesDir/native_crash.log); a one-shot mirror copies it to external storage
8891
// here on the *next* normal startup so the user can pull it without root.
@@ -166,7 +169,7 @@ protected override void OnCreate(Bundle? savedInstanceState)
166169
if (Resources?.Configuration != null)
167170
IsTablet = Resources.Configuration.SmallestScreenWidthDp >= 600;
168171

169-
// Phones: manifest already requests SensorLandscape; do not re-assign at runtime —
172+
// Phones: manifest already requests Landscape; do not re-assign at runtime —
170173
// a no-op assignment is harmless on most devices but a redundant RequestedOrientation
171174
// write can still nudge the SurfaceView into a recreate cycle on some OEMs while the
172175
// SDL draw thread is mid-Vulkan-init. Tablets get a more permissive policy applied
@@ -175,7 +178,7 @@ protected override void OnCreate(Bundle? savedInstanceState)
175178
if (IsTablet)
176179
RequestedOrientation = DefaultOrientation = ScreenOrientation.FullUser;
177180
else
178-
DefaultOrientation = ScreenOrientation.SensorLandscape;
181+
DefaultOrientation = ScreenOrientation.Landscape;
179182

180183
foreach (string asm in new[] { "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Mania" })
181184
{
@@ -397,13 +400,22 @@ public void SurfaceCreated(ISurfaceHolder holder)
397400
if (oldRef != IntPtr.Zero)
398401
global::Android.Runtime.JNIEnv.DeleteGlobalRef(oldRef);
399402

400-
surfaceEvent.Set();
401-
Debug.WriteLine("[osu!] Native surface JNI global reference created");
403+
Debug.WriteLine("[osu!] Native surface JNI global reference created (waiting for SurfaceChanged for signal)");
402404
}
403405
}
404406

405407
public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height)
406408
{
409+
if (width > 0 && height > 0)
410+
{
411+
surfaceEvent.Set();
412+
Debug.WriteLine($"[osu!] Native surface signal set (size: {width}x{height})");
413+
}
414+
else
415+
{
416+
surfaceEvent.Reset();
417+
Debug.WriteLine("[osu!] Native surface signal reset (invalid size)");
418+
}
407419
}
408420

409421
public void SurfaceDestroyed(ISurfaceHolder holder)

0 commit comments

Comments
 (0)