Exemple #1
0
        private async Task DeleteLogsOnSuspendedAsync()
        {
            await _mutex.LockAsync().ConfigureAwait(false);

            var stateSnapshot = _stateKeeper.GetStateSnapshot();

            try
            {
                if (SendingLog != null || FailedToSendLog != null)
                {
                    await SignalDeletingLogs(stateSnapshot).ConfigureAwait(false);
                }
            }
            catch (StorageException)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "Failed to invoke events for logs being deleted.");
                return;
            }
            catch (StatefulMutexException e)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The DeleteLogs operation has been cancelled", e);
                return;
            }
            finally
            {
                _mutex.Unlock();
            }
            await _storage.DeleteLogsAsync(Name).ConfigureAwait(false);
        }
Exemple #2
0
        private async Task CountFromDiskAsync()
        {
            await _mutex.LockAsync().ConfigureAwait(false);

            var stateSnapshot = _stateKeeper.GetStateSnapshot();

            _mutex.Unlock();
            var logCount = await _storage.CountLogsAsync(Name).ConfigureAwait(false);

            try
            {
                await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false);

                _pendingLogCount = logCount;
                CheckPendingLogs();
            }
            catch (StatefulMutexException e)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The CountFromDisk operation has been cancelled", e);
            }
            finally
            {
                _mutex.Unlock();
            }
        }
Exemple #3
0
        internal static Dictionary <long, Guid> SessionsFromString(string sessionsString)
        {
            var sessionsDict = new Dictionary <long, Guid>();

            if (sessionsString == null)
            {
                return(sessionsDict);
            }
            var sessions = sessionsString.Split(StorageEntrySeparator);

            foreach (var sessionString in sessions)
            {
                var splitSession = sessionString.Split(StorageKeyValueSeparator);
                try
                {
                    var time = long.Parse(splitSession[0]);
                    var sid  = Guid.Parse(splitSession[1]);
                    sessionsDict.Add(time, sid);
                }
                catch (FormatException e) //TODO other exceptions?
                {
                    MobileCenterLog.Warn(Analytics.Instance.LogTag, $"Ignore invalid session in store: {sessionString}", e);
                }
            }
            return(sessionsDict);
        }
Exemple #4
0
        public Task Enqueue(Log log)
        {
            _mutex.Lock();
            var stateSnapshot = _stateKeeper.GetStateSnapshot();

            try
            {
                if (_discardLogs)
                {
                    MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is disabled; logs are discarded");
                    _mutex.Unlock();
                    SendingLog?.Invoke(this, new SendingLogEventArgs(log));
                    FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException()));
                    return(null);
                }
                _mutex.Unlock();
                EnqueuingLog?.Invoke(this, new EnqueuingLogEventArgs(log));
                _mutex.Lock(stateSnapshot);
                return(Task.Run(async() =>
                {
                    await PrepareLogAsync(log).ConfigureAwait(false);
                    await PersistLogAsync(log, stateSnapshot).ConfigureAwait(false);
                }));
            }
            catch (StatefulMutexException e)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Enqueue operation has been cancelled", e);
                return(null);
            }
            finally
            {
                _mutex.Unlock();
            }
        }
        public override NSArray AttachmentsWithCrashes(MSCrashes crashes, MSErrorReport msReport)
        {
            if (_owner.GetErrorAttachments == null)
            {
                return(null);
            }

            var report      = ErrorReportCache.GetErrorReport(msReport);
            var attachments = _owner.GetErrorAttachments(report);

            if (attachments != null)
            {
                var nsArray = new NSMutableArray();
                foreach (var attachment in attachments)
                {
                    if (attachment != null)
                    {
                        nsArray.Add(attachment.internalAttachment);
                    }
                    else
                    {
                        MobileCenterLog.Warn(Crashes.LogTag, "Skipping null ErrorAttachmentLog in Crashes.GetErrorAttachments.");
                    }
                }
                return(nsArray);
            }

            return(null);
        }
