Skip to content

Commit 01a7b47

Browse files
Fix Android 16 startup crashes related to early system access
1 parent 551fab8 commit 01a7b47

2 files changed

Lines changed: 62 additions & 25 deletions

File tree

osu.Android/OsuGameActivity.cs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public class OsuGameActivity : AndroidGameActivity
5151

5252
public new bool IsTablet { get; private set; }
5353

54-
private readonly OsuGameAndroid game;
54+
private OsuGameAndroid? game;
5555

5656
private bool gameCreated;
5757

@@ -60,19 +60,23 @@ protected override Framework.Game CreateGame()
6060
if (gameCreated)
6161
throw new InvalidOperationException("Framework tried to create a game twice.");
6262

63+
if (game == null)
64+
throw new InvalidOperationException("Game was not initialised in OnCreate.");
65+
6366
gameCreated = true;
6467
return game;
6568
}
6669

6770
public OsuGameActivity()
6871
{
69-
game = new OsuGameAndroid(this);
7072
}
7173

7274
protected override void OnCreate(Bundle? savedInstanceState)
7375
{
7476
base.OnCreate(savedInstanceState);
7577

78+
game = new OsuGameAndroid(this);
79+
7680
// Initialise MAUI Essentials so that Battery, Connectivity and other platform
7781
// APIs can resolve the current Activity/context. Without this call the
7882
// BroadcastReceivers registered in the merged manifest (BatteryBroadcastReceiver,
@@ -145,7 +149,7 @@ private void handleIntent(Intent? intent)
145149
else if (osu_url_schemes.Contains(intent.Scheme))
146150
{
147151
if (intent.DataString != null)
148-
game.HandleLink(intent.DataString);
152+
game?.HandleLink(intent.DataString);
149153
}
150154

151155
break;
@@ -188,7 +192,7 @@ await Task.WhenAll(uris.Select(async uri =>
188192
}
189193
})).ConfigureAwait(false);
190194

191-
await game.Import(tasks.ToArray()).ConfigureAwait(false);
195+
if (game != null) await game.Import(tasks.ToArray()).ConfigureAwait(false);
192196
}, TaskCreationOptions.LongRunning);
193197
}
194198
}

osu.Android/OsuGameAndroid.cs

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,29 @@ public partial class OsuGameAndroid : OsuGame
2727
[Cached]
2828
private readonly OsuGameActivity gameActivity;
2929

30-
private readonly PackageInfo? packageInfo;
30+
private PackageInfo? packageInfo;
31+
private bool packageInfoChecked;
32+
33+
private PackageInfo? getPackageInfo()
34+
{
35+
if (packageInfoChecked)
36+
return packageInfo;
37+
38+
try
39+
{
40+
packageInfo = gameActivity.PackageManager?.GetPackageInfo(gameActivity.PackageName!, 0);
41+
}
42+
catch (Exception e)
43+
{
44+
Debug.WriteLine($"[osu!] Failed to retrieve package info: {e.Message}");
45+
}
46+
finally
47+
{
48+
packageInfoChecked = true;
49+
}
50+
51+
return packageInfo;
52+
}
3153

3254
public override Vector2 ScalingContainerTargetDrawSize => DrawWidth > 0 && DrawHeight > 0
3355
? new Vector2(1024, 1024 * DrawHeight / DrawWidth)
@@ -53,16 +75,6 @@ public OsuGameAndroid(OsuGameActivity activity)
5375
: base(null)
5476
{
5577
gameActivity = activity;
56-
57-
try
58-
{
59-
packageInfo = Application.Context.ApplicationContext!.PackageManager!.GetPackageInfo(Application.Context.ApplicationContext.PackageName!, 0);
60-
}
61-
catch (Exception e)
62-
{
63-
Debug.WriteLine($"[osu!] Failed to retrieve package info: {e.Message}");
64-
packageInfo = null;
65-
}
6678
}
6779

6880
public override string Version
@@ -72,7 +84,7 @@ public override string Version
7284
if (!IsDeployedBuild)
7385
return @"local " + (DebugUtils.IsDebugBuild ? @"debug" : @"release");
7486

75-
return packageInfo?.VersionName ?? @"unknown";
87+
return getPackageInfo()?.VersionName ?? @"unknown";
7688
}
7789
}
7890

@@ -82,7 +94,7 @@ public override Version AssemblyVersion
8294
{
8395
try
8496
{
85-
string? versionName = packageInfo?.VersionName;
97+
string? versionName = getPackageInfo()?.VersionName;
8698

8799
if (!string.IsNullOrEmpty(versionName))
88100
return new Version(versionName.Split('-').First());
@@ -209,9 +221,17 @@ private void selectHighestRefreshRate()
209221
{
210222
try
211223
{
212-
var display = gameActivity.WindowManager?.DefaultDisplay;
224+
if (gameActivity.IsFinishing || gameActivity.IsDestroyed)
225+
return;
226+
227+
var window = gameActivity.Window;
228+
var windowManager = gameActivity.WindowManager;
229+
230+
if (window == null || windowManager == null)
231+
return;
213232

214-
if (display == null || gameActivity.Window == null)
233+
var display = windowManager.DefaultDisplay;
234+
if (display == null)
215235
return;
216236

217237
#pragma warning disable CA1422
@@ -222,17 +242,30 @@ private void selectHighestRefreshRate()
222242
return;
223243

224244
var preferred = modes.OrderByDescending(m => m.RefreshRate).First();
225-
var layoutParams = gameActivity.Window.Attributes;
226245

227-
if (layoutParams != null)
246+
gameActivity.RunOnUiThread(() =>
228247
{
229-
layoutParams.PreferredDisplayModeId = preferred.ModeId;
230-
gameActivity.Window.Attributes = layoutParams;
231-
}
248+
try
249+
{
250+
if (window.Attributes is WindowManagerLayoutParams layoutParams)
251+
{
252+
layoutParams.PreferredDisplayModeId = preferred.ModeId;
253+
window.Attributes = layoutParams;
254+
Debug.WriteLine($"[osu!] Highest refresh rate selected: {preferred.RefreshRate}Hz (mode {preferred.ModeId})");
255+
}
256+
}
257+
catch (Exception e)
258+
{
259+
// On some devices (e.g. Samsung S23 on Android 16), accessing display properties
260+
// via the vendor property 'vendor.display.enable_optimal_refresh_rate' can trigger
261+
// SELinux denials or crashes if the window is not yet fully trusted.
262+
Debug.WriteLine($"[osu!] Failed to apply preferred display mode: {e.Message}");
263+
}
264+
});
232265
}
233266
catch (Exception e)
234267
{
235-
Debug.WriteLine($"[osu!] Failed to select highest refresh rate: {e.Message}");
268+
Debug.WriteLine($"[osu!] Failed to query supported display modes: {e.Message}");
236269
}
237270
}
238271

0 commit comments

Comments
 (0)