From 4c0114e651183a0b8c97c0d5aac3e57e827b4f83 Mon Sep 17 00:00:00 2001
From: chris-cwa <69595386+chris-cwa@users.noreply.github.com>
Date: Mon, 23 Nov 2020 13:40:12 +0100
Subject: [PATCH] Ignore negative secondsSinceLastScan (EXPOSUREAPP-3897)
 (#1694)

ignore negative secondsSinceLastScan
---
 .../coronawarnapp/risk/DefaultRiskLevels.kt   |  5 ++-
 .../exposure-windows-risk-calculation.json    | 40 +++++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
index ab39dda9e..3d8dc7205 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
@@ -14,6 +14,7 @@ import org.joda.time.Instant
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
+import kotlin.math.max
 
 @Singleton
 class DefaultRiskLevels @Inject constructor() : RiskLevels {
@@ -37,7 +38,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
             // Get total seconds at attenuation in exposure window
             val secondsAtAttenuation: Double = scanInstances
                 .filter { attenuationFilter.attenuationRange.inRange(it.minAttenuationDb) }
-                .fold(.0) { acc, scanInstance -> acc + scanInstance.secondsSinceLastScan }
+                .fold(.0) { acc, scanInstance -> acc + max(scanInstance.secondsSinceLastScan, 0) }
 
             val minutesAtAttenuation = secondsAtAttenuation / 60
             return attenuationFilter.dropIfMinutesInRange.inRange(minutesAtAttenuation)
@@ -86,7 +87,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
                     .filter { it.attenuationRange.inRange(scanInstance.minAttenuationDb) }
                     .map { it.weight }
                     .firstOrNull() ?: .0
-            seconds + scanInstance.secondsSinceLastScan * weight
+            seconds + max(scanInstance.secondsSinceLastScan, 0) * weight
         }
 
     private fun determineRiskLevel(
diff --git a/Corona-Warn-App/src/test/resources/exposure-windows-risk-calculation.json b/Corona-Warn-App/src/test/resources/exposure-windows-risk-calculation.json
index 405aa4295..5e0018d98 100644
--- a/Corona-Warn-App/src/test/resources/exposure-windows-risk-calculation.json
+++ b/Corona-Warn-App/src/test/resources/exposure-windows-risk-calculation.json
@@ -1060,6 +1060,46 @@
       "expAgeOfMostRecentDateWithHighRisk": null,
       "expNumberOfDaysWithLowRisk": 1,
       "expNumberOfDaysWithHighRisk": 0
+    },
+    {
+      "description": "ignores negative secondsSinceLastScan (can happen when time-travelling, not officially supported)",
+      "exposureWindows": [
+        {
+          "ageInDays": 1,
+          "reportType": 3,
+          "infectiousness": 2,
+          "calibrationConfidence": 0,
+          "scanInstances": [
+            {
+              "minAttenuation": 25,
+              "typicalAttenuation": 25,
+              "secondsSinceLastScan": -86160
+            },
+            {
+              "minAttenuation": 25,
+              "typicalAttenuation": 25,
+              "secondsSinceLastScan": 300
+            },
+            {
+              "minAttenuation": 25,
+              "typicalAttenuation": 25,
+              "secondsSinceLastScan": 300
+            },
+            {
+              "minAttenuation": 25,
+              "typicalAttenuation": 25,
+              "secondsSinceLastScan": 300
+            }
+          ]
+        }
+      ],
+      "expTotalRiskLevel": 2,
+      "expTotalMinimumDistinctEncountersWithLowRisk": 0,
+      "expTotalMinimumDistinctEncountersWithHighRisk": 1,
+      "expAgeOfMostRecentDateWithLowRisk": null,
+      "expAgeOfMostRecentDateWithHighRisk": 1,
+      "expNumberOfDaysWithLowRisk": 0,
+      "expNumberOfDaysWithHighRisk": 1
     }
   ]
 }
\ No newline at end of file
-- 
GitLab