diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt
index e3b014dea87c13623842857fbe2667f4ab791b85..d6c542fe27fa24962e8e1f7cecd46ba6dfde2302 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt
@@ -5,7 +5,6 @@ import android.os.Bundle
 import android.view.View
 import android.widget.RadioButton
 import android.widget.RadioGroup
-import android.widget.Toast
 import androidx.core.app.ShareCompat
 import androidx.core.content.FileProvider
 import androidx.core.view.ViewCompat
@@ -13,6 +12,7 @@ import androidx.core.view.children
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.navArgs
+import com.google.android.material.snackbar.Snackbar
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentTestRiskLevelCalculationBinding
 import de.rki.coronawarnapp.storage.TestSettings
@@ -59,12 +59,9 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le
         binding.buttonClearDiagnosisKeyCache.setOnClickListener { vm.clearKeyCache() }
         binding.buttonResetRiskLevel.setOnClickListener { vm.resetRiskLevel() }
         binding.buttonExposureWindowsShare.setOnClickListener { vm.shareExposureWindows() }
-        vm.riskLevelResetEvent.observe2(this) {
-            Toast.makeText(
-                requireContext(), "Reset done, please fetch diagnosis keys from server again",
-                Toast.LENGTH_SHORT
-            ).show()
-        }
+
+        vm.dataResetEvent.observe2(this) { Snackbar.make(requireView(), it, Snackbar.LENGTH_SHORT).show() }
+
         vm.additionalRiskCalcInfo.observe2(this) {
             binding.labelRiskAdditionalInfo.text = it
         }
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
index ca4ed7c6094acda5e0f635abeb35af7304ece19e..f69b6f8899cb4818d826257f270bb7d210844324 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
@@ -10,16 +10,15 @@ import com.squareup.inject.assisted.AssistedInject
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
 import de.rki.coronawarnapp.appconfig.ConfigData
 import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask
+import de.rki.coronawarnapp.diagnosiskeys.download.KeyPackageSyncSettings
 import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
-import de.rki.coronawarnapp.exception.ExceptionCategory
-import de.rki.coronawarnapp.exception.reporting.report
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.latestSubmission
 import de.rki.coronawarnapp.risk.RiskLevelTask
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TimeVariables
 import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
-import de.rki.coronawarnapp.storage.AppDatabase
-import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.storage.SubmissionRepository
 import de.rki.coronawarnapp.storage.TestSettings
 import de.rki.coronawarnapp.task.TaskController
@@ -32,21 +31,17 @@ import de.rki.coronawarnapp.util.NetworkRequestWrapper.Companion.withSuccess
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
 import de.rki.coronawarnapp.util.di.AppContext
-import de.rki.coronawarnapp.util.security.SecurityHelper
 import de.rki.coronawarnapp.util.ui.SingleLiveEvent
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
 import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.firstOrNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.sample
-import kotlinx.coroutines.withContext
 import org.joda.time.Instant
 import org.joda.time.format.DateTimeFormat
 import timber.log.Timber
 import java.io.File
-import java.util.Date
 import java.util.concurrent.TimeUnit
 
 class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
@@ -60,7 +55,9 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     tracingCardStateProvider: TracingCardStateProvider,
     private val riskLevelStorage: RiskLevelStorage,
     private val testSettings: TestSettings,
-    private val timeStamper: TimeStamper
+    private val timeStamper: TimeStamper,
+    private val exposureDetectionTracker: ExposureDetectionTracker,
+    private val keyPackageSyncSettings: KeyPackageSyncSettings
 ) : CWAViewModel(
     dispatcherProvider = dispatcherProvider
 ) {
@@ -78,7 +75,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         Timber.d("Example arg: %s", exampleArg)
     }
 
