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 298354d970bbf4dc537899eeeb5d4f55efdfbc81..a6719b32124e2e25c4c78600f41ab1c030eee8a3 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
@@ -83,11 +83,12 @@ class DownloadDiagnosisKeysTask @Inject constructor(
 
             if (wasLastDetectionPerformedRecently(now, exposureConfig, trackedExposureDetections)) {
                 // At most one detection every 6h
+                Timber.tag(TAG).i("task aborted, because detection was performed recently")
                 return object : Task.Result {}
             }
 
             if (hasRecentDetectionAndNoNewFiles(now, keySyncResult, trackedExposureDetections)) {
-                //  Last check was within 24h, and there are no new files.
+                Timber.tag(TAG).i("task aborted, last check was within 24h, and there are no new files")
                 return object : Task.Result {}
             }
 
@@ -111,13 +112,14 @@ class DownloadDiagnosisKeysTask @Inject constructor(
             )
             Timber.tag(TAG).d("Diagnosis Keys provided (success=%s, token=%s)", isSubmissionSuccessful, token)
 
-            internalProgress.send(Progress.ApiSubmissionFinished)
-            throwIfCancelled()
-
+            // 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 {}
         } catch (error: Exception) {
             Timber.tag(TAG).e(error)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt
index 46499c31230bf37e06c79f5c124a5e02f9f61215..767788b052571ee50d306089dec02aba0a7513d6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/diagnosiskeys/download/KeyPackageSyncSettings.kt
@@ -1,9 +1,11 @@
 package de.rki.coronawarnapp.diagnosiskeys.download
 
+import android.annotation.SuppressLint
 import android.content.Context
 import com.google.gson.Gson
 import de.rki.coronawarnapp.util.di.AppContext
 import de.rki.coronawarnapp.util.preferences.FlowPreference
+import de.rki.coronawarnapp.util.preferences.clearAndNotify
 import de.rki.coronawarnapp.util.serialization.BaseGson
 import org.joda.time.Instant
 import javax.inject.Inject
@@ -32,6 +34,11 @@ class KeyPackageSyncSettings @Inject constructor(
         writer = FlowPreference.gsonWriter(gson)
     )
 
+    @SuppressLint("ApplySharedPref")
+    fun clear() {
+        prefs.clearAndNotify()
+    }
+
     data class LastDownload(
         val startedAt: Instant,
         val finishedAt: Instant? = null,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
index c5e4b8073deb172dcb4856afb9d2406af38ef94d..b40e1e6d01cea3bac0e0a3e5a38b781ee3c3ef35 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/DefaultExposureDetectionTracker.kt
@@ -134,6 +134,13 @@ class DefaultExposureDetectionTracker @Inject constructor(
         }
     }
 
+    override fun clear() {
+        Timber.i("clear()")
+        detectionStates.updateSafely {
+            emptyMap()
+        }
+    }
+
     companion object {
         private const val TAG = "DefaultExposureDetectionTracker"
         private const val MAX_ENTRY_SIZE = 5
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTracker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTracker.kt
index 4d9bcf0c6e318c96a314d71bdd0050f0ecf2bd88..ee1a302ea67b14b76516cd5c7bb738e22430814a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTracker.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/detectiontracker/ExposureDetectionTracker.kt
@@ -8,4 +8,6 @@ interface ExposureDetectionTracker {
     fun trackNewExposureDetection(identifier: String)
 
     fun finishExposureDetection(identifier: String, result: TrackedExposureDetection.Result)
+
+    fun clear()
 }
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 2e0347c40203980058112cec2e5e6e660055134e..26a4902fceed1cfef67b084fc1a084202bd2d216 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
@@ -726,4 +726,8 @@ object LocalData {
                 putBoolean(PREFERENCE_INTEROPERABILITY_IS_USED_AT_LEAST_ONCE, value)
             }
         }
+
+    fun clear() {
+        lastTimeDiagnosisKeysFetchedFlowPref.update { 0L }
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
index 86690e22520a4689a14f591cf5e5edab61ef853a..fd1064bbfef209742a3e763e9ecd386543311e80 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
@@ -22,8 +22,11 @@ package de.rki.coronawarnapp.util
 import android.annotation.SuppressLint
 import android.content.Context
 import de.rki.coronawarnapp.appconfig.AppConfigProvider
+import de.rki.coronawarnapp.diagnosiskeys.download.KeyPackageSyncSettings
 import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
+import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
 import de.rki.coronawarnapp.storage.AppDatabase
+import de.rki.coronawarnapp.storage.LocalData
 import de.rki.coronawarnapp.storage.RiskLevelRepository
 import de.rki.coronawarnapp.storage.SubmissionRepository
 import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository
@@ -43,7 +46,9 @@ class DataReset @Inject constructor(
     @AppContext private val context: Context,
     private val keyCacheRepository: KeyCacheRepository,
     private val appConfigProvider: AppConfigProvider,
-    private val interoperabilityRepository: InteroperabilityRepository
+    private val interoperabilityRepository: InteroperabilityRepository,
+    private val exposureDetectionTracker: ExposureDetectionTracker,
+    private val keyPackageSyncSettings: KeyPackageSyncSettings
 ) {
 
     private val mutex = Mutex()
@@ -56,6 +61,8 @@ class DataReset @Inject constructor(
         Timber.w("CWA LOCAL DATA DELETION INITIATED.")
         // Database Reset
         AppDatabase.reset(context)
+        // Because LocalData does not behave like a normal shared preference
+        LocalData.clear()
         // Shared Preferences Reset
         SecurityHelper.resetSharedPrefs()
         // Reset the current risk level stored in LiveData
@@ -65,6 +72,8 @@ class DataReset @Inject constructor(
         keyCacheRepository.clear()
         appConfigProvider.clear()
         interoperabilityRepository.clear()
+        exposureDetectionTracker.clear()
+        keyPackageSyncSettings.clear()
         Timber.w("CWA LOCAL DATA DELETION COMPLETED.")
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/FlowPreference.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/FlowPreference.kt
index c64cbedfde89f3c164c26e3eb1ea2204102d2bd3..2e97359373b6de42a333ade72b7b4542c5e42a73 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/FlowPreference.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/FlowPreference.kt
@@ -6,6 +6,7 @@ import com.google.gson.Gson
 import de.rki.coronawarnapp.util.serialization.fromJson
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import timber.log.Timber
 
 class FlowPreference<T> constructor(
     private val preferences: SharedPreferences,
@@ -17,6 +18,21 @@ class FlowPreference<T> constructor(
     private val flowInternal = MutableStateFlow(internalValue)
     val flow: Flow<T> = flowInternal
 
+    private val preferenceChangeListener =
+        SharedPreferences.OnSharedPreferenceChangeListener { changedPrefs, changedKey ->
+            if (changedKey != key) return@OnSharedPreferenceChangeListener
+
+            val newValue = reader(changedPrefs, changedKey)
+            val currentvalue = flowInternal.value
+            if (currentvalue != newValue && flowInternal.compareAndSet(currentvalue, newValue)) {
+                Timber.v("%s:%s changed to %s", changedPrefs, changedKey, newValue)
+            }
+        }
+
+    init {
+        preferences.registerOnSharedPreferenceChangeListener(preferenceChangeListener)
+    }
+
     private var internalValue: T
         get() = reader(preferences, key)
         set(newValue) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/SharedPreferenceExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/SharedPreferenceExtensions.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0469b3840d2b9417a4ad728314f57cfa871a63ec
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/preferences/SharedPreferenceExtensions.kt
@@ -0,0 +1,17 @@
+package de.rki.coronawarnapp.util.preferences
+
+import android.content.SharedPreferences
+import androidx.core.content.edit
+import timber.log.Timber
+
+fun SharedPreferences.clearAndNotify() {
+    val currentKeys = this.all.keys.toSet()
+    Timber.v("%s clearAndNotify(): %s", this, currentKeys)
+    edit {
+        currentKeys.forEach { remove(it) }
+    }
+    // Clear does not notify anyone using registerOnSharedPreferenceChangeListener
+    edit(commit = true) {
+        clear()
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/SecurityHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/SecurityHelper.kt
index 370ab58fd4ce02e2358bc1dd9f66557020403546..c1c145e4965c2cd874b65a4c107f0976e59557dd 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/SecurityHelper.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/security/SecurityHelper.kt
@@ -27,6 +27,7 @@ import androidx.annotation.VisibleForTesting
 import de.rki.coronawarnapp.exception.CwaSecurityException
 import de.rki.coronawarnapp.util.di.AppInjector
 import de.rki.coronawarnapp.util.di.ApplicationComponent
+import de.rki.coronawarnapp.util.preferences.clearAndNotify
 import de.rki.coronawarnapp.util.security.SecurityConstants.CWA_APP_SQLITE_DB_PW
 import de.rki.coronawarnapp.util.security.SecurityConstants.DB_PASSWORD_MAX_LENGTH
 import de.rki.coronawarnapp.util.security.SecurityConstants.DB_PASSWORD_MIN_LENGTH
@@ -80,7 +81,7 @@ object SecurityHelper {
 
     @SuppressLint("ApplySharedPref")
     fun resetSharedPrefs() {
-        globalEncryptedSharedPreferencesInstance.edit().clear().commit()
+        globalEncryptedSharedPreferencesInstance.clearAndNotify()
     }
 
     private fun getStoredDbPassword(): ByteArray? =
diff --git a/Corona-Warn-App/src/test/java/testhelpers/preferences/MockFlowPreference.kt b/Corona-Warn-App/src/test/java/testhelpers/preferences/MockFlowPreference.kt
index 33d46578e1a956c645d549edc3f793d2fc9b27df..0b855e1b9938ed9d47650a26c85ba5bbe9ba4927 100644
--- a/Corona-Warn-App/src/test/java/testhelpers/preferences/MockFlowPreference.kt
+++ b/Corona-Warn-App/src/test/java/testhelpers/preferences/MockFlowPreference.kt
@@ -16,5 +16,6 @@ fun <T> mockFlowPreference(
         val updateCall = arg<(T) -> T>(0)
         flow.value = updateCall(flow.value)
     }
+
     return instance
 }
diff --git a/Corona-Warn-App/src/test/java/testhelpers/preferences/MockSharedPreferences.kt b/Corona-Warn-App/src/test/java/testhelpers/preferences/MockSharedPreferences.kt
index a6294b00f54bcdbc96ab6bd18195389fc1a79e51..c094e560e5401f80bbf7bedff36af3537cc8a67e 100644
--- a/Corona-Warn-App/src/test/java/testhelpers/preferences/MockSharedPreferences.kt
+++ b/Corona-Warn-App/src/test/java/testhelpers/preferences/MockSharedPreferences.kt
@@ -3,6 +3,7 @@ package testhelpers.preferences
 import android.content.SharedPreferences
 
 class MockSharedPreferences : SharedPreferences {
+    private val listeners = mutableListOf<SharedPreferences.OnSharedPreferenceChangeListener>()
     private val dataMap = mutableMapOf<String, Any>()
     val dataMapPeek: Map<String, Any>
         get() = dataMap.toMap()
@@ -36,12 +37,12 @@ class MockSharedPreferences : SharedPreferences {
         dataMap.putAll(newData)
     }
 
-    override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) {
-        throw NotImplementedError()
+    override fun registerOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
+        listeners.add(listener)
     }
 
-    override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener?) {
-        throw NotImplementedError()
+    override fun unregisterOnSharedPreferenceChangeListener(listener: SharedPreferences.OnSharedPreferenceChangeListener) {
+        listeners.remove(listener)
     }
 
     private fun createEditor(