diff --git a/source/plugin/Assets/GoogleMobileAds/Common/DevInsightsEmitter.cs b/source/plugin/Assets/GoogleMobileAds/Common/DevInsightsEmitter.cs new file mode 100644 index 000000000..07e120d10 --- /dev/null +++ b/source/plugin/Assets/GoogleMobileAds/Common/DevInsightsEmitter.cs @@ -0,0 +1,65 @@ +using System; +using System.IO; +using System.Threading; +using UnityEngine; + +namespace GoogleMobileAds.Common +{ + // Emits insights when the SDK compiles in dev build mode. + // TODO: b/431227569 - We should add tracing to the Unity plugin, like for the Decagon SDK: + // http://google3/java/com/google/android/libraries/ads/mobile/sdk/internal/tracing/ExportingDebugTraceMonitor.kt. + public class InsightsEmitter : IInsightsEmitter + { + // LINT.IfChange + private const string _DEFAULT_INSIGHTS_FILE_NAME = "unity_insights.jsonl"; + // LINT.ThenChange(//depot/google3/javatests/com/google/android/apps/internal/admobsdk/mediumtest/unityplugin/UnityTestUtils.java) + + private readonly string _filePath; + private readonly bool _canWrite = true; + private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(); + + /** + * Creates a new InsightsEmitter. + * + * @param filePath The file path to write the insights to. If null, the file will be written + * to the external storage directory. + */ + public InsightsEmitter(string filePath = null) + { + _filePath = filePath ?? Path.Combine(Application.persistentDataPath, + _DEFAULT_INSIGHTS_FILE_NAME); + Debug.Log("Unity insights will be written to: " + _filePath); + + try + { + // No-op if the directory and subdirectories already exist. + Directory.CreateDirectory(Path.GetDirectoryName(_filePath)); + } catch (Exception e) { + Debug.LogError("Failed to create directory for Unity insights (no insights will be " + + "written): " + e.Message); + _canWrite = false; + } + } + + public void Emit(Insight insight) + { + if (!_canWrite) + { + return; + } + + _lock.EnterWriteLock(); + try + { + Debug.Log("Writing insight: " + insight.ToString()); + + // Writing needs to be synchronous for the read cursor to consume insights in order. + File.AppendAllText(_filePath, insight.ToJson() + Environment.NewLine); + } + finally + { + _lock.ExitWriteLock(); + } + } + } +} diff --git a/source/plugin/Assets/GoogleMobileAds/Common/IInsightsEmitter.cs b/source/plugin/Assets/GoogleMobileAds/Common/IInsightsEmitter.cs new file mode 100644 index 000000000..a8ce09ee0 --- /dev/null +++ b/source/plugin/Assets/GoogleMobileAds/Common/IInsightsEmitter.cs @@ -0,0 +1,9 @@ +using System; + +namespace GoogleMobileAds.Common +{ + public interface IInsightsEmitter + { + void Emit(Insight insight); + } +} diff --git a/source/plugin/Assets/GoogleMobileAds/Common/Insight.cs b/source/plugin/Assets/GoogleMobileAds/Common/Insight.cs new file mode 100644 index 000000000..e0971d931 --- /dev/null +++ b/source/plugin/Assets/GoogleMobileAds/Common/Insight.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using UnityEngine; + +// LINT.IfChange +namespace GoogleMobileAds.Common +{ + // Protos are not supported in g3 for C#: http://yaqs/6221611834537934848. Instead, we use + // JSON Lines (http://jsonlines.org) to serialize the insights. + [Serializable] + public class Insight + { + public Insight() + { + StartTimeMillis = (long)DateTime.UtcNow + .Subtract(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)) + .TotalMilliseconds; + } + + public enum CuiName + { + Unknown = 0, + SdkInitialized = 1, + AdRequested = 2, + AdLoaded = 3, + AdFailedToLoad = 4, + AdShown = 5, + AdClosed = 6, + AdClicked = 7, + } + + public enum AdFormat + { + Unknown = 0, + Banner = 1, + Interstitial = 2, + Rewarded = 3, + RewardedInterstitial = 4, + AppOpen = 5, + Native = 6, + } + + public enum AdPlatform + { + Unknown = 0, + Android = 1, + Ios = 2, + Unity = 3, + } + + // The name of the insight, commonly referred as a CUI (Critical User Interaction). + public CuiName Name; + + // If the event associated with the insight succeeded or failed. + public bool Success; + + // The Epoch time in milliseconds when the CUI started. + public long StartTimeMillis; + + // How long the operation took to complete in milliseconds. + public long LatencyMillis; + + // The GMA SDK version. + public string SdkVersion; + + // The AdMob / Google Ad Manager app id. + public string AppId; + + // The ad unit ID. + public string AdUnitId; + + // The format of the ad. + public AdFormat Format; + + // The platform on which the CUI was performed. + public AdPlatform Platform; + + // The keywords associated with the insight. They are used to group insights together for + // analysis. + public List Tags; + + // Any additional details about the insight. + public string Details; + + // Returns a string representation of the insight. + public override string ToString() + { + return string.Format( + "Insight[Name={0}, Success={1}, StartTimeMillis={2}, LatencyMillis={3}, " + + "SdkVersion='{4}', AppId='{5}', AdUnitId='{6}', Format={7}, Platform={8}, " + + "Tags='{9}', Details='{10}']", + Name, + Success, + StartTimeMillis, + LatencyMillis, + SdkVersion, + AppId, + AdUnitId, + Format, + Platform, + string.Join(",", Tags), + Details); + } + + // Returns a JSON string representation of the insight. + public string ToJson() + { + return JsonUtility.ToJson(this, prettyPrint: true); + } + } +} +// LINT.ThenChange(//depot/google3/javatests/com/google/android/apps/internal/admobsdk/mediumtest/unityplugin/Insight.java) diff --git a/source/plugin/Assets/GoogleMobileAds/Common/ProdInsightsEmitter.cs b/source/plugin/Assets/GoogleMobileAds/Common/ProdInsightsEmitter.cs new file mode 100644 index 000000000..b6df7fae7 --- /dev/null +++ b/source/plugin/Assets/GoogleMobileAds/Common/ProdInsightsEmitter.cs @@ -0,0 +1,11 @@ +using System; + +namespace GoogleMobileAds.Common +{ + public class InsightsEmitter : IInsightsEmitter + { + public void Emit(Insight insight) { + // Intentionally left blank. + } + } +} diff --git a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/MobileAdsClient.cs b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/MobileAdsClient.cs index c4cf7e603..14fabcf8a 100644 --- a/source/plugin/Assets/GoogleMobileAds/Platforms/Android/MobileAdsClient.cs +++ b/source/plugin/Assets/GoogleMobileAds/Platforms/Android/MobileAdsClient.cs @@ -27,6 +27,7 @@ public class MobileAdsClient : AndroidJavaProxy, IMobileAdsClient { private readonly static MobileAdsClient _instance = new MobileAdsClient(); private readonly AndroidJavaClass _mobileAdsClass; + private readonly IInsightsEmitter _insightsEmitter = new InsightsEmitter(); private Action _initCompleteAction; private MobileAdsClient() : base(Utils.OnInitializationCompleteListenerClassName) { @@ -52,6 +53,12 @@ public void Initialize(Action initCompleteAction) try { _mobileAdsClass.CallStatic("initialize", Utils.GetCurrentActivityAndroidJavaObject(), this); + _insightsEmitter.Emit(new Insight() + { + Name = Insight.CuiName.SdkInitialized, + Platform = Insight.AdPlatform.Android, + Success = true + }); } finally { AndroidJNI.DetachCurrentThread(); }