Exemple #6
0
        private async Task PersistLogAsync(Log log, State stateSnapshot)
        {
            try
            {
                await _storage.PutLogAsync(Name, log).ConfigureAwait(false);
            }
            catch (StorageException e)
            {
                MobileCenterLog.Error(MobileCenterLog.LogTag, "Error persisting log", e);
                return;
            }
            try
            {
                await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false);

                _pendingLogCount++;
                if (_enabled)
                {
                    CheckPendingLogs();
                    return;
                }
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is temporarily disabled; log was saved to disk");
            }
            catch (StatefulMutexException e)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The PersistLog operation has been cancelled", e);
            }
            finally
            {
                _mutex.Unlock();
            }
        }
 private void HandleSendingSuccess(State state, string batchId)
 {
     if (!_mutex.IsCurrent(state))
     {
         return;
     }
     try
     {
         _storage.DeleteLogs(Name, batchId);
     }
     catch (StorageException e)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, $"Could not delete logs for batch {batchId}", e);
         throw;
     }
     finally
     {
         List <Log> removedLogs;
         using (_mutex.GetLock(state))
         {
             removedLogs = _sendingBatches[batchId];
             _sendingBatches.Remove(batchId);
         }
         if (SentLog != null)
         {
             foreach (var log in removedLogs)
             {
                 SentLog?.Invoke(this, new SentLogEventArgs(log));
             }
         }
     }
 }
Exemple #8
0
        public static void Log(string tag, string message, Exception exception = null, MobileCenterLogType type = MobileCenterLogType.Warn)
        {
            switch (type)
            {
            case MobileCenterLogType.Info:
                MobileCenterLog.Info(tag, message, exception);
                break;

            case MobileCenterLogType.Warn:
                MobileCenterLog.Warn(tag, message, exception);
                break;

            case MobileCenterLogType.Error:
                MobileCenterLog.Error(tag, message, exception);
                break;

            case MobileCenterLogType.Assert:
                MobileCenterLog.Assert(tag, message, exception);
                break;

            case MobileCenterLogType.Verbose:
                MobileCenterLog.Verbose(tag, message, exception);
                break;

            case MobileCenterLogType.Debug:
                MobileCenterLog.Debug(tag, message, exception);
                break;

            default:
                throw new Exception("MobileCenterLogType Does Not Exist");
            }
        }
Exemple #9
0
        public void OnChannelGroupReady(IChannelGroup channelGroup, string appSecret)
        {
            MobileCenterLog.Warn(MobileCenterLog.LogTag, "Crashes service is not yet supported on this platform.");
            try
            {
#if REFERENCE
#else
                WatsonRegistrationManager.Start(appSecret);
#pragma warning disable CS0612 // Type or member is obsolete
                MobileCenter.CorrelationIdChanged += (s, id) =>
                {
                    WatsonRegistrationManager.SetCorrelationId(id.ToString());
                };

                // Checking for null and setting id needs to be atomic to avoid
                // overwriting
                Guid newId = Guid.NewGuid();
                MobileCenter.TestAndSetCorrelationId(Guid.Empty, ref newId);
#pragma warning restore CS0612 // Type or member is obsolete
#endif
            }
            catch (Exception e)
            {
#if DEBUG
                throw new MobileCenterException("Failed to register crashes with Watson", e);
#endif
            }
        }
 private async Task PersistLogAsync(Log log, State state)
 {
     try
     {
         await _storage.PutLog(Name, log).ConfigureAwait(false);
     }
     catch (StorageException e)
     {
         MobileCenterLog.Error(MobileCenterLog.LogTag, "Error persisting log", e);
         return;
     }
     try
     {
         bool enabled;
         using (await _mutex.GetLockAsync(state).ConfigureAwait(false))
         {
             _pendingLogCount++;
             enabled = _enabled;
         }
         if (enabled)
         {
             CheckPendingLogs(state);
             return;
         }
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is temporarily disabled; log was saved to disk");
     }
     catch (StatefulMutexException)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "The PersistLog operation has been cancelled");
     }
 }
 public async Task EnqueueAsync(Log log)
 {
     try
     {
         State state;
         bool  discardLogs;
         using (await _mutex.GetLockAsync().ConfigureAwait(false))
         {
             state       = _mutex.State;
             discardLogs = _discardLogs;
         }
         if (discardLogs)
         {
             MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is disabled; logs are discarded");
             SendingLog?.Invoke(this, new SendingLogEventArgs(log));
             FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException()));
         }
         EnqueuingLog?.Invoke(this, new EnqueuingLogEventArgs(log));
         await PrepareLogAsync(log, state).ConfigureAwait(false);
         await PersistLogAsync(log, state).ConfigureAwait(false);
     }
     catch (StatefulMutexException)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Enqueue operation has been cancelled");
     }
 }
