From 46eb59af569936a66ad9d7c645b7e8c3aede5f63 Mon Sep 17 00:00:00 2001
From: Hee Tatt Ooi <hee.tatt.ooi@sap.com>
Date: Sun, 7 Jun 2020 17:07:09 +0200
Subject: [PATCH] timestamp for last successful risk level calculation (#255)

---
 .../coronawarnapp/TestRiskLevelCalculation.kt |  4 +-
 .../de/rki/coronawarnapp/storage/LocalData.kt | 32 ++++++++++++-
 .../transaction/RiskLevelTransaction.kt       | 47 +++++++++++++++----
 .../src/main/res/values/strings.xml           |  4 ++
 .../transaction/RiskLevelTransactionTest.kt   |  9 ++++
 5 files changed, 85 insertions(+), 11 deletions(-)

diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
index 9f492814b..94df6e4ab 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/TestRiskLevelCalculation.kt
@@ -258,7 +258,9 @@ class TestRiskLevelCalculation : Fragment() {
                             "Tracing Duration: " +
                             "${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days \n" +
                             "Tracing Duration in last 14 days: " +
-                            "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days"
+                            "${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days \n" +
+                            "Last time risk level calculation ${LocalData.lastTimeRiskLevelCalculation()}"
+
                     binding.labelRiskScore.text = riskAsString
 
                     val lowClass =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
index c6c6bba9d..fc0d79de6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
@@ -276,7 +276,6 @@ object LocalData {
      * @param value timestamp as Date
      */
     fun lastTimeDiagnosisKeysFromServerFetch(value: Date?) {
-        // TODO need this for nullable ref, shout not be goto for nullable storage
         getSharedPreferenceInstance().edit(true) {
             putLong(
                 CoronaWarnApplication.getAppContext()
@@ -286,6 +285,37 @@ object LocalData {
         }
     }
 
+    /**
+     * Gets the last time of successful risk level calculation as long
+     * from the EncryptedSharedPrefs
+     *
+     * @return Long
+     */
+    fun lastTimeRiskLevelCalculation(): Long? {
+        val time = getSharedPreferenceInstance().getLong(
+            CoronaWarnApplication.getAppContext()
+                .getString(R.string.preference_timestamp_risk_level_calculation),
+            0L
+        )
+        return Date(time).time
+    }
+
+    /**
+     * Sets the last time of successful risk level calculation as long
+     * from the EncryptedSharedPrefs
+     *
+     * @param value timestamp as Long
+     */
+    fun lastTimeRiskLevelCalculation(value: Long?) {
+        getSharedPreferenceInstance().edit(true) {
+            putLong(
+                CoronaWarnApplication.getAppContext()
+                    .getString(R.string.preference_timestamp_risk_level_calculation),
+                value ?: 0L
+            )
+        }
+    }
+
     /**
      * Gets the last timestamp the user manually triggered the key retrieval process
      *
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt
index df32874cc..690595b1f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/transaction/RiskLevelTransaction.kt
@@ -32,6 +32,7 @@ import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactio
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.CLOSE
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RETRIEVE_APPLICATION_CONFIG
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RETRIEVE_EXPOSURE_SUMMARY
+import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.RISK_CALCULATION_DATE_UPDATE
 import de.rki.coronawarnapp.transaction.RiskLevelTransaction.RiskLevelTransactionState.UPDATE_RISK_LEVEL
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.millisecondsToHours
 import kotlinx.coroutines.Dispatchers
@@ -108,7 +109,8 @@ import java.util.concurrent.atomic.AtomicReference
  * 6. [CHECK_INCREASED_RISK]
  * 7. [CHECK_UNKNOWN_RISK_INITIAL_TRACING_DURATION]
  * 8. [UPDATE_RISK_LEVEL]
- * 9. [CLOSE]
+ * 9. [RISK_CALCULATION_DATE_UPDATE]
+ * 10. [CLOSE]
  *
  * This transaction will queue up any start calls and executes them in the given order (unlike the other defined
  * transactions (e.g. [RetrieveDiagnosisKeysTransaction]). This is necessary in order to respond to various trigger
@@ -152,6 +154,9 @@ object RiskLevelTransaction : Transaction() {
         /** Update and persist the Risk Level Score with the calculated score */
         UPDATE_RISK_LEVEL,
 
+        /** Update of the Date to reflect a complete Transaction State */
+        RISK_CALCULATION_DATE_UPDATE,
+
         /** Transaction Closure */
         CLOSE
     }
@@ -170,6 +175,9 @@ object RiskLevelTransaction : Transaction() {
     /** atomic reference for the rollback value for the last calculated risk level score */
     private val lastCalculatedRiskLevelScoreForRollback = AtomicReference<RiskLevel>()
 
+    /** atomic reference for the rollback value for date of last risk level calculation */
+    private val lastCalculatedRiskLevelDate = AtomicReference<Long>()
+
     /** initiates the transaction. This suspend function guarantees a successful transaction once completed. */
     suspend fun start() = lockAndExecute {
         /****************************************************
@@ -220,6 +228,7 @@ object RiskLevelTransaction : Transaction() {
         if (result == UNDETERMINED) {
             lastCalculatedRiskLevelScoreForRollback.set(RiskLevelRepository.getLastCalculatedScore())
             executeUpdateRiskLevelScore(LOW_LEVEL_RISK)
+            executeRiskLevelCalculationDateUpdate()
             executeClose()
             return@lockAndExecute
         } else {
@@ -233,6 +242,9 @@ object RiskLevelTransaction : Transaction() {
             if (UPDATE_RISK_LEVEL.isInStateStack()) {
                 updateRiskLevelScore(lastCalculatedRiskLevelScoreForRollback.get())
             }
+            if (RISK_CALCULATION_DATE_UPDATE.isInStateStack()) {
+                LocalData.lastTimeRiskLevelCalculation(lastCalculatedRiskLevelDate.get())
+            }
         } catch (e: Exception) {
             // We handle every exception through a RollbackException to make sure that a single EntryPoint
             // is available for the caller.
@@ -261,7 +273,10 @@ object RiskLevelTransaction : Transaction() {
         // if there was no key retrieval before, we return no calculation state
         TimeVariables.getLastTimeDiagnosisKeysFromServerFetch()
             ?: return@executeState UNKNOWN_RISK_INITIAL.also {
-                Log.v(TAG, "$transactionId - no last time diagnosis keys from server fetch timestamp was found")
+                Log.v(
+                    TAG,
+                    "$transactionId - no last time diagnosis keys from server fetch timestamp was found"
+                )
             }
 
         Log.v(TAG, "$transactionId - CHECK_UNKNOWN_RISK_INITIAL_NO_KEYS not applicable")
@@ -314,15 +329,16 @@ object RiskLevelTransaction : Transaction() {
     /**
      * Executes the [RETRIEVE_EXPOSURE_SUMMARY] Transaction State
      */
-    private suspend fun executeRetrieveExposureSummary(): ExposureSummary = executeState(RETRIEVE_EXPOSURE_SUMMARY) {
-        val lastExposureSummary = getLastExposureSummary() ?: getNewExposureSummary()
+    private suspend fun executeRetrieveExposureSummary(): ExposureSummary =
+        executeState(RETRIEVE_EXPOSURE_SUMMARY) {
+            val lastExposureSummary = getLastExposureSummary() ?: getNewExposureSummary()
 
-        return@executeState lastExposureSummary.also {
-            // todo remove after testing sessions
-            recordedTransactionValuesForTestingOnly.exposureSummary = it
-            Log.v(TAG, "$transactionId - get the exposure summary for further calculation")
+            return@executeState lastExposureSummary.also {
+                // todo remove after testing sessions
+                recordedTransactionValuesForTestingOnly.exposureSummary = it
+                Log.v(TAG, "$transactionId - get the exposure summary for further calculation")
+            }
         }
-    }
 
     /**
      * Executes the [CHECK_INCREASED_RISK] Transaction State
@@ -412,6 +428,7 @@ object RiskLevelTransaction : Transaction() {
     private suspend fun executeClose() = executeState(CLOSE) {
         Log.v(TAG, "$transactionId - transaction will close")
         lastCalculatedRiskLevelScoreForRollback.set(null)
+        lastCalculatedRiskLevelDate.set(null)
     }
 
     /****************************************************
@@ -435,6 +452,8 @@ object RiskLevelTransaction : Transaction() {
             )
             lastCalculatedRiskLevelScoreForRollback.set(RiskLevelRepository.getLastCalculatedScore())
             executeUpdateRiskLevelScore(riskLevel)
+            lastCalculatedRiskLevelDate.set(LocalData.lastTimeRiskLevelCalculation())
+            executeRiskLevelCalculationDateUpdate()
             executeClose()
             return true
         }
@@ -522,4 +541,14 @@ object RiskLevelTransaction : Transaction() {
             Log.v(TAG, "$transactionId - generated new exposure summary with $googleToken")
         }
     }
+
+    /**
+     * Executes the CALCULATION_DATE_UPDATE Transaction State
+     */
+    private suspend fun executeRiskLevelCalculationDateUpdate() {
+        val currentDate = System.currentTimeMillis()
+        executeState(RISK_CALCULATION_DATE_UPDATE) {
+            LocalData.lastTimeRiskLevelCalculation(currentDate)
+        }
+    }
 }
diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml
index e24cb16b0..27ba1c703 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -85,6 +85,10 @@
         <xliff:g id="preference">preference_risk_level_score_successful</xliff:g>
     </string>
     <!-- NOTR -->
+    <string name="preference_timestamp_risk_level_calculation">
+        <xliff:g id="preference">preference_timestamp_risk_level_calculation</xliff:g>
+    </string>
+    <!-- NOTR -->
     <string name="preference_test_guid">
         <xliff:g id="preference">preference_test_guid</xliff:g>
     </string>
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt
index e38eecbc2..550851bd0 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/transaction/RiskLevelTransactionTest.kt
@@ -51,12 +51,15 @@ class RiskLevelTransactionTest {
         mockkObject(ExposureSummaryRepository.Companion)
         mockkObject(RiskLevel.Companion)
 
+
         every { ExposureSummaryRepository.getExposureSummaryRepository() } returns esRepositoryMock
 
         every { RiskLevelRepository.getLastCalculatedScore() } returns UNDETERMINED
 
         every { RiskLevelRepository.setRiskLevelScore(any()) } just Runs
         every { RiskLevel.riskLevelChangedBetweenLowAndHigh(any(), any()) } returns false
+        every { LocalData.lastTimeRiskLevelCalculation() } returns System.currentTimeMillis()
+        every { LocalData.lastTimeRiskLevelCalculation(any()) } just Runs
     }
 
     /** Test case for [NO_CALCULATION_POSSIBLE_TRACING_OFF] */
@@ -79,6 +82,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](testRiskLevel)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
@@ -110,6 +114,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](testRiskLevel)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
@@ -153,6 +158,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](testRiskLevel)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
@@ -209,6 +215,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](testRiskLevel)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
@@ -271,6 +278,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](testRiskLevel)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
@@ -332,6 +340,7 @@ class RiskLevelTransactionTest {
                 RiskLevelTransaction["isValidResult"](UNDETERMINED)
 
                 RiskLevelRepository.setRiskLevelScore(testRiskLevel)
+                RiskLevelTransaction["executeRiskLevelCalculationDateUpdate"]()
                 RiskLevelTransaction["executeClose"]()
             }
         }
-- 
GitLab