-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathAttributionContext.cs
More file actions
90 lines (82 loc) · 3.81 KB
/
Copy pathAttributionContext.cs
File metadata and controls
90 lines (82 loc) · 3.81 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#nullable enable
using System.Collections.Generic;
namespace Immutable.Audience.Unity.Mobile
{
// Builds the platform attribution snapshot that ships on game_launch when
// EnableMobileAttribution is true.
//
// iOS: ATT status is always read; IDFA is only included when status is
// authorized (Apple returns the all-zeros UUID otherwise, which the native
// bridge filters to null).
//
// Android: gaid + gaidLimitAdTracking are read from the GAIDBridge disk
// cache populated by the previous launch's background fetch (Google's
// AdvertisingIdClient is sync + must run off main thread, so first launch
// ships nothing — gaidLimitAdTracking shows up on launch #2 onwards).
internal static class AttributionContext
{
// Maps Apple's ATTrackingManagerAuthorizationStatus to the wire
// strings the analytics pipeline expects. Stable values shared with
// backend dashboards.
internal static string AttStatusToString(int status)
{
switch (status)
{
case 0: return "notDetermined";
case 1: return "restricted";
case 2: return "denied";
case 3: return "authorized";
default: return "unknown";
}
}
// persistentDataPath is required for Android (GAID disk cache); iOS
// ignores it. Returns a possibly-empty dict — never null — so callers
// can merge unconditionally.
internal static IReadOnlyDictionary<string, object> Capture(string? persistentDataPath = null)
{
var props = new Dictionary<string, object>();
#if UNITY_IOS || UNITY_EDITOR
// Compiled in on iOS device builds AND in the editor (any target)
// so AttributionContextTests can drive Capture() via the ATTBridge
// test seams. Excluded on real Android device builds so attStatus
// never ships there. Native ATTBridge calls are themselves gated
// by #if UNITY_IOS, so non-iOS editor targets get the safe stubs.
var status = ATTBridge.GetStatus();
props["attStatus"] = AttStatusToString(status);
// Only ship IDFA when the user has authorized tracking. The native
// bridge already returns null for the zero-UUID case, but gating
// here makes the contract explicit and survives a future native
// change.
if (status == 3)
{
var idfa = ATTBridge.GetIDFA();
if (!string.IsNullOrEmpty(idfa))
props["idfa"] = idfa!;
}
#endif
#if UNITY_ANDROID && !UNITY_EDITOR && AUDIENCE_MOBILE_ATTRIBUTION
// Gated on AUDIENCE_MOBILE_ATTRIBUTION so a build that disables
// GAID at compile time can't read a stale cache file written by
// a previous install where the define was on.
if (!string.IsNullOrEmpty(persistentDataPath))
{
var info = GAIDBridge.GetCached(persistentDataPath!);
if (info.HasValue)
EmitGaidProps(info.Value, props);
}
#endif
return props;
}
// Defensive emission gate: even if a stale cache from a pre-fix build
// retained a non-empty GAID under opt-out, this method never ships
// the raw identifier when LimitAdTracking is true. gaidLimitAdTracking
// always ships so the pipeline can distinguish "fetched, opted out"
// from "not fetched yet".
internal static void EmitGaidProps(GAIDInfo info, IDictionary<string, object> props)
{
if (!info.LimitAdTracking && !string.IsNullOrEmpty(info.Gaid))
props["gaid"] = info.Gaid;
props["gaidLimitAdTracking"] = info.LimitAdTracking;
}
}
}