Skip to content

Commit 80a073c

Browse files
committed
fix(audience): isolate Install() collector failures so one doesn't disable the other
CollectGameLaunchProperties() and CollectContext() ran unguarded back to back in Install(), so an exception in either (e.g. a Screen.currentResolution read too early in boot) left both providers unassigned for the rest of the session, silently stripping context and game_launch device data from every event. Each collector is now isolated in its own try/catch, with the failure logged and the provider still assigned (with whatever partial data survives).
1 parent 1e6fb1a commit 80a073c

3 files changed

Lines changed: 60 additions & 16 deletions

File tree

src/Packages/Audience/Runtime/Unity/AudienceUnityHooks.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22

3+
using System;
34
using System.Collections.Generic;
45
using System.Collections.ObjectModel;
56
using Immutable.Audience.Unity.Mobile;
@@ -31,12 +32,33 @@ private static void Install()
3132
_persistentDataPath = Application.persistentDataPath;
3233
ImmutableAudience.DefaultPersistentDataPathProvider = () => Application.persistentDataPath;
3334

35+
// Set before the collectors below run so a collector failure logs via Debug.Log, not Console.WriteLine.
36+
if (Log.Writer == null) Log.Writer = Debug.Log;
37+
3438
// Captured once on main thread; ReadOnlyDictionary blocks downstream mutation.
35-
IReadOnlyDictionary<string, object> launchProps =
36-
new ReadOnlyDictionary<string, object>(DeviceCollector.CollectGameLaunchProperties());
37-
IReadOnlyDictionary<string, object> contextProps =
38-
new ReadOnlyDictionary<string, object>(DeviceCollector.CollectContext());
39+
// Each collector is isolated so one throwing can't block the other's provider or abort Install().
40+
IReadOnlyDictionary<string, object> launchProps;
41+
try
42+
{
43+
launchProps = new ReadOnlyDictionary<string, object>(DeviceCollector.CollectGameLaunchProperties());
44+
}
45+
catch (Exception ex)
46+
{
47+
Log.Warn(AudienceLogs.LaunchPropertiesCollectionFailed(ex));
48+
launchProps = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
49+
}
3950
ImmutableAudience.LaunchContextProvider = () => launchProps;
51+
52+
IReadOnlyDictionary<string, object> contextProps;
53+
try
54+
{
55+
contextProps = new ReadOnlyDictionary<string, object>(DeviceCollector.CollectContext());
56+
}
57+
catch (Exception ex)
58+
{
59+
Log.Warn(AudienceLogs.ContextCollectionFailed(ex));
60+
contextProps = new ReadOnlyDictionary<string, object>(new Dictionary<string, object>());
61+
}
4062
ImmutableAudience.ContextProvider = () => contextProps;
4163

4264
#if UNITY_IOS && !UNITY_EDITOR && AUDIENCE_MOBILE_ATTRIBUTION
@@ -58,8 +80,6 @@ private static void Install()
5880
#endif
5981

6082
UnityLifecycleBridge.EnsureExists();
61-
62-
if (Log.Writer == null) Log.Writer = Debug.Log;
6383
}
6484

6585
// Warms the install referrer cache for the next launch and returns

src/Packages/Audience/Runtime/Unity/DeviceCollector.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,28 @@ internal static Dictionary<string, object> CollectContext()
3232

3333
private static string? TryResolveScreenString()
3434
{
35-
var resolution = Screen.currentResolution;
36-
int width = resolution.width;
37-
int height = resolution.height;
38-
39-
if (width <= 0 || height <= 0)
35+
// Can throw before a window/graphics device exists this early in boot;
36+
// isolated so a failure here doesn't cost CollectContext()'s other fields.
37+
try
4038
{
41-
width = Screen.width;
42-
height = Screen.height;
39+
var resolution = Screen.currentResolution;
40+
int width = resolution.width;
41+
int height = resolution.height;
42+
43+
if (width <= 0 || height <= 0)
44+
{
45+
width = Screen.width;
46+
height = Screen.height;
47+
}
48+
49+
if (width <= 0 || height <= 0) return null;
50+
return $"{width}x{height}";
51+
}
52+
catch (Exception ex)
53+
{
54+
Log.Warn(AudienceLogs.ScreenResolutionReadFailed(ex));
55+
return null;
4356
}
44-
45-
if (width <= 0 || height <= 0) return null;
46-
return $"{width}x{height}";
4757
}
4858

4959
internal static Dictionary<string, object> CollectGameLaunchProperties(

src/Packages/Audience/Runtime/Utility/Log.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,20 @@ internal static string IdentityLoadOrGenerateFailed(Exception ex) =>
168168
$"Identity file read/write failed. {ex.GetType().Name}: {ex.Message}. " +
169169
"Events will ship without identity fields this session.";
170170

171+
// ---- Install-time device/context collection ----
172+
173+
internal static string LaunchPropertiesCollectionFailed(Exception ex) =>
174+
$"CollectGameLaunchProperties threw {ex.GetType().Name}: {ex.Message} during Install(). " +
175+
"game_launch will ship without auto-detected Unity context this session.";
176+
177+
internal static string ContextCollectionFailed(Exception ex) =>
178+
$"CollectContext threw {ex.GetType().Name}: {ex.Message} during Install(). " +
179+
"All events will ship without locale/screen/timezone/userAgent this session.";
180+
181+
internal static string ScreenResolutionReadFailed(Exception ex) =>
182+
$"Screen resolution read threw {ex.GetType().Name}: {ex.Message}. " +
183+
"Continuing without a screen field.";
184+
171185
// ---- Steam auto-detection ----
172186

173187
internal static string SteamPlatformDetectionFailed(Exception ex) =>

0 commit comments

Comments
 (0)