using System; using System.Threading.Tasks; using Unity.Services.Analytics.Data; using Unity.Services.Analytics.Internal; using Unity.Services.Analytics.Platform; using Unity.Services.Authentication.Internal; using Unity.Services.Core.Device.Internal; using UnityEngine; using Event = Unity.Services.Analytics.Internal.Event; namespace Unity.Services.Analytics { partial class AnalyticsServiceInstance : IAnalyticsService { public string PrivacyUrl => "https://unity3d.com/legal/privacy-policy"; const string k_CollectUrlPattern = "https://collect.analytics.unity3d.com/api/analytics/collect/v1/projects/{0}/environments/{1}"; const string k_ForgetCallingId = "com.unity.services.analytics.Events." + nameof(OptOut); internal IPlayerId PlayerId { get; private set; } internal IInstallationId InstallId { get; private set; } internal string CustomAnalyticsId { get; private set; } internal IBuffer dataBuffer = new Internal.Buffer(); internal IDataGenerator dataGenerator = new DataGenerator(); internal IDispatcher dataDispatcher { get; set; } string m_CollectURL; readonly string m_SessionID; readonly StdCommonParams m_CommonParams = new StdCommonParams(); readonly string m_StartUpCallingId = "com.unity.services.analytics.Events.Startup"; internal IIDeviceIdentifiersInternal deviceIdentifiersInternal = new DeviceIdentifiersInternal(); internal bool ServiceEnabled { get; private set; } = true; internal AnalyticsServiceInstance() { // The docs say nothing about Application.cloudProjectId being guaranteed or not, // we add a check just to be sure. if (string.IsNullOrEmpty(Application.cloudProjectId)) { Debug.LogError("No Cloud ProjectID Found for Analytics"); return; } dataDispatcher = new Dispatcher(dataBuffer, new WebRequestHelper(), ConsentTracker); m_SessionID = Guid.NewGuid().ToString(); m_CommonParams.ClientVersion = Application.version; m_CommonParams.ProjectID = Application.cloudProjectId; m_CommonParams.GameBundleID = Application.identifier; m_CommonParams.Platform = Runtime.Name(); m_CommonParams.BuildGuuid = Application.buildGUID; m_CommonParams.Idfv = deviceIdentifiersInternal.Idfv; } public void Flush() { if (!ServiceEnabled) { return; } if (string.IsNullOrEmpty(Application.cloudProjectId)) { return; } if (InstallId == null) { #if UNITY_ANALYTICS_DEVELOPMENT Debug.Log("The Core callback hasn't yet triggered."); #endif return; } if (ConsentTracker.IsGeoIpChecked() && ConsentTracker.IsConsentGiven()) { dataBuffer.InstallID = InstallId.GetOrCreateIdentifier(); dataBuffer.PlayerID = PlayerId?.PlayerId; dataBuffer.UserID = !string.IsNullOrEmpty(CustomAnalyticsId) ? CustomAnalyticsId : dataBuffer.InstallID; dataBuffer.SessionID = m_SessionID; dataDispatcher.CollectUrl = m_CollectURL; dataDispatcher.Flush(); } if (ConsentTracker.IsOptingOutInProgress()) { analyticsForgetter.AttemptToForget(); } } public void RecordInternalEvent(Event eventToRecord) { if (!ServiceEnabled) { return; } dataBuffer.PushEvent(eventToRecord); } internal void SetDependencies(IInstallationId installId, IPlayerId playerId, string environment, string customAnalyticsId) { InstallId = installId; PlayerId = playerId; CustomAnalyticsId = customAnalyticsId; m_CollectURL = string.Format(k_CollectUrlPattern, Application.cloudProjectId, environment.ToLowerInvariant()); } internal async Task Initialize(IInstallationId installId, IPlayerId playerId, string environment, string customAnalyticsId) { SetDependencies(installId, playerId, environment, customAnalyticsId); if (!ServiceEnabled) { return; } await InitializeUser(); } async Task InitializeUser() { SetVariableCommonParams(); #if UNITY_ANALYTICS_DEVELOPMENT Debug.LogFormat("UA2 Setup\nCollectURL:{0}\nSessionID:{1}", m_CollectURL, m_SessionID); #endif try { await ConsentTracker.CheckGeoIP(); if (ConsentTracker.IsGeoIpChecked() && (ConsentTracker.IsConsentDenied() || ConsentTracker.IsOptingOutInProgress())) { OptOut(); } } #if UNITY_ANALYTICS_EVENT_LOGS catch (ConsentCheckException e) { Debug.Log("Initial GeoIP lookup fail: " + e.Message); } #else catch (ConsentCheckException) {} #endif } /// /// Sets up the internals of the Analytics SDK, including the regular sending of events and assigning /// the userID to be used in further event recording. /// internal void Startup() { if (!ServiceEnabled) { return; } // Startup Events. dataGenerator.SdkStartup(ref dataBuffer, DateTime.Now, m_CommonParams, m_StartUpCallingId); dataGenerator.ClientDevice(ref dataBuffer, DateTime.Now, m_CommonParams, m_StartUpCallingId, SystemInfo.processorType, SystemInfo.graphicsDeviceName, SystemInfo.processorCount, SystemInfo.systemMemorySize, Screen.width, Screen.height, (int)Screen.dpi); #if UNITY_DOTSRUNTIME var isTiny = true; #else var isTiny = false; #endif dataGenerator.GameStarted(ref dataBuffer, DateTime.Now, m_CommonParams, m_StartUpCallingId, Application.buildGUID, SystemInfo.operatingSystem, isTiny, DebugDevice.IsDebugDevice(), Locale.AnalyticsRegionLanguageCode()); } internal void NewPlayerEvent() { if (!ServiceEnabled) { return; } if (InstallId != null && new InternalNewPlayerHelper(InstallId).IsNewPlayer()) { dataGenerator.NewPlayer(ref dataBuffer, DateTime.Now, m_CommonParams, m_StartUpCallingId, SystemInfo.deviceModel); } } /// /// Records the gameEnded event, and flushes the buffer to upload all events. /// internal void GameEnded() { if (!ServiceEnabled) { return; } dataGenerator.GameEnded(ref dataBuffer, DateTime.Now, m_CommonParams, "com.unity.services.analytics.Events.Shutdown", DataGenerator.SessionEndState.QUIT); if (ConsentTracker.IsGeoIpChecked()) { Flush(); } } // // Internal tick is called by the Heartbeat at set intervals. // internal void InternalTick() { if (!ServiceEnabled) { return; } SetVariableCommonParams(); dataGenerator.GameRunning(ref dataBuffer, DateTime.Now, m_CommonParams, "com.unity.services.analytics.Events.InternalTick"); if (ConsentTracker.IsGeoIpChecked()) { Flush(); } } void SetVariableCommonParams() { m_CommonParams.Idfv = deviceIdentifiersInternal.Idfv; m_CommonParams.DeviceVolume = DeviceVolumeProvider.GetDeviceVolume(); m_CommonParams.BatteryLoad = SystemInfo.batteryLevel; m_CommonParams.UasUserID = PlayerId?.PlayerId; } void GameEnded(DataGenerator.SessionEndState quitState) { if (!ServiceEnabled) { return; } dataGenerator.GameEnded(ref dataBuffer, DateTime.Now, m_CommonParams, "com.unity.services.analytics.Events.GameEnded", quitState); } public async Task SetAnalyticsEnabled(bool enabled) { if (enabled && !ServiceEnabled) { dataBuffer = new Internal.Buffer(); dataDispatcher = new Dispatcher(dataBuffer, new WebRequestHelper(), ConsentTracker); await InitializeUser(); ServiceEnabled = true; } else if (!enabled && ServiceEnabled) { dataBuffer.ClearBuffer(); dataBuffer.ClearDiskCache(); dataBuffer = new BufferRevoked(); ServiceEnabled = false; } } } }