Exemple #12
0
        ///<exception cref="IngestionException"/>
        private async Task ExecuteAsyncHelper()
        {
            while (true)
            {
                try
                {
                    await Ingestion.ExecuteCallAsync(this).ConfigureAwait(false);

                    return;
                }
                catch (IngestionException e)
                {
                    if (!e.IsRecoverable || _retryCount >= _retryIntervals.Length)
                    {
                        throw;
                    }
                    MobileCenterLog.Warn(MobileCenterLog.LogTag, "Failed to execute service call", e);
                }
                await _retryIntervals[_retryCount++]().ConfigureAwait(false);
                if (_tokenSource.Token.IsCancellationRequested)
                {
                    throw new IngestionException("The operation has been cancelled");
                }
            }
        }
        private static Func <Task> GetDelayFunc(TimeSpan[] intervals, int retry)
        {
            return(async() =>
            {
                var delayMilliseconds = (int)(intervals[retry].TotalMilliseconds / 2.0);
                delayMilliseconds += await GetRandomIntAsync(delayMilliseconds).ConfigureAwait(false);

                var message = $"Try #{retry} failed and will be retried in {delayMilliseconds} ms";
                MobileCenterLog.Warn(MobileCenterLog.LogTag, message);
                await Task.Delay(delayMilliseconds).ConfigureAwait(false);
            });
        }
Exemple #14
0
        /// <summary>
        /// If enabled, register push channel and send URI to backend.
        /// Also start intercepting pushes.
        /// If disabled and previously enabled, stop listening for pushes (they will still be received though).
        /// </summary>
        private void ApplyEnabledState(bool enabled)
        {
            if (enabled)
            {
                // We expect caller of this method to lock on _mutex, we can't do it here as that lock is not recursive
                MobileCenterLog.Debug(LogTag, "Getting push token...");
                var state = _mutex.State;
                Task.Run(async() =>
                {
                    var channel = await new WindowsPushNotificationChannelManager().CreatePushNotificationChannelForApplicationAsync()
                                  .AsTask().ConfigureAwait(false);
                    try
                    {
                        using (await _mutex.GetLockAsync(state).ConfigureAwait(false))
                        {
                            var pushToken = channel.Uri;
                            if (!string.IsNullOrEmpty(pushToken))
                            {
                                // Save channel member
                                _channel = channel;

                                // Subscribe to push
                                channel.PushNotificationReceived += OnPushNotificationReceivedHandler;

                                // Send channel URI to backend
                                MobileCenterLog.Debug(LogTag, $"Push token '{pushToken}'");

                                var pushInstallationLog = new PushInstallationLog(null, null, pushToken, Guid.NewGuid());

                                // Do not await the call to EnqueueAsync or the UI thread can be blocked!
#pragma warning disable CS4014
                                Channel.EnqueueAsync(pushInstallationLog);
#pragma warning restore
                            }
                            else
                            {
                                MobileCenterLog.Error(LogTag, "Push service registering with Mobile Center backend has failed.");
                            }
                        }
                    }
                    catch (StatefulMutexException)
                    {
                        MobileCenterLog.Warn(LogTag, "Push Enabled state changed after creating channel.");
                    }
                });
            }
            else if (_channel != null)
            {
                _channel.PushNotificationReceived -= OnPushNotificationReceivedHandler;
            }
        }
        public DefaultScreenSizeProvider()
        {
            if (!ApiInformation.IsPropertyPresent(typeof(DisplayInformation).FullName, "ScreenHeightInRawPixels") ||
                !ApiInformation.IsPropertyPresent(typeof(DisplayInformation).FullName, "ScreenWidthInRawPixels"))
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage);
                _displayInformationEventSemaphore.Release();
                return;
            }

            // Only try to get screen size once resuming event is invoked, because there's no point
            // in trying beforehand.
            ApplicationLifecycleHelper.Instance.ApplicationResuming += SetUpDisplayInformation;
        }