-    val riskLevelResetEvent = SingleLiveEvent<Unit>()
+    val dataResetEvent = SingleLiveEvent<String>()
     val shareFileEvent = SingleLiveEvent<File>()
 
     val showRiskStatusCard = SubmissionRepository.deviceUIStateFlow.map {
@@ -151,8 +148,8 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
 
     val additionalRiskCalcInfo = combine(
         riskLevelStorage.riskLevelResults,
-        LocalData.lastTimeDiagnosisKeysFromServerFetchFlow()
-    ) { riskLevelResults, lastTimeDiagnosisKeysFromServerFetch ->
+        exposureDetectionTracker.latestSubmission()
+    ) { riskLevelResults, latestSubmission ->
 
         val (latestCalc, latestSuccessfulCalc) = riskLevelResults.tryLatestResultsWithDefaults()
 
@@ -162,7 +159,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
             riskLevelLastSuccessfulCalculated = latestSuccessfulCalc.riskState,
             matchedKeyCount = latestCalc.matchedKeyCount,
             daysSinceLastExposure = latestCalc.daysWithEncounters,
-            lastTimeDiagnosisKeysFromServerFetch = lastTimeDiagnosisKeysFromServerFetch
+            lastKeySubmission = latestSubmission?.startedAt
         )
     }.asLiveData()
 
@@ -172,13 +169,13 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         riskLevelLastSuccessfulCalculated: RiskState,
         matchedKeyCount: Int,
         daysSinceLastExposure: Int,
-        lastTimeDiagnosisKeysFromServerFetch: Date?
+        lastKeySubmission: Instant?
     ): String = StringBuilder()
         .appendLine("Risk Level: $riskLevel")
         .appendLine("Last successful Risk Level: $riskLevelLastSuccessfulCalculated")
         .appendLine("Matched key count: $matchedKeyCount")
         .appendLine("Days since last Exposure: $daysSinceLastExposure days")
-        .appendLine("Last Time Server Fetch: ${lastTimeDiagnosisKeysFromServerFetch?.time?.let { Instant.ofEpochMilli(it) }}")
+        .appendLine("Last key submission: $lastKeySubmission")
         .appendLine("Tracing Duration: ${TimeUnit.MILLISECONDS.toDays(TimeVariables.getTimeActiveTracingDuration())} days")
         .appendLine("Tracing Duration in last 14 days: ${TimeVariables.getActiveTracingDaysInRetentionPeriod()} days")
         .appendLine("Last time risk level calculation $lastTimeRiskLevelCalculation")
@@ -210,24 +207,8 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     fun resetRiskLevel() {
         Timber.d("Resetting risk level")
         launch {
-            withContext(Dispatchers.IO) {
-                try {
-                    // Preference reset
-                    SecurityHelper.resetSharedPrefs()
-                    // Database Reset
-                    AppDatabase.reset(context)
-                    // Export File Reset
-                    keyCacheRepository.clear()
-
-                    riskLevelStorage.clear()
-
-                    LocalData.lastTimeDiagnosisKeysFromServerFetch(null)
-                } catch (e: Exception) {
-                    e.report(ExceptionCategory.INTERNAL)
-                }
-            }
-            taskController.submit(DefaultTaskRequest(RiskLevelTask::class))
-            riskLevelResetEvent.postValue(Unit)
+            riskLevelStorage.clear()
+            dataResetEvent.postValue("Risk level calculation related data reset.")
         }
     }
 
