Пример #1
0
        public async Task UploadSelfExposureKeysToServer(IEnumerable <TemporaryExposureKey> temporaryExposureKeys)
        {
            var diagnosisUid = LocalStateManager.Instance.LatestDiagnosis.DiagnosisUid;

            if (string.IsNullOrEmpty(diagnosisUid))
            {
                throw new InvalidOperationException();
            }

            try
            {
                var url = $"{apiUrlBase.TrimEnd('/')}/selfdiagnosis";

                var json = JsonConvert.SerializeObject(new SelfDiagnosisSubmissionRequest
                {
                    DiagnosisUid = diagnosisUid,
                    Keys         = temporaryExposureKeys
                });

                var http     = new HttpClient();
                var response = await http.PutAsync(url, new StringContent(json));

                response.EnsureSuccessStatusCode();

                LocalStateManager.Instance.LatestDiagnosis.Shared = true;
                LocalStateManager.Save();
            }
            catch
            {
                throw;
            }
        }
Пример #2
0
        // this will be called when a potential exposure has been detected
        public async Task ExposureDetectedAsync(ExposureDetectionSummary summary, Func <Task <IEnumerable <ExposureInfo> > > getExposureInfo)
        {
            LocalStateManager.Instance.ExposureSummary = summary;

            var exposureInfo = await getExposureInfo();

            // Add these on main thread in case the UI is visible so it can update
            await Device.InvokeOnMainThreadAsync(() =>
            {
                foreach (var i in exposureInfo)
                {
                    LocalStateManager.Instance.ExposureInformation.Add(i);
                }
            });

            LocalStateManager.Save();
            // If Enabled Local Notifications
            if (LocalStateManager.Instance.EnableNotifications)
            {
                var notification = new NotificationRequest
                {
                    NotificationId = 100,
                    Title          = "Possible COVID-19 Exposure",
                    Description    = "It is possible you have been exposed to someone who was a confirmed diagnosis of COVID-19.  Tap for more details."
                };

                NotificationCenter.Current.Show(notification);
            }
        }
Пример #3
0
 Task Disabled()
 {
     LocalStateManager.Instance.LastIsEnabled = false;
     LocalStateManager.Instance.IsWelcomed    = false;
     LocalStateManager.Save();
     return(NavigationService.NavigateAsync(nameof(HomePage)));
 }
Пример #4
0
        // this will be called when they keys need to be collected from the server
        public async Task FetchExposureKeysFromServerAsync(ITemporaryExposureKeyBatches batches)
        {
            // This is "default" by default
            var region = LocalStateManager.Instance.Region ?? DefaultRegion;

            var checkForMore = true;

            do
            {
                try
                {
                    // Find next batch number
                    var batchNumber = LocalStateManager.Instance.ServerBatchNumber + 1;

                    // Build the blob storage url for the given batch file we are on next
                    var url = $"{apiUrlBlobStorageBase}/{blobStorageContainerNamePrefix}{region}/{batchNumber}.dat";

                    var response = await http.GetAsync(url);

                    // If we get a 404, there are no newer batch files available to download
                    if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                    {
                        checkForMore = false;
                        break;
                    }

                    response.EnsureSuccessStatusCode();

                    // Skip batch files which are older than 14 days
                    if (response.Content.Headers.LastModified.HasValue)
                    {
                        if (response.Content.Headers.LastModified < DateTimeOffset.UtcNow.AddDays(-14))
                        {
                            LocalStateManager.Instance.ServerBatchNumber = batchNumber;
                            LocalStateManager.Save();
                            checkForMore = true;
                            continue;
                        }
                    }

                    // Read the batch file stream
                    using var responseStream = await response.Content.ReadAsStreamAsync();

                    // Parse into a Proto.File
                    var batchFile = TemporaryExposureKeyBatch.Parser.ParseFrom(responseStream);

                    // Submit to the batch processor
                    await batches.AddBatchAsync(batchFile);

                    // Update the number we are on
                    LocalStateManager.Instance.ServerBatchNumber = batchNumber;
                    LocalStateManager.Save();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    checkForMore = false;
                }
            } while (checkForMore);
        }
        Task Disabled()
        {
            LocalStateManager.Instance.LastIsEnabled = false;
            LocalStateManager.Instance.IsWelcomed    = false;
            LocalStateManager.Save();

            return(GoToAsync($"//{nameof(WelcomePage)}"));
        }