Exemple #16
0
 public void Pause()
 {
     lock (_lockObject)
     {
         if (_currentSessionState == SessionState.Inactive)
         {
             MobileCenterLog.Warn(Analytics.Instance.LogTag, "Trying to pause already inactive session.");
             return;
         }
         MobileCenterLog.Debug(Analytics.Instance.LogTag, "SessionTracker.Pause");
         _lastPausedTime      = TimeHelper.CurrentTimeInMilliseconds();
         _currentSessionState = SessionState.Inactive;
     }
 }
 static WpfHelper()
 {
     try
     {
         var assemblies = AppDomain.CurrentDomain.GetAssemblies();
         PresentationFramework =
             assemblies.FirstOrDefault(assembly => assembly.GetName().Name == "PresentationFramework");
         IsRunningOnWpf = PresentationFramework != null;
     }
     catch (AppDomainUnloadedException)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "Unabled to determine whether this application is WPF or Windows Forms; proceeding as though it is Windows Forms.");
     }
 }
Exemple #18
0
        /// <summary>
        /// Asynchronously retrieves logs from storage and flags them to avoid duplicate retrievals on subsequent calls
        /// </summary>
        /// <param name="channelName">Name of the channel to retrieve logs from</param>
        /// <param name="limit">The maximum number of logs to retrieve</param>
        /// <param name="logs">A list to which the retrieved logs will be added</param>
        /// <returns>A batch ID for the set of returned logs; null if no logs are found</returns>
        /// <exception cref="StorageException"/>
        public async Task <string> GetLogsAsync(string channelName, int limit, List <Log> logs)
        {
            using (await _taskLockSource.GetTaskLockAsync().ConfigureAwait(false))
            {
                logs?.Clear();
                var retrievedLogs = new List <Log>();
                MobileCenterLog.Debug(MobileCenterLog.LogTag, $"Trying to get up to {limit} logs from storage for {channelName}");

                var idPairs = new List <Tuple <Guid?, long> >();
                var failedToDeserializeALog = false;
                var retrievedEntries        =
                    await _storageAdapter.GetAsync <LogEntry>(entry => entry.Channel == channelName, limit)
                    .ConfigureAwait(false);

                foreach (var entry in retrievedEntries)
                {
                    if (_pendingDbIdentifiers.Contains(entry.Id))
                    {
                        continue;
                    }
                    try
                    {
                        var log = LogSerializer.DeserializeLog(entry.Log);
                        retrievedLogs.Add(log);
                        idPairs.Add(Tuple.Create(log.Sid, Convert.ToInt64(entry.Id)));
                    }
                    catch (JsonException e)
                    {
                        MobileCenterLog.Error(MobileCenterLog.LogTag, "Cannot deserialize a log in storage", e);
                        failedToDeserializeALog = true;
                        await _storageAdapter.DeleteAsync <LogEntry>(row => row.Id == entry.Id).ConfigureAwait(false);
                    }
                }
                if (failedToDeserializeALog)
                {
                    MobileCenterLog.Warn(MobileCenterLog.LogTag, "Deleted logs that could not be deserialized");
                }
                if (idPairs.Count == 0)
                {
                    MobileCenterLog.Debug(MobileCenterLog.LogTag, $"No available logs in storage for channel '{channelName}'");
                    return(null);
                }

                // Process the results
                var batchId = Guid.NewGuid().ToString();
                ProcessLogIds(channelName, batchId, idPairs);
                logs?.AddRange(retrievedLogs);
                return(batchId);
            }
        }
        /// <summary>
        /// Validates properties.
        /// </summary>
        /// <param name="properties">Properties collection to validate.</param>
        /// <param name="logName">Log name.</param>
        /// <param name="logType">Log type.</param>
        /// <returns>Valid properties collection with maximum size of 5</returns>
        private IDictionary <string, string> ValidateProperties(IDictionary <string, string> properties, string logName, string logType)
        {
            if (properties == null)
            {
                return(null);
            }
            var result = new Dictionary <string, string>();

            foreach (var property in properties)
            {
                if (result.Count >= MaxEventProperties)
                {
                    MobileCenterLog.Warn(LogTag,
                                         $"{logType} '{logName}' : properties cannot contain more than {MaxEventProperties} items. Skipping other properties.");
                    break;
                }

                // Skip empty property.
                var key   = property.Key;
                var value = property.Value;
                if (string.IsNullOrEmpty(key))
                {
                    MobileCenterLog.Warn(LogTag,
                                         $"{logType} '{logName}' : a property key cannot be null or empty. Property will be skipped.");
                    break;
                }
                if (value == null)
                {
                    MobileCenterLog.Warn(LogTag,
                                         $"{logType} '{logName}' : property '{key}' : property value cannot be null. Property will be skipped.");
                    break;
                }

                // Truncate exceeded property.
                if (key.Length > MaxEventPropertyKeyLength)
                {
                    MobileCenterLog.Warn(LogTag,
                                         $"{logType} '{logName}' : property '{key}' : property key length cannot be longer than {MaxEventPropertyKeyLength} characters. Property key will be truncated.");
                    key = key.Substring(0, MaxEventPropertyKeyLength);
                }
                if (value.Length > MaxEventPropertyValueLength)
                {
                    MobileCenterLog.Warn(LogTag,
                                         $"{logType} '{logName}' : property '{key}' : property value length cannot be longer than {MaxEventPropertyValueLength} characters. Property value will be truncated.");
                    value = value.Substring(0, MaxEventPropertyValueLength);
                }
                result.Add(key, value);
            }
            return(result);
        }