@@ -257,7 +238,13 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
 
     fun clearKeyCache() {
         Timber.d("Clearing key cache")
-        launch { keyCacheRepository.clear() }
+        launch {
+            keyCacheRepository.clear()
+            keyPackageSyncSettings.clear()
+            exposureDetectionTracker.clear()
+
+            dataResetEvent.postValue("Download & Submission related data reset.")
+        }
     }
 
     fun selectFakeExposureWindowMode(newMode: TestSettings.FakeExposureWindowTypes) {
diff --git a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml
index 1e6d0bf1b5e927068a3b9b833f1473cffb91eaee..b11dc97e7c883c490333a4e70fde9e1bc1c03323 100644
--- a/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml
+++ b/Corona-Warn-App/src/deviceForTesters/res/layout/fragment_test_risk_level_calculation.xml
@@ -34,13 +34,13 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:orientation="vertical">
-            
+
             <LinearLayout
                 android:id="@+id/environment_container"
                 style="@style/card"
                 android:layout_width="match_parent"
-                android:orientation="vertical"
-                android:layout_height="wrap_content">
+                android:layout_height="wrap_content"
+                android:orientation="vertical">
 
                 <TextView
                     android:id="@+id/fake_windows_title"
@@ -50,11 +50,11 @@
                     android:text="Fake exposure windows" />
 
                 <TextView
-                    android:layout_width="match_parent"
                     style="@style/TextAppearance.AppCompat.Caption"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
                     android:layout_marginTop="@dimen/spacing_tiny"
-                    android:text="Takes effect the next time `ExposureNotificationClient.exposureWindows` is called, i.e. on risk level calculation."
-                    android:layout_height="wrap_content" />
+                    android:text="Takes effect the next time `ExposureNotificationClient.exposureWindows` is called, i.e. on risk level calculation." />
 
                 <RadioGroup
                     android:id="@+id/fake_windows_toggle_group"
@@ -83,28 +83,48 @@
             </FrameLayout>
 
             <Button
-                android:id="@+id/button_retrieve_diagnosis_keys"
+                android:id="@+id/button_calculate_risk_level"
                 style="@style/buttonPrimary"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Retrieve Diagnosis Keys" />
+                android:text="Calculate Risk Level" />
+            <TextView
+                style="@style/TextAppearance.AppCompat.Caption"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Start the task that gets the latest exposure windows and calculates a current risk state." />
 
             <Button
-                android:id="@+id/button_calculate_risk_level"
+                android:id="@+id/button_reset_risk_level"
                 style="@style/buttonPrimary"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Calculate Risk Level" />
+                android:text="Reset Risk Level" />
+
+            <TextView
+                style="@style/TextAppearance.AppCompat.Caption"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Delete the all stored calculated risk level results." />
 
             <Button
-                android:id="@+id/button_reset_risk_level"
+                android:id="@+id/button_retrieve_diagnosis_keys"
                 style="@style/buttonPrimary"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Reset Risk Level" />
+                android:text="Download Diagnosis Keys" />
+
+            <TextView
+                style="@style/TextAppearance.AppCompat.Caption"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Start the task syncs the local diagnosis key cache with the server and submits them to the exposure notification framework for detection (if constraints allow). " />
 
             <Button
                 android:id="@+id/button_clear_diagnosis_key_cache"
@@ -112,7 +132,13 @@
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                android:text="Clear Diagnosis-Key cache" />
+                android:text="Reset Diagnosis-Keys" />
+            <TextView
+                style="@style/TextAppearance.AppCompat.Caption"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="@dimen/spacing_tiny"
+                android:text="Restore download task conditions to initial state. Remove cached keys, delete last download logs, reset tracked exposure detections. " />
 
             <TextView
                 android:id="@+id/label_aggregated_risk_result_title"
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 8f34bcc8ced3d56d3c19ba2269e5a02e4be119b4..cd5bc298faa82904ddbf2c2610ed954ed4a98750 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
@@ -9,7 +9,6 @@ import de.rki.coronawarnapp.nearby.ENFClient
 import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient
 import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection
 import de.rki.coronawarnapp.risk.RollbackItem
-import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.task.Task
 import de.rki.coronawarnapp.task.TaskCancellationException
 import de.rki.coronawarnapp.task.TaskFactory
@@ -109,12 +108,6 @@ class DownloadDiagnosisKeysTask @Inject constructor(
             )
             Timber.tag(TAG).d("Diagnosis Keys provided (success=%s)", isSubmissionSuccessful)
 
-            // EXPOSUREAPP-3878 write timestamp immediately after submission,
-            // so that progress observers can rely on a clean app state
-            if (isSubmissionSuccessful) {
-                saveTimestamp(currentDate, rollbackItems)
-            }
-
             internalProgress.send(Progress.ApiSubmissionFinished)
 
             return object : Task.Result {}
@@ -159,18 +152,6 @@ class DownloadDiagnosisKeysTask @Inject constructor(
         }
     }
 
