diff --git a/osu.Android/CrashDiagnostics.cs b/osu.Android/CrashDiagnostics.cs index 4abf6f0f9111..3e8ce28d976b 100644 --- a/osu.Android/CrashDiagnostics.cs +++ b/osu.Android/CrashDiagnostics.cs @@ -9,6 +9,7 @@ using Android.Content; using Debug = System.Diagnostics.Debug; using osu.Android.Native; +using System.Collections.Concurrent; namespace osu.Android { @@ -38,6 +39,7 @@ internal static class CrashDiagnostics private static string? internalDir; private static string? externalDir; + private static readonly ConcurrentDictionary exceptionCounts = new ConcurrentDictionary(); private static string? sentinelPath; private static string? installedLogPath; private static bool sentinelWritten; @@ -285,6 +287,13 @@ public static void WriteInstallState() private static void writeManagedException(string source, Exception? ex) { + if (ex is EntryPointNotFoundException && ex.Message.Contains("CFStringCreateWithCharacters")) + return; + + string key = $"{source}_{ex?.GetType().Name}_{ex?.StackTrace?.GetHashCode() ?? 0}"; + if (exceptionCounts.AddOrUpdate(key, 1, (_, count) => count + 1) > 10) + return; + try { string block = diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs index 5b60f456dc80..2b11f820e99c 100644 --- a/osu.Android/OsuGameActivity.cs +++ b/osu.Android/OsuGameActivity.cs @@ -27,7 +27,7 @@ namespace osu.Android // frame — the SurfaceView is sized correctly on creation and there is no orientation-change // event during startup. This is defensive hardening alongside the main fix in osu.Android.props // (disabling trimming + profiled AOT, which was the actual cause of the startup crash). - [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)] + [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)] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osz", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osk", DataHost = "*", DataMimeType = "*/*")] [IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\.osr", DataHost = "*", DataMimeType = "*/*")] @@ -83,6 +83,9 @@ public OsuGameActivity() protected override void OnCreate(Bundle? savedInstanceState) { + // Force orientation immediately to prevent unnecessary surface recreation on startup. + RequestedOrientation = ScreenOrientation.Landscape; + // Crash diagnostics first. The native handler write target is internal storage // (FilesDir/native_crash.log); a one-shot mirror copies it to external storage // here on the *next* normal startup so the user can pull it without root. @@ -166,7 +169,7 @@ protected override void OnCreate(Bundle? savedInstanceState) if (Resources?.Configuration != null) IsTablet = Resources.Configuration.SmallestScreenWidthDp >= 600; - // Phones: manifest already requests SensorLandscape; do not re-assign at runtime — + // Phones: manifest already requests Landscape; do not re-assign at runtime — // a no-op assignment is harmless on most devices but a redundant RequestedOrientation // write can still nudge the SurfaceView into a recreate cycle on some OEMs while the // SDL draw thread is mid-Vulkan-init. Tablets get a more permissive policy applied @@ -175,7 +178,7 @@ protected override void OnCreate(Bundle? savedInstanceState) if (IsTablet) RequestedOrientation = DefaultOrientation = ScreenOrientation.FullUser; else - DefaultOrientation = ScreenOrientation.SensorLandscape; + DefaultOrientation = ScreenOrientation.Landscape; foreach (string asm in new[] { "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Mania" }) { @@ -397,13 +400,22 @@ public void SurfaceCreated(ISurfaceHolder holder) if (oldRef != IntPtr.Zero) global::Android.Runtime.JNIEnv.DeleteGlobalRef(oldRef); - surfaceEvent.Set(); - Debug.WriteLine("[osu!] Native surface JNI global reference created"); + Debug.WriteLine("[osu!] Native surface JNI global reference created (waiting for SurfaceChanged for signal)"); } } public void SurfaceChanged(ISurfaceHolder holder, global::Android.Graphics.Format format, int width, int height) { + if (width > 0 && height > 0) + { + surfaceEvent.Set(); + Debug.WriteLine($"[osu!] Native surface signal set (size: {width}x{height})"); + } + else + { + surfaceEvent.Reset(); + Debug.WriteLine("[osu!] Native surface signal reset (invalid size)"); + } } public void SurfaceDestroyed(ISurfaceHolder holder)