Skip to content
This repository has been archived by the owner on Apr 12, 2023. It is now read-only.

Commit

Permalink
DiagnosisKeysDataMapping can be updated after 7+ days since last up…
Browse files Browse the repository at this point in the history
…dated.
  • Loading branch information
keiji committed Nov 21, 2021
1 parent a2fbb24 commit 0b99b1b
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 27 deletions.
8 changes: 7 additions & 1 deletion Covid19Radar/Covid19Radar/Common/PreferenceKey.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Covid19Radar.Common
{
public static class PreferenceKey
{
// for preferences
// for UserDataRepository
public static string AppVersion = "AppVersion";
public static string LastProcessTekTimestamp = "LastProcessTekTimestamp";
public static string ExposureNotificationConfiguration = "ExposureNotificationConfiguration";
Expand All @@ -21,6 +21,12 @@ public static class PreferenceKey
public const string DailySummaries = "DailySummaries";
public const string ExposureWindows = "ExposureWindows";

// for ExposureConfigurationRepository
public const string IsExposureConfigurationUpdated = "IsExposureConfigurationUpdated";
public const string ExposureConfigurationDownloadedEpoch = "ExposureConfigurationDownloadedEpoch";
public const string ExposureConfigurationAppliedEpoch = "ExposureConfigurationAppliedEpoch";


// for secure storage
public static string ExposureInformation = "ExposureInformation";
}
Expand Down
204 changes: 178 additions & 26 deletions Covid19Radar/Covid19Radar/Repository/ExposureConfigurationRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Chino;
using Covid19Radar.Common;
using Covid19Radar.Services;
using Covid19Radar.Services.Logs;
using Newtonsoft.Json;
Expand All @@ -16,33 +18,49 @@ namespace Covid19Radar.Repository
{
public interface IExposureConfigurationRepository
{
public DateTime? GetExposureConfigurationDownloadedDateTime();

public bool IsDiagnosisKeysDataMappingConfigurationUpdated();
public void SetDiagnosisKeysDataMappingConfigurationUpdated(bool updated);
public void SetDiagnosisKeysDataMappingAppliedDateTime(DateTime dateTime);
public DateTime? GetDiagnosisKeysDataMappingConfigurationAppliedDateTime();

public Task<ExposureConfiguration> GetExposureConfigurationAsync();
public void RemoveExposureConfiguration();
}

public class ExposureConfigurationRepository : IExposureConfigurationRepository
{
private const string CONFIG_DIR = "exposure_configuration";
private const string CURRENT_CONFIG_FILENAME = "current.json";

private const int EXPOSURE_CONFIGURATION_FILE_RETENTION_DAYS = 2;
private const int EXPOSURE_CONFIGURATION_RETENTION_DAYS = 7 + 1;

private readonly HttpClient _client;
private readonly IPreferencesService _preferencesService;
private readonly IServerConfigurationRepository _serverConfigurationRepository;
private readonly ILoggerService _loggerService;

private readonly string _configDir;

private string _exposureConfigurationPath;
private string _currentExposureConfigurationPath;

public ExposureConfigurationRepository(
IHttpClientService httpClientService,
IPreferencesService preferencesService,
IServerConfigurationRepository serverConfigurationRepository,
ILoggerService loggerService
)
{
_client = httpClientService.Create();
_preferencesService = preferencesService;
_serverConfigurationRepository = serverConfigurationRepository;
_loggerService = loggerService;

_configDir = PrepareConfigDir();

_currentExposureConfigurationPath = Path.Combine(_configDir, CURRENT_CONFIG_FILENAME);
}

private string PrepareConfigDir()
Expand All @@ -61,30 +79,43 @@ public async Task<ExposureConfiguration> GetExposureConfigurationAsync()
{
_loggerService.StartMethod();

await _serverConfigurationRepository.LoadAsync();
string url = _serverConfigurationRepository.ExposureConfigurationUrl;

string fileName = url.Split('/').Last();
_exposureConfigurationPath = Path.Combine(_configDir, fileName);
ExposureConfiguration currentExposureConfiguration = null;

if (File.Exists(_exposureConfigurationPath))
if (File.Exists(_currentExposureConfigurationPath))
{
_loggerService.Debug("ExposureConfiguration file is found in cache-dir.");
_loggerService.Debug("ExposureConfiguration file is found.");

string exposureConfigurationAsJson = await LoadAsync();
string exposureConfigurationAsJson = await LoadAsync(_currentExposureConfigurationPath);

try
{
return JsonConvert.DeserializeObject<ExposureConfiguration>(exposureConfigurationAsJson);
currentExposureConfiguration = JsonConvert.DeserializeObject<ExposureConfiguration>(exposureConfigurationAsJson);

if (!IsDownloadedExposureConfigurationOutdated(EXPOSURE_CONFIGURATION_FILE_RETENTION_DAYS))
{
return currentExposureConfiguration;
}
else
{
_loggerService.Info($"ExposureConfiguration is found but the file is outdated.");
}
}
catch (JsonException exception)
{
_loggerService.Exception("JsonException. Cached ExposureConfiguration file has been deleted.", exception);
File.Delete(_exposureConfigurationPath);
_loggerService.Exception("JsonException. ExposureConfiguration file has been deleted.", exception);
RemoveExposureConfiguration(_currentExposureConfigurationPath);
}
}

ExposureConfiguration exposureConfiguration;
if (currentExposureConfiguration is null)
{
currentExposureConfiguration = new ExposureConfiguration();
}

await _serverConfigurationRepository.LoadAsync();
string url = _serverConfigurationRepository.ExposureConfigurationUrl;

ExposureConfiguration newExposureConfiguration = null;

var response = await _client.GetAsync(url);
if (response.IsSuccessStatusCode)
Expand All @@ -94,45 +125,166 @@ public async Task<ExposureConfiguration> GetExposureConfigurationAsync()

try
{
exposureConfiguration = JsonConvert.DeserializeObject<ExposureConfiguration>(exposureConfigurationAsJson);
await SaveAsync(exposureConfigurationAsJson);
newExposureConfiguration = JsonConvert.DeserializeObject<ExposureConfiguration>(exposureConfigurationAsJson);
}
catch (JsonException exception)
{
_loggerService.Exception("JsonException. Default configuration will be loaded.", exception);
exposureConfiguration = new ExposureConfiguration();
_loggerService.Exception("JsonException.", exception);
}

}
else
{
_loggerService.Warning($"Download ExposureConfiguration failed from {url}. Default configuration will be loaded.");
exposureConfiguration = new ExposureConfiguration();
_loggerService.Warning($"Download ExposureConfiguration failed from {url}");
}

if (newExposureConfiguration is null)
{
return currentExposureConfiguration;
}

if (IsUpdatedDiagnosisKeysDataMapping(currentExposureConfiguration, newExposureConfiguration))
{
if (IsExposureConfigurationOutdated(EXPOSURE_CONFIGURATION_RETENTION_DAYS))
{
currentExposureConfiguration = newExposureConfiguration;
SetDiagnosisKeysDataMappingConfigurationUpdated(true);
}
else
{
_loggerService.Info($"DiagnosisKeysDataMappingConfig has been changed but not updated, because current configuration is updated in {EXPOSURE_CONFIGURATION_RETENTION_DAYS} days.");
}
}
else
{
currentExposureConfiguration = newExposureConfiguration;
}

await SaveAsync(
JsonConvert.SerializeObject(currentExposureConfiguration, Formatting.Indented),
_currentExposureConfigurationPath);
SetExposureConfigurationDownloadedDateTime(DateTime.UtcNow);

_loggerService.EndMethod();

return exposureConfiguration;
return currentExposureConfiguration;
}

private bool IsUpdatedDiagnosisKeysDataMapping(
ExposureConfiguration exposureConfiguration1,
ExposureConfiguration exposureConfiguration2
)
{
return
(exposureConfiguration1.GoogleDiagnosisKeysDataMappingConfig
!= exposureConfiguration2.GoogleDiagnosisKeysDataMappingConfig)
||
(exposureConfiguration1.AppleExposureConfigV2.InfectiousnessForDaysSinceOnsetOfSymptoms
!= exposureConfiguration2.AppleExposureConfigV2.InfectiousnessForDaysSinceOnsetOfSymptoms);

}

private bool IsExposureConfigurationOutdated(int retensionDays)
{
DateTime? appliedDate = GetDiagnosisKeysDataMappingConfigurationAppliedDateTime();
if (appliedDate is null)
{
return true;
}
return (DateTime.UtcNow - appliedDate) > TimeSpan.FromDays(retensionDays);
}

private bool IsDownloadedExposureConfigurationOutdated(int retensionDays)
{
DateTime? downloadedDate = GetExposureConfigurationDownloadedDateTime();
if (downloadedDate is null)
{
return true;
}
return (DateTime.UtcNow - downloadedDate) > TimeSpan.FromDays(retensionDays);
}

private async Task<string> LoadAsync()
private async Task<string> LoadAsync(string path)
{
using var reader = File.OpenText(_exposureConfigurationPath);
using var reader = File.OpenText(path);
return await reader.ReadToEndAsync();
}

private async Task SaveAsync(string exposureConfigurationAsJson)
=> await File.WriteAllTextAsync(_exposureConfigurationPath, exposureConfigurationAsJson);
private async Task SaveAsync(string exposureConfigurationAsJson, string outPath)
=> await File.WriteAllTextAsync(outPath, exposureConfigurationAsJson);

public void RemoveExposureConfiguration()
{
if (!File.Exists(_exposureConfigurationPath))
RemoveExposureConfiguration(_currentExposureConfigurationPath);
}

private void RemoveExposureConfiguration(string path)
{
if (!File.Exists(path))
{
_loggerService.Debug("No ExposureConfiguration file found.");
return;
}

File.Delete(_exposureConfigurationPath);
File.Delete(path);
}

public void SetDiagnosisKeysDataMappingConfigurationUpdated(bool updated)
=> _preferencesService.SetValue(PreferenceKey.IsExposureConfigurationUpdated, updated);

public bool IsDiagnosisKeysDataMappingConfigurationUpdated()
=> _preferencesService.GetValue(PreferenceKey.IsExposureConfigurationUpdated, true);

private void SetExposureConfigurationDownloadedDateTime(DateTime dateTime)
{
_loggerService.StartMethod();
_preferencesService.SetValue(PreferenceKey.ExposureConfigurationDownloadedEpoch, dateTime.ToUnixEpoch());
_loggerService.EndMethod();
}

public DateTime? GetExposureConfigurationDownloadedDateTime()
{
_loggerService.StartMethod();
try
{
if (!_preferencesService.ContainsKey(PreferenceKey.ExposureConfigurationDownloadedEpoch))
{
return null;
}

long epoch = _preferencesService.GetValue(PreferenceKey.ExposureConfigurationDownloadedEpoch, 0L);
return DateTime.UnixEpoch.AddSeconds(epoch);
}
finally
{
_loggerService.EndMethod();
}
}

public void SetDiagnosisKeysDataMappingAppliedDateTime(DateTime dateTime)
{
_loggerService.StartMethod();
_preferencesService.SetValue(PreferenceKey.ExposureConfigurationAppliedEpoch, dateTime.ToUnixEpoch());
_loggerService.EndMethod();
}

public DateTime? GetDiagnosisKeysDataMappingConfigurationAppliedDateTime()
{
_loggerService.StartMethod();
try
{
if (!_preferencesService.ContainsKey(PreferenceKey.ExposureConfigurationAppliedEpoch))
{
return null;
}

long epoch = _preferencesService.GetValue(PreferenceKey.ExposureConfigurationAppliedEpoch, 0L);
return DateTime.UnixEpoch.AddSeconds(epoch);
}
finally
{
_loggerService.EndMethod();
}
}
}
}
23 changes: 23 additions & 0 deletions Covid19Radar/Covid19Radar/Services/ExposureDetectionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
Expand Down Expand Up @@ -30,6 +31,8 @@ public class ExposureDetectionService : IExposureDetectionService
private readonly ILocalNotificationService _localNotificationService;
private readonly IExposureRiskCalculationService _exposureRiskCalculationService;