-    private fun saveTimestamp(
-        currentDate: Date,
-        rollbackItems: MutableList<RollbackItem>
-    ) {
-        val lastFetchDateForRollback = LocalData.lastTimeDiagnosisKeysFromServerFetch()
-        rollbackItems.add {
-            LocalData.lastTimeDiagnosisKeysFromServerFetch(lastFetchDateForRollback)
-        }
-        Timber.tag(TAG).d("dateUpdate(currentDate=%s)", currentDate)
-        LocalData.lastTimeDiagnosisKeysFromServerFetch(currentDate)
-    }
-
     private fun rollback(rollbackItems: MutableList<RollbackItem>) {
         try {
             Timber.tag(TAG).d("Initiate Rollback")
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a05ccdf39d2d6e6ce63cd0b147d4c2f2ce89abfb
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensions.kt
@@ -0,0 +1,22 @@
+package de.rki.coronawarnapp.nearby.modules.detectiontracker
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.map
+
+suspend fun ExposureDetectionTracker.lastSubmission(
+    onlyFinished: Boolean = true
+): TrackedExposureDetection? = calculations
+    .first().values
+    .filter { it.isSuccessful || !onlyFinished }
+    .maxByOrNull { it.startedAt }
+
+fun ExposureDetectionTracker.latestSubmission(
+    onlySuccessful: Boolean = true
+): Flow<TrackedExposureDetection?> = calculations
+    .map { entries ->
+        entries.values.filter { it.isSuccessful || !onlySuccessful }
+    }
+    .map { detections ->
+        detections.maxByOrNull { it.startedAt }
+    }
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 326f0579081966e92e4e4aeaf4a0394e81fe2974..1c2adaaf16b39eb53644cb8744ce02ec6f49a55e 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
@@ -4,12 +4,10 @@ import android.content.SharedPreferences
 import androidx.core.content.edit
 import de.rki.coronawarnapp.CoronaWarnApplication
 import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.util.preferences.createFlowPreference
 import de.rki.coronawarnapp.util.security.SecurityHelper.globalEncryptedSharedPreferencesInstance
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.map
-import java.util.Date
+import timber.log.Timber
 
 /**
  * LocalData is responsible for all access to the shared preferences. Each preference is accessible
@@ -306,28 +304,6 @@ object LocalData {
             .edit(commit = true) { putBoolean(PREFERENCE_HAS_RISK_STATUS_LOWERED, value) }
             .also { isUserToBeNotifiedOfLoweredRiskLevelFlowInternal.value = value }
 
-    /****************************************************
-     * SERVER FETCH DATA
-     ****************************************************/
-
-    private val dateMapperForFetchTime: (Long) -> Date? = {
-        if (it != 0L) Date(it) else null
-    }
-
-    private val lastTimeDiagnosisKeysFetchedFlowPref by lazy {
-        getSharedPreferenceInstance()
-            .createFlowPreference<Long>(key = "preference_timestamp_diagnosis_keys_fetch", 0L)
-    }
-
-    fun lastTimeDiagnosisKeysFromServerFetchFlow() = lastTimeDiagnosisKeysFetchedFlowPref.flow
-        .map { dateMapperForFetchTime(it) }
-
-    fun lastTimeDiagnosisKeysFromServerFetch() =
-        dateMapperForFetchTime(lastTimeDiagnosisKeysFetchedFlowPref.value)
-
-    fun lastTimeDiagnosisKeysFromServerFetch(value: Date?) =
-        lastTimeDiagnosisKeysFetchedFlowPref.update { value?.time ?: 0L }
-
     /****************************************************
      * SETTINGS DATA
      ****************************************************/
@@ -583,6 +559,6 @@ object LocalData {
         }
 
     fun clear() {
-        lastTimeDiagnosisKeysFetchedFlowPref.update { 0L }
+        Timber.w("LocalData.clear()")
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt
index 8174877c2a3ff71247ee328dbc671add9d8d321e..e107f9754d38e32023ecfdab4cb7a2ce6d525eb0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TracingRepository.kt
@@ -4,6 +4,8 @@ import android.content.Context
 import de.rki.coronawarnapp.diagnosiskeys.download.DownloadDiagnosisKeysTask
 import de.rki.coronawarnapp.nearby.ENFClient
 import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.lastSubmission
 import de.rki.coronawarnapp.risk.RiskLevelTask
 import de.rki.coronawarnapp.risk.TimeVariables.getActiveTracingDaysInRetentionPeriod
 import de.rki.coronawarnapp.task.TaskController
@@ -24,7 +26,6 @@ import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 import org.joda.time.Duration
 import timber.log.Timber
-import java.util.Date
 import java.util.NoSuchElementException
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -43,11 +44,10 @@ class TracingRepository @Inject constructor(
     @AppScope private val scope: CoroutineScope,
     private val taskController: TaskController,
     enfClient: ENFClient,
-    private val timeStamper: TimeStamper
+    private val timeStamper: TimeStamper,
+    private val exposureDetectionTracker: ExposureDetectionTracker
 ) {
 
-    val lastTimeDiagnosisKeysFetched: Flow<Date?> = LocalData.lastTimeDiagnosisKeysFromServerFetchFlow()
-
     private val internalActiveTracingDaysInRetentionPeriod = MutableStateFlow(0L)
     val activeTracingDaysInRetentionPeriod: Flow<Long> = internalActiveTracingDaysInRetentionPeriod
 
@@ -121,15 +121,15 @@ class TracingRepository @Inject constructor(
         // model the keys are only fetched on button press of the user
         val isBackgroundJobEnabled = ConnectivityHelper.autoModeEnabled(context)
 
-        val wasNotYetFetched = LocalData.lastTimeDiagnosisKeysFromServerFetch() == null
-
         Timber.tag(TAG).v("Network is enabled $isNetworkEnabled")
         Timber.tag(TAG).v("Background jobs are enabled $isBackgroundJobEnabled")
-        Timber.tag(TAG).v("Was not yet fetched from server $wasNotYetFetched")
 
         if (isNetworkEnabled && isBackgroundJobEnabled) {
             scope.launch {
-                if (wasNotYetFetched || downloadDiagnosisKeysTaskDidNotRunRecently()) {
+                val lastSubmission = exposureDetectionTracker.lastSubmission(onlyFinished = false)
+                Timber.tag(TAG).v("Last submission was %s", lastSubmission)
+
+                if (lastSubmission == null || downloadDiagnosisKeysTaskDidNotRunRecently()) {
                     Timber.tag(TAG).v("Start the fetching and submitting of the diagnosis keys")
 
                     taskController.submitBlocking(
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt
index 3737a45265d7cdf50261cb7e7cfbe6030ac999c8..a2f71aeb12586afed13237c5b390e337bcbb5e91 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardState.kt
@@ -16,7 +16,6 @@ import de.rki.coronawarnapp.ui.tracing.common.BaseTracingState
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDate
 import org.joda.time.Instant
 import org.joda.time.format.DateTimeFormat
-import java.util.Date
 
 @Suppress("TooManyFunctions")
 data class TracingCardState(
@@ -27,7 +26,7 @@ data class TracingCardState(
     val daysWithEncounters: Int,
     val lastEncounterAt: Instant?,
     val activeTracingDays: Long,
-    val lastTimeDiagnosisKeysFetched: Date?,
+    val lastExposureDetectionTime: Instant?,
     override val isManualKeyRetrievalEnabled: Boolean,
     override val showDetails: Boolean = false
 ) : BaseTracingState() {
@@ -166,10 +165,10 @@ data class TracingCardState(
         else -> ""
     }
 
-    private fun formatRelativeDateTimeString(c: Context, date: Date): CharSequence? =
+    private fun formatRelativeDateTimeString(c: Context, date: Instant): CharSequence? =
         DateUtils.getRelativeDateTimeString(
             c,
-            date.time,
+            date.millis,
             DateUtils.DAY_IN_MILLIS,
             DateUtils.DAY_IN_MILLIS * 2,
             0
@@ -183,10 +182,10 @@ data class TracingCardState(
      */
     fun getTimeFetched(c: Context): String {
         if (isTracingOff()) {
-            return if (lastTimeDiagnosisKeysFetched != null) {
+            return if (lastExposureDetectionTime != null) {
                 c.getString(
                     R.string.risk_card_body_time_fetched,
-                    formatRelativeDateTimeString(c, lastTimeDiagnosisKeysFetched)
+                    formatRelativeDateTimeString(c, lastExposureDetectionTime)
                 )
             } else {
                 c.getString(R.string.risk_card_body_not_yet_fetched)
@@ -194,10 +193,10 @@ data class TracingCardState(
         }
         return when (riskState) {
             LOW_RISK, INCREASED_RISK -> {
-                if (lastTimeDiagnosisKeysFetched != null) {
+                if (lastExposureDetectionTime != null) {
                     c.getString(
                         R.string.risk_card_body_time_fetched,
-                        formatRelativeDateTimeString(c, lastTimeDiagnosisKeysFetched)
+                        formatRelativeDateTimeString(c, lastExposureDetectionTime)
                     )
                 } else {
                     c.getString(R.string.risk_card_body_not_yet_fetched)
@@ -206,10 +205,10 @@ data class TracingCardState(
             CALCULATION_FAILED -> {
                 when (lastSuccessfulRiskState) {
                     LOW_RISK, INCREASED_RISK -> {
-                        if (lastTimeDiagnosisKeysFetched != null) {
+                        if (lastExposureDetectionTime != null) {
                             c.getString(
                                 R.string.risk_card_body_time_fetched,
-                                formatRelativeDateTimeString(c, lastTimeDiagnosisKeysFetched)
+                                formatRelativeDateTimeString(c, lastExposureDetectionTime)
                             )
                         } else {
                             c.getString(R.string.risk_card_body_not_yet_fetched)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateProvider.kt
index 1f8e51ee7dc53a2824e8f09ea6d83f56faa31ab6..5fa194430033216fce6d63dc6848fe1a2bf89053 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateProvider.kt
@@ -1,6 +1,8 @@
 package de.rki.coronawarnapp.ui.tracing.card
 
 import dagger.Reusable
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.latestSubmission
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.storage.TracingRepository
@@ -20,7 +22,8 @@ class TracingCardStateProvider @Inject constructor(
     tracingStatus: GeneralTracingStatus,
     backgroundModeStatus: BackgroundModeStatus,
     tracingRepository: TracingRepository,
-    riskLevelStorage: RiskLevelStorage
+    riskLevelStorage: RiskLevelStorage,
+    exposureDetectionTracker: ExposureDetectionTracker
 ) {
 
     val state: Flow<TracingCardState> = combine(
@@ -36,8 +39,8 @@ class TracingCardStateProvider @Inject constructor(
         tracingRepository.activeTracingDaysInRetentionPeriod.onEach {
             Timber.v("activeTracingDaysInRetentionPeriod: $it")
         },
-        tracingRepository.lastTimeDiagnosisKeysFetched.onEach {
-            Timber.v("lastTimeDiagnosisKeysFetched: $it")
+        exposureDetectionTracker.latestSubmission().onEach {
+            Timber.v("latestSubmission: $it")
         },
         backgroundModeStatus.isAutoModeEnabled.onEach {
             Timber.v("isAutoModeEnabled: $it")
@@ -46,7 +49,7 @@ class TracingCardStateProvider @Inject constructor(
         tracingProgress,
         riskLevelResults,
         activeTracingDaysInRetentionPeriod,
-        lastTimeDiagnosisKeysFetched,
+        latestSubmission,
         isBackgroundJobEnabled ->
 
         val (
@@ -61,7 +64,7 @@ class TracingCardStateProvider @Inject constructor(
             riskState = latestCalc.riskState,
             tracingProgress = tracingProgress,
             lastSuccessfulRiskState = latestSuccessfulCalc.riskState,
-            lastTimeDiagnosisKeysFetched = lastTimeDiagnosisKeysFetched,
+            lastExposureDetectionTime = latestSubmission?.startedAt,
             daysWithEncounters = latestCalc.daysWithEncounters,
             lastEncounterAt = latestCalc.lastRiskEncounterAt,
             activeTracingDays = activeTracingDaysInRetentionPeriod,
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensionsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensionsTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..18702738c8cc0c58bde2bf7263f94895c3387d80
--- /dev/null
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTrackerExtensionsTest.kt
@@ -0,0 +1,94 @@
+package de.rki.coronawarnapp.nearby.modules.detectiontracker
+
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.every
+import io.mockk.impl.annotations.MockK
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.test.runBlockingTest
+import org.joda.time.Instant
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+import java.util.UUID
+
+class ExposureDetectionTrackerExtensionsTest : BaseTest() {
+
+    @MockK lateinit var tracker: ExposureDetectionTracker
+
+    private val fakeCalculations: MutableStateFlow<Map<String, TrackedExposureDetection>> = MutableStateFlow(emptyMap())
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+        every { tracker.calculations } returns fakeCalculations
+    }
+
+    @AfterEach
+    fun teardown() {
+    }
+
+    private fun createFakeCalculation(
+        startedAt: Instant,
+        result: TrackedExposureDetection.Result? = TrackedExposureDetection.Result.NO_MATCHES
+    ) = TrackedExposureDetection(
+        identifier = UUID.randomUUID().toString(),
+        startedAt = startedAt,
+        finishedAt = if (result != null) startedAt.plus(100) else null,
+        result = result
+    )
+
+    @Test
+    fun `last submission`() {
+        val tr1 = createFakeCalculation(startedAt = Instant.EPOCH)
+        val tr2 = createFakeCalculation(startedAt = Instant.EPOCH.plus(1))
+        val tr3 = createFakeCalculation(startedAt = Instant.EPOCH.plus(2), result = null)
+        fakeCalculations.value = mapOf(
+            tr1.identifier to tr1,
+            tr2.identifier to tr2,
+            tr3.identifier to tr3,
+        )
+        runBlockingTest {
+            tracker.lastSubmission(onlyFinished = false) shouldBe tr3
+            tracker.lastSubmission(onlyFinished = true) shouldBe tr2
+        }
+    }
+
+    @Test
+    fun `last submission on empty data`() {
+        runBlockingTest {
+            tracker.lastSubmission(onlyFinished = false) shouldBe null
+            tracker.lastSubmission(onlyFinished = true) shouldBe null
+        }
+    }
+
+    @Test
+    fun `latest submission`() {
+        val tr1 = createFakeCalculation(startedAt = Instant.EPOCH)
+        val tr2 = createFakeCalculation(startedAt = Instant.EPOCH.plus(1))
+        val tr3 = createFakeCalculation(startedAt = Instant.EPOCH.plus(2), result = null)
+        fakeCalculations.value = mapOf(
+            tr1.identifier to tr1,
+            tr2.identifier to tr2,
+            tr3.identifier to tr3,
+        )
+        runBlockingTest {
+            tracker.latestSubmission(onlySuccessful = false).first() shouldBe tr3
+            tracker.latestSubmission(onlySuccessful = true).first() shouldBe tr2
+        }
+    }
+
+    @Test
+    fun `latest submission on empty data`() = runBlockingTest {
+        tracker.latestSubmission(onlySuccessful = false).first() shouldBe null
+        tracker.latestSubmission(onlySuccessful = true).first() shouldBe null
+
+        val tr1 = createFakeCalculation(startedAt = Instant.EPOCH)
+        fakeCalculations.value = mapOf(tr1.identifier to tr1)
+
+        tracker.latestSubmission(onlySuccessful = false).first() shouldBe tr1
+        tracker.latestSubmission(onlySuccessful = true).first() shouldBe tr1
+    }
+}
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateTest.kt
index 52e718e79e61f7f33daea36d9556ba134e6b9851..dd06def65231a1bba01790341fbe48f662a6edb4 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/ui/tracing/card/TracingCardStateTest.kt
@@ -22,7 +22,6 @@ import org.junit.jupiter.api.AfterEach
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import testhelpers.BaseTest
-import java.util.Date
 
 class TracingCardStateTest : BaseTest() {
 
@@ -46,7 +45,7 @@ class TracingCardStateTest : BaseTest() {
         daysWithEncounters: Int = 0,
         lastEncounterAt: Instant? = null,
         activeTracingDaysInRetentionPeriod: Long = 0,
-        lastTimeDiagnosisKeysFetched: Date? = mockk(),
+        lastExposureDetectionTime: Instant? = mockk(),
         isBackgroundJobEnabled: Boolean = false
     ) = TracingCardState(
         tracingStatus = tracingStatus,
@@ -56,7 +55,7 @@ class TracingCardStateTest : BaseTest() {
         daysWithEncounters = daysWithEncounters,
         lastEncounterAt = lastEncounterAt,
         activeTracingDays = activeTracingDaysInRetentionPeriod,
-        lastTimeDiagnosisKeysFetched = lastTimeDiagnosisKeysFetched,
+        lastExposureDetectionTime = lastExposureDetectionTime,
         isManualKeyRetrievalEnabled = !isBackgroundJobEnabled
     )
 
@@ -251,11 +250,11 @@ class TracingCardStateTest : BaseTest() {
 
     @Test
     fun `text for last time diagnosis keys were fetched`() {
-        val date = Date()
+        val date = Instant()
         createInstance(
             riskState = INCREASED_RISK,
             lastSuccessfulRiskState = LOW_RISK,
-            lastTimeDiagnosisKeysFetched = date
+            lastExposureDetectionTime = date
         ).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
@@ -264,7 +263,7 @@ class TracingCardStateTest : BaseTest() {
         createInstance(
             riskState = CALCULATION_FAILED,
             lastSuccessfulRiskState = LOW_RISK,
-            lastTimeDiagnosisKeysFetched = date
+            lastExposureDetectionTime = date
         ).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
@@ -273,7 +272,7 @@ class TracingCardStateTest : BaseTest() {
         createInstance(
             riskState = CALCULATION_FAILED,
             lastSuccessfulRiskState = LOW_RISK,
-            lastTimeDiagnosisKeysFetched = date
+            lastExposureDetectionTime = date
         ).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
@@ -282,36 +281,36 @@ class TracingCardStateTest : BaseTest() {
         createInstance(
             riskState = LOW_RISK,
             lastSuccessfulRiskState = LOW_RISK,
-            lastTimeDiagnosisKeysFetched = date
+            lastExposureDetectionTime = date
         ).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
         }
 
-        createInstance(riskState = INCREASED_RISK, lastTimeDiagnosisKeysFetched = date).apply {
+        createInstance(riskState = INCREASED_RISK, lastExposureDetectionTime = date).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
         }
 
-        createInstance(riskState = CALCULATION_FAILED, lastTimeDiagnosisKeysFetched = date).apply {
+        createInstance(riskState = CALCULATION_FAILED, lastExposureDetectionTime = date).apply {
             getTimeFetched(context) shouldBe ""
         }
 
-        createInstance(riskState = LOW_RISK, lastTimeDiagnosisKeysFetched = date).apply {
+        createInstance(riskState = LOW_RISK, lastExposureDetectionTime = date).apply {
             getTimeFetched(context)
             verify { context.getString(eq(R.string.risk_card_body_time_fetched), any()) }
         }
 
-        createInstance(riskState = INCREASED_RISK, lastTimeDiagnosisKeysFetched = null).apply {
+        createInstance(riskState = INCREASED_RISK, lastExposureDetectionTime = null).apply {
             getTimeFetched(context)
             verify { context.getString(R.string.risk_card_body_not_yet_fetched) }
         }
 
-        createInstance(riskState = CALCULATION_FAILED, lastTimeDiagnosisKeysFetched = null).apply {
+        createInstance(riskState = CALCULATION_FAILED, lastExposureDetectionTime = null).apply {
             getTimeFetched(context) shouldBe ""
         }
 
-        createInstance(riskState = LOW_RISK, lastTimeDiagnosisKeysFetched = null).apply {
+        createInstance(riskState = LOW_RISK, lastExposureDetectionTime = null).apply {
             getTimeFetched(context)
             verify { context.getString(R.string.risk_card_body_not_yet_fetched) }
         }