From e2dfb4268730db73c3815799006b09df92ff3949 Mon Sep 17 00:00:00 2001 From: Matthias Urhahn <matthias.urhahn@sap.com> Date: Mon, 4 Jan 2021 16:58:20 +0100 Subject: [PATCH] Better handling of issues caused by time traveling (DownloadTask) (DEV) #2018 * If maxExposureDetectionsPerDay is 0, return a saner value than 99 days. maxExposureDetectionsPerDay is checked separately and will abort the download task if necessary. * When checking whether the last detection was recent, add a sanity check that causes it to not skip, if the last detection was in our future. Co-authored-by: Ralf Gehrer <ralfgehrer@users.noreply.github.com> --- .../mapping/ExposureDetectionConfigMapper.kt | 2 +- .../download/DownloadDiagnosisKeysTask.kt | 18 ++++++++++++++++-- .../ExposureDetectionConfigMapperTest.kt | 4 ++-- .../download/DownloadDiagnosisKeysTaskTest.kt | 14 +++++++++++++- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapper.kt index 5110334a3..900ce5bef 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapper.kt @@ -54,7 +54,7 @@ fun ExposureDetectionParametersAndroid?.maxExposureDetectionsPerDay(): Int = fun ExposureDetectionParametersAndroid?.minTimeBetweenExposureDetections(): Duration { val detectionsPerDay = this.maxExposureDetectionsPerDay() return if (detectionsPerDay == 0) { - Duration.standardDays(99) + Duration.standardDays(1) } else { (24 / detectionsPerDay).let { Duration.standardHours(it.toLong()) } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt index 7be1a88e9..6a7414214 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTask.kt @@ -140,9 +140,23 @@ class DownloadDiagnosisKeysTask @Inject constructor( trackedDetections: Collection<TrackedExposureDetection> ): Boolean { val lastDetection = trackedDetections.maxByOrNull { it.startedAt } - val nextDetectionAt = lastDetection?.startedAt?.plus(exposureConfig.minTimeBetweenDetections) + if (lastDetection == null) { + Timber.tag(TAG).d("No previous detections exist, don't abort.") + return false + } + + if (lastDetection.startedAt.isAfter(now.plus(Duration.standardHours(1)))) { + Timber.tag(TAG).w("Last detection happened in our future? Don't abort as precaution.") + return false + } + + val nextDetectionAt = lastDetection.startedAt.plus(exposureConfig.minTimeBetweenDetections) + + Duration(now, nextDetectionAt).also { + Timber.tag(TAG).d("Next detection is available in %d min", it.standardMinutes) + } - return (nextDetectionAt != null && now.isBefore(nextDetectionAt)).also { + return (now.isBefore(nextDetectionAt)).also { if (it) Timber.tag(TAG).w("Aborting. Last detection is recent: %s (now=%s)", lastDetection, now) } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapperTest.kt index f39e4f9de..7a6e080ae 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/mapping/ExposureDetectionConfigMapperTest.kt @@ -21,13 +21,13 @@ class ExposureDetectionConfigMapperTest : BaseTest() { } @Test - fun `detection interval 0 defaults to almost infinite delay`() { + fun `detection interval 0 defaults to sane delay`() { val exposureDetectionParameters = ExposureDetectionParametersAndroid.newBuilder() val rawConfig = AppConfigAndroid.ApplicationConfigurationAndroid.newBuilder() .setExposureDetectionParameters(exposureDetectionParameters) .build() createInstance().map(rawConfig).apply { - minTimeBetweenDetections shouldBe Duration.standardDays(99) + minTimeBetweenDetections shouldBe Duration.standardDays(1) maxExposureDetectionsPerUTCDay shouldBe 0 } } diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt index f85ce9af7..c22593689 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/diagnosiskeys/download/DownloadDiagnosisKeysTaskTest.kt @@ -141,7 +141,7 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { } @Test - fun `execution is skipped if last detection was recent via`() = runBlockingTest { + fun `execution is skipped if last detection was recent`() = runBlockingTest { // Last detection was at T+2h every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(2)) @@ -157,6 +157,18 @@ class DownloadDiagnosisKeysTaskTest : BaseTest() { } } + @Test + fun `execution is NOT skipped if last detection is in our future`() = runBlockingTest { + // Last detection was at T, i.e. our time is now T-1h, so it was in our future. + every { timeStamper.nowUTC } returns Instant.EPOCH.minus(Duration.standardHours(1).plus(1)) + + createInstance().run(DownloadDiagnosisKeysTask.Arguments()) + + coVerify { + enfClient.provideDiagnosisKeys(any(), any()) + } + } + @Test fun `wasLastDetectionPerformedRecently honors paramters from config`() = runBlockingTest { every { timeStamper.nowUTC } returns Instant.EPOCH.plus(Duration.standardHours(4)) -- GitLab