Esempio n. 1
0
        static async Task <ENManager> GetManagerAsync()
        {
            if (manager == null)
            {
                manager = new ENManager();
                await manager.ActivateAsync();
            }

            return(manager);
        }
        public static Task PlatformScheduleFetch()
        {
            string logPrefix = $"{nameof(BackgroundServiceHandler)}.{nameof(PlatformScheduleFetch)}: ";


            // iOS 12.5

            if (AppDelegate.ShouldOperateIn12_5Mode)
            {
                if (ObjCRuntime.Class.GetHandle("ENManager") == IntPtr.Zero)
                {
                    LogUtils.LogException(Enums.LogSeverity.ERROR,
                                          new NullReferenceException("Pointer to ENManager is null"),
                                          logPrefix + "Failed to retrieve ENManager instance [iOS 12.5 mode]");
                    return(null);
                }

                ENManager manager = new ENManager();

                manager.SetLaunchActivityHandler(activityFlags =>
                {
                    if (activityFlags.HasFlag(ENActivityFlags.PeriodicRun))
                    {
                        Task.Run(async() =>
                        {
                            CancellationTokenSource cancelSrc = new CancellationTokenSource();
                            try
                            {
                                await Xamarin.ExposureNotifications.ExposureNotification.UpdateKeysFromServer(cancelSrc.Token);
                            }
                            catch (OperationCanceledException e)
                            {
                                LogUtils.LogException(Enums.LogSeverity.WARNING, e, logPrefix + "Background task took too long to complete [iOS 12.5 mode]");
                            }
                            catch (NSErrorException nserror)
                            {
                                if (nserror.Domain == "ENErrorDomain")
                                {
                                    LogUtils.LogException(Enums.LogSeverity.WARNING, nserror, logPrefix + $"Background task failed due to EN API Error Code={nserror.Code} [iOS 12.5 mode]");
                                }
                                else
                                {
                                    LogUtils.LogException(Enums.LogSeverity.WARNING, nserror, logPrefix + $"Background task failed due to NSError {nserror.Domain} {nserror.Code}. [iOS 12.5 mode]");
                                }
                            }
                            catch (Exception ex)
                            {
                                LogUtils.LogException(Enums.LogSeverity.WARNING, ex, logPrefix + $"Background task: An error occurred inside the async task [iOS 12.5 mode]");
                            }
                        });
                    }
                });
                return(manager.ActivateAsync());
            }

            // iOS 13+

            // This is a special ID suffix which iOS treats a certain way
            // we can basically request infinite background tasks
            // and iOS will throttle it sensibly for us.
            string id = CommonServiceLocator.ServiceLocator.Current.GetInstance <IAppInfo>().PackageName + ".exposure-notification";

            BGTaskScheduler.Shared.Register(id, null, async task =>
            {
                try
                {
                    if (ENManager.AuthorizationStatus != ENAuthorizationStatus.Authorized) // ENManager is not authorised yet
                    {
                        Debug.WriteLine(logPrefix + "ENManager AuthorizationStatus isn't authorized");
                        scheduleBgTask(0); // reschedule to try in the future
                        return;
                    }

                    bool shouldFetch = await Xamarin.ExposureNotifications.ExposureNotification.IsEnabledAsync();
                    if (!shouldFetch)
                    {
                        LogUtils.LogMessage(Enums.LogSeverity.WARNING, logPrefix + "Did not pull. EN was not enabled (iOS)");
                    }

                    System.Diagnostics.Debug.WriteLine($"Background Task is fetching: {shouldFetch}");
                    if (shouldFetch)
                    {
                        CancellationTokenSource cancelSrc = new CancellationTokenSource();
                        task.ExpirationHandler            = cancelSrc.Cancel;

                        // Run the actual task on a background thread
                        await Task.Run(async() =>
                        {
                            try
                            {
                                await Xamarin.ExposureNotifications.ExposureNotification.UpdateKeysFromServer(cancelSrc.Token);
                                task.SetTaskCompleted(true);
                            }
                            catch (OperationCanceledException e)
                            {
                                LogUtils.LogException(Enums.LogSeverity.WARNING, e, logPrefix + "Background task took too long to complete. - BG Task is rescheduled");
                                task.SetTaskCompleted(false);
                            }
                            catch (NSErrorException nserror)
                            {
                                if (nserror.Domain == "ENErrorDomain")
                                {
                                    LogUtils.LogException(Enums.LogSeverity.WARNING, nserror, logPrefix + $"Background task failed due to EN API Error Code={nserror.Code} - BG Task is rescheduled");
                                    task.SetTaskCompleted(false);
                                }
                                else
                                {
                                    LogUtils.LogException(Enums.LogSeverity.WARNING, nserror, logPrefix + $"Background task failed due to NSError {nserror.Domain} {nserror.Code}. - BG Task is rescheduled");
                                    task.SetTaskCompleted(false);
                                }
                            }
                            catch (Exception ex)
                            {
                                LogUtils.LogException(Enums.LogSeverity.WARNING, ex, logPrefix + $"Background task: An error occurred inside the async task - BG Task is rescheduled");
                                task.SetTaskCompleted(false);
                            }
                        });
                    }
                }
                catch (Exception e)
                {
                    LogUtils.LogException(Enums.LogSeverity.WARNING, e, logPrefix + $"Background task: An error occurred before executing the async task - BG Task is rescheduled");
                    task.SetTaskCompleted(false);
                }

                scheduleBgTask(0);
            });

            scheduleBgTask(0);

            return(Task.CompletedTask);

            void scheduleBgTask(int numberOfRetries)
            {
                BGProcessingTaskRequest newBgTask = new BGProcessingTaskRequest(id);

                newBgTask.RequiresNetworkConnectivity = true;
                try
                {
                    BGTaskScheduler.Shared.Submit(newBgTask, out var error);
                    if (error != null)
                    {
                        if ((BGTaskSchedulerErrorCode)(int)error.Code == BGTaskSchedulerErrorCode.TooManyPendingTaskRequests && numberOfRetries < 5)
                        {
                            BGTaskScheduler.Shared.Cancel(id);
                            scheduleBgTask(++numberOfRetries);
                        }
                        else
                        {
                            if ((BGTaskSchedulerErrorCode)(int)error.Code == BGTaskSchedulerErrorCode.Unavailable)
                            {
                                //This will happen IF:
                                // - The EN-API permission is not granted/was removed
                                // AND
                                // - The “Background app refresh” setting is turned OFF.
                            }
                            throw new NSErrorException(error);
                        }
                    }
                }
                catch (Exception ex)
                {
                    LogUtils.LogException(Enums.LogSeverity.ERROR, ex, logPrefix + $"Failed to schedule the background task (Tried 5 times). It will try again when the app is restarted.");
                }
            }
        }