Пример #6
0
        // this will be called when the user is submitting a diagnosis and the local keys need to go to the server
        public async Task UploadSelfExposureKeysToServerAsync(IEnumerable <TemporaryExposureKey> temporaryExposureKeys)
        {
            var pendingDiagnosis = LocalStateManager.Instance.PendingDiagnosis;

            if (pendingDiagnosis == null || string.IsNullOrEmpty(pendingDiagnosis.DiagnosisUid))
            {
                throw new InvalidOperationException();
            }

            var selfDiag = await CreateSubmissionAsync();

            var url = $"{AppSettings.Instance.ApiUrlBase.TrimEnd('/')}/selfdiagnosis";

            var json = JsonConvert.SerializeObject(selfDiag);

            using var http = new HttpClient();
            var response = await http.PutAsync(url, new StringContent(json));

            response.EnsureSuccessStatusCode();

            // Update pending status
            pendingDiagnosis.Shared = true;
            LocalStateManager.Save();

            async Task <SelfDiagnosisSubmission> CreateSubmissionAsync()
            {
                // Create the network keys
                var keys = temporaryExposureKeys.Select(k => new ExposureKey
                {
                    Key              = Convert.ToBase64String(k.Key),
                    RollingStart     = (long)(k.RollingStart - DateTime.UnixEpoch).TotalMinutes / 10,
                    RollingDuration  = (int)(k.RollingDuration.TotalMinutes / 10),
                    TransmissionRisk = (int)k.TransmissionRiskLevel
                });

                // Create the submission
                var submission = new SelfDiagnosisSubmission(true)
                {
                    AppPackageName            = AppInfo.PackageName,
                    DeviceVerificationPayload = null,
                    Platform            = DeviceInfo.Platform.ToString().ToLowerInvariant(),
                    Regions             = AppSettings.Instance.SupportedRegions,
                    Keys                = keys.ToArray(),
                    VerificationPayload = pendingDiagnosis.DiagnosisUid,
                };

                // See if we can add the device verification
                if (DependencyService.Get <IDeviceVerifier>() is IDeviceVerifier verifier)
                {
                    submission.DeviceVerificationPayload = await verifier?.VerifyAsync(submission);
                }

                return(submission);
            }
        }
Пример #7
0
        public async Task FetchExposureKeysFromServer(Func <IEnumerable <TemporaryExposureKey>, Task> addKeys)
        {
            var latestKeysResponseIndex = LocalStateManager.Instance.LatestKeysResponseIndex;

            var take = 1024;
            var skip = 0;

            var checkForMore = false;

            do
            {
                // Get the newest date we have keys from and request since then
                // or if no date stored, only return as much as the past 14 days of keys
                var url = $"{apiUrlBase.TrimEnd('/')}/keys?since={latestKeysResponseIndex}&skip={skip}&take={take}";

                var response = await http.GetAsync(url);

                response.EnsureSuccessStatusCode();

                var responseData = await response.Content.ReadAsStringAsync();

                if (string.IsNullOrEmpty(responseData))
                {
                    break;
                }

                // Response contains the timestamp in seconds since epoch, and the list of keys
                var keys = JsonConvert.DeserializeObject <KeysResponse>(responseData);

                var numKeys = keys?.Keys?.Count() ?? 0;

                // See if keys were returned on this call
                if (numKeys > 0)
                {
                    // Call the callback with the batch of keys to add
                    await addKeys(keys.Keys);

                    var newLatestKeysResponseIndex = keys.Latest;

                    if (newLatestKeysResponseIndex > LocalStateManager.Instance.LatestKeysResponseIndex)
                    {
                        LocalStateManager.Instance.LatestKeysResponseIndex = newLatestKeysResponseIndex;
                        LocalStateManager.Save();
                    }

                    // Increment our skip starting point for the next batch
                    skip += take;
                }

                // If we got back more or the same amount of our requested take, there may be
                // more left on the server to request again
                checkForMore = numKeys >= take;
            } while (checkForMore);
        }
Пример #8
0
        public async Task ExposureDetected(ExposureDetectionSummary summary, Func <Task <IEnumerable <ExposureInfo> > > getDetailsFunc)
        {
            LocalStateManager.Instance.ExposureSummary = summary;

            var details = await getDetailsFunc();

            LocalStateManager.Instance.ExposureInformation.AddRange(details);

            LocalStateManager.Save();

            MessagingCenter.Instance.Send(this, "exposure_info_changed");

            // TODO: Save this info and alert the user
            // Pop up a local notification
        }
