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 ab39dda9e64d0849d655603e9c720ff53819c7f8..3d8dc7205d3755f631f6bdc1aae85a4835c84f20 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 405aa42953a5f7a37a9ae6e98748fb95bafc9ee0..5e0018d988f1dd138bc825ead07ff7812e18efe6 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