private readonly IExposureConfigurationRepository _exposureConfigurationRepository;

private readonly IExposureDataCollectServer _exposureDataCollectServer;

public ExposureDetectionService
Expand All @@ -38,13 +41,15 @@ public ExposureDetectionService
IUserDataRepository userDataRepository,
ILocalNotificationService localNotificationService,
IExposureRiskCalculationService exposureRiskCalculationService,
IExposureConfigurationRepository exposureConfigurationRepository,
IExposureDataCollectServer exposureDataCollectServer
)
{
_loggerService = loggerService;
_userDataRepository = userDataRepository;
_localNotificationService = localNotificationService;
_exposureRiskCalculationService = exposureRiskCalculationService;
_exposureConfigurationRepository = exposureConfigurationRepository;
_exposureDataCollectServer = exposureDataCollectServer;
}

Expand All @@ -59,6 +64,12 @@ public void ExposureDetected(ExposureConfiguration exposureConfiguration, string

_ = Task.Run(async () =>
{
if (_exposureConfigurationRepository.IsDiagnosisKeysDataMappingConfigurationUpdated())
{
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingAppliedDateTime(DateTime.UtcNow);
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingConfigurationUpdated(false);
}

await _userDataRepository.SetExposureDataAsync(
dailySummaries.ToList(),
exposureWindows.ToList()
Expand Down Expand Up @@ -95,6 +106,12 @@ public void ExposureDetected(ExposureConfiguration exposureConfiguration, string

_ = Task.Run(async() =>
{
if (_exposureConfigurationRepository.IsDiagnosisKeysDataMappingConfigurationUpdated())
{
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingAppliedDateTime(DateTime.UtcNow);
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingConfigurationUpdated(false);
}

bool isNewExposureDetected = _userDataRepository.AppendExposureData(
exposureSummary,
exposureInformations.ToList(),
Expand Down Expand Up @@ -125,6 +142,12 @@ public void ExposureNotDetected(ExposureConfiguration exposureConfiguration, str

_ = Task.Run(async () =>
{
if (_exposureConfigurationRepository.IsDiagnosisKeysDataMappingConfigurationUpdated())
{
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingAppliedDateTime(DateTime.UtcNow);
_exposureConfigurationRepository.SetDiagnosisKeysDataMappingConfigurationUpdated(false);
}

await _exposureDataCollectServer.UploadExposureDataAsync(
exposureConfiguration,
DeviceInfo.Model,
Expand Down

0 comments on commit 0b99b1b

Please sign in to comment.