Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions osu.Android/CrashDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Android.Content;
using Debug = System.Diagnostics.Debug;
using osu.Android.Native;
using System.Collections.Concurrent;

namespace osu.Android
{
Expand Down Expand Up @@ -38,6 +39,7 @@ internal static class CrashDiagnostics

private static string? internalDir;
private static string? externalDir;
private static readonly ConcurrentDictionary<string, int> exceptionCounts = new ConcurrentDictionary<string, int>();
private static string? sentinelPath;
private static string? installedLogPath;
private static bool sentinelWritten;
Expand Down Expand Up @@ -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 =
Expand Down
22 changes: 17 additions & 5 deletions osu.Android/OsuGameActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "*/*")]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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" })
{
Expand Down Expand Up @@ -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)
Expand Down
Loading