Exemple #20
0
 public void Resume()
 {
     lock (_lockObject)
     {
         if (_currentSessionState == SessionState.Active)
         {
             MobileCenterLog.Warn(Analytics.Instance.LogTag, "Trying to resume already active session.");
             return;
         }
         MobileCenterLog.Debug(Analytics.Instance.LogTag, "SessionTracker.Resume");
         _lastResumedTime     = TimeHelper.CurrentTimeInMilliseconds();
         _currentSessionState = SessionState.Active;
         SendStartSessionIfNeeded();
     }
 }
 /// <summary>
 /// Validates name.
 /// </summary>
 /// <param name="name">Log name to validate.</param>
 /// <param name="logType">Log type.</param>
 /// <returns><c>true</c> if validation succeeds, otherwise <с>false</с>.</returns>
 private bool ValidateName(ref string name, string logType)
 {
     if (string.IsNullOrEmpty(name))
     {
         MobileCenterLog.Error(LogTag, $"{logType} name cannot be null or empty.");
         return(false);
     }
     if (name.Length > MaxEventNameLength)
     {
         MobileCenterLog.Warn(LogTag,
                              $"{logType} '{name}' : name length cannot be longer than {MaxEventNameLength} characters. Name will be truncated.");
         name = name.Substring(0, MaxEventNameLength);
         return(true);
     }
     return(true);
 }
 private void Suspend(State state, bool deleteLogs, Exception exception)
 {
     try
     {
         IEnumerable <Log> unsentLogs = null;
         using (_mutex.GetLock(state))
         {
             _enabled        = false;
             _batchScheduled = false;
             _discardLogs    = deleteLogs;
             if (deleteLogs)
             {
                 unsentLogs = _sendingBatches.Values.SelectMany(batch => batch);
                 _sendingBatches.Clear();
             }
             state = _mutex.InvalidateState();
         }
         if (unsentLogs != null)
         {
             foreach (var log in unsentLogs)
             {
                 FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, exception));
             }
         }
         if (deleteLogs)
         {
             try
             {
                 _ingestion.Close();
             }
             catch (IngestionException e)
             {
                 MobileCenterLog.Error(MobileCenterLog.LogTag, "Failed to close ingestion", e);
             }
             using (_mutex.GetLock(state))
             {
                 _pendingLogCount = 0;
                 TriggerDeleteLogsOnSuspending();
             }
         }
         _storage.ClearPendingLogState(Name);
     }
     catch (StatefulMutexException)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Suspend operation has been cancelled");
     }
 }
        /// <summary>
        /// If enabled, register push channel and send URI to backend.
        /// Also start intercepting pushes.
        /// If disabled and previously enabled, stop listening for pushes (they will still be received though).
        /// </summary>
        private void ApplyEnabledState(bool enabled)
        {
            if (enabled)
            {
                // We expect caller of this method to lock on _mutex, we can't do it here as that lock is not recursive
                var stateSnapshot = _stateKeeper.GetStateSnapshot();
                Task.Run(async() =>
                {
                    var channel = await new WindowsPushNotificationChannelManager().CreatePushNotificationChannelForApplicationAsync()
                                  .AsTask().ConfigureAwait(false);
                    try
                    {
                        _mutex.Lock(stateSnapshot);
                        var pushToken = channel.Uri;
                        if (!string.IsNullOrEmpty(pushToken))
                        {
                            // Save channel member
                            _channel = channel;

                            // Subscribe to push
                            channel.PushNotificationReceived += OnPushNotificationReceivedHandler;

                            // Send channel URI to backend
                            MobileCenterLog.Debug(LogTag, $"Push token '{pushToken}'");
                            var pushInstallationLog = new PushInstallationLog(0, null, pushToken, Guid.NewGuid());
                            await Channel.Enqueue(pushInstallationLog).ConfigureAwait(false);
                        }
                        else
                        {
                            MobileCenterLog.Error(LogTag, "Push service registering with Mobile Center backend has failed.");
                        }
                    }
                    catch (StatefulMutexException)
                    {
                        MobileCenterLog.Warn(LogTag, "Push Enabled state changed after creating channel.");
                    }
                    finally
                    {
                        _mutex.Unlock();
                    }
                });
            }
            else if (_channel != null)
            {
                _channel.PushNotificationReceived -= OnPushNotificationReceivedHandler;
            }
        }
        public async Task ClearAsync()
        {
            var state = _mutex.State;
            await _storage.DeleteLogs(Name).ConfigureAwait(false);

            try
            {
                using (await _mutex.GetLockAsync(state).ConfigureAwait(false))
                {
                    _pendingLogCount = 0;
                }
            }
            catch (StatefulMutexException)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Clear operation has been cancelled");
            }
        }
 private void Resume(State state)
 {
     try
     {
         using (_mutex.GetLock(state))
         {
             _enabled     = true;
             _discardLogs = false;
             state        = _mutex.InvalidateState();
         }
     }
     catch (StatefulMutexException)
     {
         MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Resume operation has been cancelled");
     }
     CheckPendingLogs(state);
 }
        public void OnChannelGroupReady(IChannelGroup channelGroup, string appSecret)
        {
            MobileCenterLog.Warn(MobileCenterLog.LogTag, "Crashes service is not yet supported on UWP.");
            try
            {
#if REFERENCE
#else
                WatsonRegistrationManager.Start(appSecret);
#endif
            }
            catch (Exception e)
            {
#if DEBUG
                throw new MobileCenterException("Failed to register crashes with Watson", e);
#endif
            }
        }