Пример #9
0
        // this will be called when a potential exposure has been detected
        public Task ExposureDetectedAsync(ExposureDetectionSummary summary, IEnumerable <ExposureInfo> exposureInfo)
        {
            LocalStateManager.Instance.ExposureSummary = summary;

            LocalStateManager.Instance.ExposureInformation.AddRange(exposureInfo);

            LocalStateManager.Save();

            MessagingCenter.Instance.Send(this, "exposure_info_changed");

            var notification = new NotificationRequest
            {
                NotificationId = 100,
                Title          = "Possible COVID-19 Exposure",
                Description    = "It is possible you have been exposed to someone who was a confirmed diagnosis of COVID-19.  Tap for more details."
            };

            NotificationCenter.Current.Show(notification);

            return(Task.CompletedTask);
        }
Пример #10
0
        // this will be called when they keys need to be collected from the server
        public async Task FetchExposureKeyBatchFilesFromServerAsync(Func <IEnumerable <string>, Task> submitBatches, CancellationToken cancellationToken)
        {
            // This is "default" by default
            var rightNow = DateTimeOffset.UtcNow;

            try
            {
                foreach (var serverRegion in AppSettings.Instance.SupportedRegions)
                {
                    // Find next directory to start checking
                    var dirNumber = LocalStateManager.Instance.ServerBatchNumbers[serverRegion] + 1;

                    // For all the directories
                    while (true)
                    {
                        cancellationToken.ThrowIfCancellationRequested();

                        // Download all the files for this directory
                        var(batchNumber, downloadedFiles) = await DownloadBatchAsync(serverRegion, dirNumber, cancellationToken);

                        if (batchNumber == 0)
                        {
                            break;
                        }

                        // Process the current directory, if there were any files
                        if (downloadedFiles.Count > 0)
                        {
                            await submitBatches(downloadedFiles);

                            // delete all temporary files
                            foreach (var file in downloadedFiles)
                            {
                                try
                                {
                                    File.Delete(file);
                                }
                                catch
                                {
                                    // no-op
                                }
                            }
                        }

                        // Update the preferences
                        LocalStateManager.Instance.ServerBatchNumbers[serverRegion] = dirNumber;
                        LocalStateManager.Save();

                        dirNumber++;
                    }
                }
            }
            catch (Exception ex)
            {
                // any expections, bail out and wait for the next time

                // TODO: log the error on some server!
                Console.WriteLine(ex);
            }

            async Task <(int, List <string>)> DownloadBatchAsync(string region, ulong dirNumber, CancellationToken cancellationToken)
            {
                var downloadedFiles = new List <string>();
                var batchNumber     = 0;

                // For all the batches in a directory
                while (true)
                {
                    // Build the blob storage url for the given batch file we are on next
                    var url = $"{AppSettings.Instance.BlobStorageUrlBase}/{AppSettings.Instance.BlobStorageContainerNamePrefix}{region.ToLowerInvariant()}/{dirNumber}/{batchNumber + 1}.dat";

                    var response = await http.GetAsync(url, cancellationToken);

                    // If we get a 404, there are no newer batch files available to download
                    if (response.StatusCode == System.Net.HttpStatusCode.NotFound)
                    {
                        break;
                    }

                    response.EnsureSuccessStatusCode();

                    // Skip batch files which are older than 14 days
                    if (response.Content.Headers.LastModified.HasValue && response.Content.Headers.LastModified < rightNow.AddDays(-14))
                    {
                        // If the first one is too old, the fake download it and pretend there was only one
                        // We can do this because each batch was created at the same time
                        batchNumber++;
                        break;
                    }

                    var tmpFile = Path.Combine(FileSystem.CacheDirectory, Guid.NewGuid().ToString() + ".zip");

                    // Read the batch file stream into a temporary file
                    using var responseStream = await response.Content.ReadAsStreamAsync();

                    using var fileStream = File.Create(tmpFile);
                    await responseStream.CopyToAsync(fileStream, cancellationToken);

                    downloadedFiles.Add(tmpFile);

                    batchNumber++;
                }

                return(batchNumber, downloadedFiles);
            }
        }