Exemple #27
0
        private async Task TriggerIngestionAsync()
        {
            await _mutex.LockAsync().ConfigureAwait(false);

            var stateSnapshot = _stateKeeper.GetStateSnapshot();

            try
            {
                if (!_enabled)
                {
                    return;
                }
                MobileCenterLog.Debug(MobileCenterLog.LogTag,
                                      $"triggerIngestion({Name}) pendingLogCount={_pendingLogCount}");
                _batchScheduled = false;
                if (_sendingBatches.Count >= _maxParallelBatches)
                {
                    MobileCenterLog.Debug(MobileCenterLog.LogTag,
                                          "Already sending " + _maxParallelBatches + " batches of analytics data to the server");
                    return;
                }

                // Get a batch from storage
                var logs = new List <Log>();
                _mutex.Unlock();
                var batchId = await _storage.GetLogsAsync(Name, _maxLogsPerBatch, logs).ConfigureAwait(false);

                await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false);

                if (batchId != null)
                {
                    _sendingBatches.Add(batchId, logs);
                    _pendingLogCount -= logs.Count;
                    TriggerIngestion(logs, stateSnapshot, batchId);
                }
            }
            catch (StatefulMutexException e)
            {
                MobileCenterLog.Warn(MobileCenterLog.LogTag, "The TriggerIngestion operation has been cancelled", e);
            }
            finally
            {
                _mutex.Unlock();
            }
        }
        // Subscribe to the proper events and try to
        private void SetUpDisplayInformation(object sender, EventArgs e)
        {
            try
            {
                CoreApplication.MainView?.CoreWindow?.Dispatcher?.RunAsync(
                    CoreDispatcherPriority.Normal, () =>
                {
                    try
                    {
                        // The exceptions that display information can throw are not documented,
                        // so a catch-all is necessary.
                        var displayInfo = DisplayInformation.GetForCurrentView();
                        UpdateDisplayInformation((int)displayInfo.ScreenHeightInRawPixels,
                                                 (int)displayInfo.ScreenWidthInRawPixels);

                        // Try to detect a change in screen size by attaching handlers to these events.
                        // Since this code can execute multiple times on the same displayInfo, prevent
                        // duplicate handlers by removing and then setting them.
                        displayInfo.OrientationChanged  -= UpdateDisplayInformationHandler;
                        displayInfo.OrientationChanged  += UpdateDisplayInformationHandler;
                        displayInfo.DpiChanged          -= UpdateDisplayInformationHandler;
                        displayInfo.DpiChanged          += UpdateDisplayInformationHandler;
                        displayInfo.ColorProfileChanged -= UpdateDisplayInformationHandler;
                        displayInfo.ColorProfileChanged += UpdateDisplayInformationHandler;

                        // If everything succeeded, we must unsubscribe from the resuming event.
                        ApplicationLifecycleHelper.Instance.ApplicationResuming -= SetUpDisplayInformation;
                    }
                    catch
                    {
                        MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage);
                    }
                    finally
                    {
                        _displayInformationEventSemaphore.Release();
                    }
                });
            }
            catch (COMException)
            {
                // This is reached if the MainView is not ready to be accessed yet.
                _displayInformationEventSemaphore.Release();
                MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage);
            }
        }
        internal static byte[] SerializeException(Exception exception)
        {
            var ms        = new MemoryStream();
            var formatter = new BinaryFormatter();

            try
            {
                formatter.Serialize(ms, exception);
            }
            catch (SerializationException e)
            {
                MobileCenterLog.Warn(Crashes.LogTag, "Failed to serialize exception for client side inspection", e);
                ms = new MemoryStream();
                formatter.Serialize(ms, e);
            }

            return(ms.ToArray());
        }
        public Task Shutdown()
        {
            ThrowIfDisposed();
            var tasks = new List <Task>();

            lock (_channelGroupLock)
            {
                foreach (var channel in _channels)
                {
                    tasks.Add(channel.Shutdown());
                }
                MobileCenterLog.Debug(MobileCenterLog.LogTag, "Waiting for storage to finish operations");
                if (!_storage.Shutdown(_shutdownTimeout))
                {
                    MobileCenterLog.Warn(MobileCenterLog.LogTag, "Storage taking too long to finish operations; shutting down channel without waiting any longer.");
                }
            }
            return(Task.WhenAll(tasks));
        }