diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigHttpCache.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigHttpCache.kt
new file mode 100644
index 0000000000000000000000000000000000000000..a3aff4add4b1df85c9093fa97efe839f2f57a924
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigHttpCache.kt
@@ -0,0 +1,8 @@
+package de.rki.coronawarnapp.appconfig
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class AppConfigHttpCache
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt
index d742d73703505fd64f5655b46f5e85b7d38f0d0a..9216419bb2197d6e3736d33fe5da863ded200dbf 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigModule.kt
@@ -18,18 +18,25 @@ import javax.inject.Singleton
 @Module
 class AppConfigModule {
 
+    @Singleton
+    @Provides
+    @AppConfigHttpCache
+    fun provideAppConfigCache(
+        @AppContext context: Context
+    ): Cache {
+        val cacheSize = 1 * 1024 * 1024L // 1MB
+        val cacheDir = File(context.cacheDir, "http_app-config")
+        return Cache(cacheDir, cacheSize)
+    }
+
     @Singleton
     @Provides
     fun provideAppConfigApi(
-        @AppContext context: Context,
         @DownloadCDNHttpClient client: OkHttpClient,
         @DownloadCDNServerUrl url: String,
-        gsonConverterFactory: GsonConverterFactory
+        gsonConverterFactory: GsonConverterFactory,
+        @AppConfigHttpCache cache: Cache
     ): AppConfigApiV1 {
-        val cacheSize = 1 * 1024 * 1024L // 1MB
-
-        val cacheDir = File(context.cacheDir, "http_app-config")
-        val cache = Cache(cacheDir, cacheSize)
 
         val cachingClient = client.newBuilder().apply {
             cache(cache)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigProvider.kt
index c4d2d982dee0738961ece46cd1dab7d2ff150c93..4cbedfc9303397c2436237a51cdd51f4a515c854 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigProvider.kt
@@ -8,7 +8,10 @@ import de.rki.coronawarnapp.server.protocols.internal.AppConfig.ApplicationConfi
 import de.rki.coronawarnapp.util.ZipHelper.unzip
 import de.rki.coronawarnapp.util.security.VerificationKeys
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.withContext
+import okhttp3.Cache
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Singleton
@@ -18,9 +21,11 @@ class AppConfigProvider @Inject constructor(
     private val appConfigAPI: Lazy<AppConfigApiV1>,
     private val verificationKeys: VerificationKeys,
     @DownloadCDNHomeCountry private val homeCountry: LocationCode,
-    private val configStorage: AppConfigStorage
+    private val configStorage: AppConfigStorage,
+    @AppConfigHttpCache private val cache: Cache
 ) {
 
+    private val mutex = Mutex()
     private val configApi: AppConfigApiV1
         get() = appConfigAPI.get()
 
@@ -58,7 +63,7 @@ class AppConfigProvider @Inject constructor(
             downloadAppConfig()
         } catch (e: Exception) {
             Timber.w(e, "Failed to download latest AppConfig.")
-            if (configStorage.isAppConfigAvailable) {
+            if (configStorage.isAppConfigAvailable()) {
                 null
             } else {
                 Timber.e("No fallback available, rethrowing!")
@@ -76,12 +81,12 @@ class AppConfigProvider @Inject constructor(
         return newConfigParsed?.also {
             Timber.d("Saving new valid config.")
             Timber.v("New Config.supportedCountries: %s", it.supportedCountriesList)
-            configStorage.appConfigRaw = newConfigRaw
+            configStorage.setAppConfigRaw(newConfigRaw)
         }
     }
 
-    private fun getFallback(): ApplicationConfiguration {
-        val lastValidConfig = tryParseConfig(configStorage.appConfigRaw)
+    private suspend fun getFallback(): ApplicationConfiguration {
+        val lastValidConfig = tryParseConfig(configStorage.getAppConfigRaw())
         return if (lastValidConfig != null) {
             Timber.d("Using fallback AppConfig.")
             lastValidConfig
@@ -91,16 +96,29 @@ class AppConfigProvider @Inject constructor(
         }
     }
 
-    suspend fun getAppConfig(): ApplicationConfiguration = withContext(Dispatchers.IO) {
-        val newAppConfig = getNewAppConfig()
+    suspend fun getAppConfig(): ApplicationConfiguration = mutex.withLock {
+        withContext(Dispatchers.IO) {
 
-        return@withContext if (newAppConfig != null) {
-            newAppConfig
-        } else {
-            Timber.w("No new config available, using last valid.")
-            getFallback()
+            val newAppConfig = getNewAppConfig()
+
+            return@withContext if (newAppConfig != null) {
+                newAppConfig
+            } else {
+                Timber.w("No new config available, using last valid.")
+                getFallback()
+            }
+        }.performSanityChecks()
+    }
+
+    suspend fun clear() = mutex.withLock {
+        withContext(Dispatchers.IO) {
+            configStorage.setAppConfigRaw(null)
+
+            // We are using Dispatchers IO to make it appropriate
+            @Suppress("BlockingMethodInNonBlockingContext")
+            cache.evictAll()
         }
-    }.performSanityChecks()
+    }
 
     private fun ApplicationConfiguration.performSanityChecks(): ApplicationConfiguration {
         var sanityChecked = this
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigStorage.kt
index f81d9bee07200b2d7669e67a0bd1cfe2bdf738c6..54b6351b104cec7b0d98305dee5f7ba040f5572b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigStorage.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/appconfig/AppConfigStorage.kt
@@ -2,6 +2,8 @@ package de.rki.coronawarnapp.appconfig
 
 import android.content.Context
 import de.rki.coronawarnapp.util.di.AppContext
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import timber.log.Timber
 import java.io.File
 import javax.inject.Inject
@@ -13,33 +15,35 @@ class AppConfigStorage @Inject constructor(
 ) {
     private val configDir = File(context.filesDir, "appconfig_storage")
     private val configFile = File(configDir, "appconfig")
+    private val mutex = Mutex()
 
-    val isAppConfigAvailable: Boolean
-        get() = configFile.exists() && configFile.length() > MIN_VALID_CONFIG_BYTES
+    suspend fun isAppConfigAvailable(): Boolean = mutex.withLock {
+        configFile.exists() && configFile.length() > MIN_VALID_CONFIG_BYTES
+    }
+
+    suspend fun getAppConfigRaw(): ByteArray? = mutex.withLock {
+        Timber.v("get() AppConfig")
+        if (!configFile.exists()) return null
+
+        val value = configFile.readBytes()
+        Timber.v("Read AppConfig of size %s and date %s", value.size, configFile.lastModified())
+        return value
+    }
+
+    suspend fun setAppConfigRaw(value: ByteArray?): Unit = mutex.withLock {
+        Timber.v("set(...) AppConfig: %dB", value?.size)
 
-    var appConfigRaw: ByteArray?
-        get() {
-            Timber.v("get() AppConfig")
-            if (!configFile.exists()) return null
+        if (configDir.mkdirs()) Timber.v("Parent folder created.")
 
-            val value = configFile.readBytes()
-            Timber.v("Read AppConfig of size %s and date %s", value.size, configFile.lastModified())
-            return value
+        if (configFile.exists()) {
+            Timber.v("Overwriting %d from %s", configFile.length(), configFile.lastModified())
         }
-        set(value) {
-            Timber.v("set(...) AppConfig: %dB", value?.size)
-
-            if (configDir.mkdirs()) Timber.v("Parent folder created.")
-
-            if (configFile.exists()) {
-                Timber.v("Overwriting %d from %s", configFile.length(), configFile.lastModified())
-            }
-            if (value != null) {
-                configFile.writeBytes(value)
-            } else {
-                configFile.delete()
-            }
+        if (value != null) {
+            configFile.writeBytes(value)
+        } else {
+            configFile.delete()
         }
+    }
 
     companion object {
         // The normal config is ~512B+, we just need to check for a non 0 value, 128 is fine.
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt
index ad835cd205ae1775d8fcb6fcb2bc792a21b75d14..6fc200f81cf687f36fff3e8c57fc50e3009a021e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/RiskLevelRepository.kt
@@ -35,7 +35,7 @@ object RiskLevelRepository {
     /**
      * Resets the data in the [RiskLevelRepository]
      *
-     * @see de.rki.coronawarnapp.util.DataRetentionHelper
+     * @see de.rki.coronawarnapp.util.DataReset
      *
      */
     fun reset() {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt
index 90768259c610867b8e66e7a53e00bb9e25bb501f..81327c8e1182373949179b771c87b4e3936e01dd 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/interoperability/InteroperabilityRepository.kt
@@ -52,4 +52,8 @@ class InteroperabilityRepository @Inject constructor(
             }
         }
     }
+
+    fun clear() {
+        _countryList.postValue(emptyList())
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt
index 83ffa5dff222e9a48d54d4c3700e6c8f1e299e41..af1a59dfd887c171a66c9e901a55a21e049d5e61 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivityModule.kt
@@ -7,6 +7,8 @@ import de.rki.coronawarnapp.ui.interoperability.InteroperabilityConfigurationFra
 import de.rki.coronawarnapp.ui.main.home.HomeFragmentModule
 import de.rki.coronawarnapp.ui.onboarding.OnboardingDeltaInteroperabilityModule
 import de.rki.coronawarnapp.ui.settings.SettingFragmentsModule
+import de.rki.coronawarnapp.ui.settings.SettingsResetFragment
+import de.rki.coronawarnapp.ui.settings.SettingsResetModule
 import de.rki.coronawarnapp.ui.submission.SubmissionFragmentModule
 import de.rki.coronawarnapp.ui.tracing.details.RiskDetailsFragmentModule
 
@@ -29,4 +31,7 @@ abstract class MainActivityModule {
 
     @ContributesAndroidInjector(modules = [InteroperabilityConfigurationFragmentModule::class])
     abstract fun intertopConfigScreen(): InteroperabilityConfigurationFragment
+
+    @ContributesAndroidInjector(modules = [SettingsResetModule::class])
+    abstract fun settingsResetScreen(): SettingsResetFragment
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsEvents.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsEvents.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8f44c39bc291ed4703ceaaeb96257decab7ab936
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsEvents.kt
@@ -0,0 +1,7 @@
+package de.rki.coronawarnapp.ui.settings
+
+sealed class SettingsEvents {
+    object ResetApp : SettingsEvents()
+    object GoBack : SettingsEvents()
+    object GoToOnboarding : SettingsEvents()
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt
index 74bf4859462a9971738756c3583c3ad76a96a30c..f59335d80f368a70ebc9e12254d385fb4cbbe52b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetFragment.kt
@@ -5,45 +5,41 @@ import android.os.Bundle
 import android.view.View
 import android.view.accessibility.AccessibilityEvent
 import androidx.fragment.app.Fragment
-import androidx.lifecycle.lifecycleScope
-import com.google.android.gms.common.api.ApiException
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentSettingsResetBinding
-import de.rki.coronawarnapp.exception.ExceptionCategory
-import de.rki.coronawarnapp.exception.reporting.report
-import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient
 import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.ui.onboarding.OnboardingActivity
-import de.rki.coronawarnapp.util.DataRetentionHelper
 import de.rki.coronawarnapp.util.DialogHelper
+import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.ui.observe2
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
-import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactoryProvider
+import de.rki.coronawarnapp.util.viewmodel.cwaViewModels
+import javax.inject.Inject
 
 /**
  * The user is informed what a reset means and he can perform it.
  *
  */
-class SettingsResetFragment : Fragment(R.layout.fragment_settings_reset) {
-
-    companion object {
-        private val TAG: String? = SettingsResetFragment::class.simpleName
-    }
+class SettingsResetFragment : Fragment(R.layout.fragment_settings_reset), AutoInject {
 
+    @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory
+    private val vm: SettingsResetViewModel by cwaViewModels { viewModelFactory }
     private val binding: FragmentSettingsResetBinding by viewBindingLazy()
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-        binding.settingsResetButtonDelete.setOnClickListener {
-            confirmReset()
-        }
-        binding.settingsResetButtonCancel.setOnClickListener {
-            (activity as MainActivity).goBack()
+        binding.apply {
+            settingsResetButtonDelete.setOnClickListener { vm.resetAllData() }
+            settingsResetButtonCancel.setOnClickListener { vm.goBack() }
+            settingsResetHeader.headerButtonBack.buttonIcon.setOnClickListener { vm.goBack() }
         }
-        binding.settingsResetHeader.headerButtonBack.buttonIcon.setOnClickListener {
-            (activity as MainActivity).goBack()
+        vm.clickEvent.observe2(this) {
+            when (it) {
+                is SettingsEvents.ResetApp -> confirmReset()
+                is SettingsEvents.GoBack -> (activity as MainActivity).goBack()
+                is SettingsEvents.GoToOnboarding -> navigateToOnboarding()
+            }
         }
     }
 
@@ -52,36 +48,11 @@ class SettingsResetFragment : Fragment(R.layout.fragment_settings_reset) {
         binding.settingsResetContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT)
     }
 
-    private fun deleteAllAppContent() {
-        lifecycleScope.launch {
-            try {
-                val isTracingEnabled = InternalExposureNotificationClient.asyncIsEnabled()
-                // only stop tracing if it is currently enabled
-                if (isTracingEnabled) {
-                    InternalExposureNotificationClient.asyncStop()
-                    BackgroundWorkScheduler.stopWorkScheduler()
-                }
-            } catch (apiException: ApiException) {
-                apiException.report(
-                    ExceptionCategory.EXPOSURENOTIFICATION, TAG, null
-                )
-            }
-            withContext(Dispatchers.IO) {
-                deleteLocalAppContent()
-            }
-            navigateToOnboarding()
-        }
-    }
-
     private fun navigateToOnboarding() {
         OnboardingActivity.start(requireContext())
         activity?.finish()
     }
 
-    private fun deleteLocalAppContent() {
-        DataRetentionHelper.clearAllLocalData(requireContext())
-    }
-
     private fun confirmReset() {
         val resetDialog = DialogHelper.DialogInstance(
             requireActivity(),
@@ -89,10 +60,8 @@ class SettingsResetFragment : Fragment(R.layout.fragment_settings_reset) {
             R.string.settings_reset_dialog_body,
             R.string.settings_reset_dialog_button_confirm,
             R.string.settings_reset_dialog_button_cancel,
-            true,
-            {
-                deleteAllAppContent()
-            }
+            cancelable = true,
+            positiveButtonFunction = vm::deleteAllAppContent
         )
 
         DialogHelper.showDialog(resetDialog).apply {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetModule.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ae89d82f47353d33aaf9f10a84681f0dd1c1383a
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetModule.kt
@@ -0,0 +1,22 @@
+package de.rki.coronawarnapp.ui.settings
+
+import dagger.Binds
+import dagger.Module
+import dagger.android.ContributesAndroidInjector
+import dagger.multibindings.IntoMap
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelFactory
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModelKey
+
+@Module
+abstract class SettingsResetModule {
+    @Binds
+    @IntoMap
+    @CWAViewModelKey(SettingsResetViewModel::class)
+    abstract fun settingsResetVM(
+        factory: SettingsResetViewModel.Factory
+    ): CWAViewModelFactory<out CWAViewModel>
+
+    @ContributesAndroidInjector
+    abstract fun settingsResetFragment(): SettingsResetFragment
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2b559cd3a7e0e3f5ff919070de566610b31811bc
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsResetViewModel.kt
@@ -0,0 +1,56 @@
+package de.rki.coronawarnapp.ui.settings
+
+import com.google.android.gms.common.api.ApiException
+import com.squareup.inject.assisted.AssistedInject
+import de.rki.coronawarnapp.exception.ExceptionCategory
+import de.rki.coronawarnapp.exception.reporting.report
+import de.rki.coronawarnapp.nearby.InternalExposureNotificationClient
+import de.rki.coronawarnapp.ui.SingleLiveEvent
+import de.rki.coronawarnapp.util.DataReset
+import de.rki.coronawarnapp.util.coroutine.DispatcherProvider
+import de.rki.coronawarnapp.util.viewmodel.CWAViewModel
+import de.rki.coronawarnapp.util.viewmodel.SimpleCWAViewModelFactory
+import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
+
+class SettingsResetViewModel @AssistedInject constructor(
+    dispatcherProvider: DispatcherProvider,
+    private val dataReset: DataReset
+) : CWAViewModel(dispatcherProvider = dispatcherProvider) {
+
+    val clickEvent: SingleLiveEvent<SettingsEvents> = SingleLiveEvent()
+
+    fun resetAllData() {
+        clickEvent.postValue(SettingsEvents.ResetApp)
+    }
+
+    fun goBack() {
+        clickEvent.postValue(SettingsEvents.GoBack)
+    }
+
+    fun deleteAllAppContent() {
+        launch {
+            try {
+                val isTracingEnabled = InternalExposureNotificationClient.asyncIsEnabled()
+                // only stop tracing if it is currently enabled
+                if (isTracingEnabled) {
+                    InternalExposureNotificationClient.asyncStop()
+                    BackgroundWorkScheduler.stopWorkScheduler()
+                }
+            } catch (apiException: ApiException) {
+                apiException.report(
+                    ExceptionCategory.EXPOSURENOTIFICATION, TAG, null
+                )
+            }
+
+            dataReset.clearAllLocalData()
+            clickEvent.postValue(SettingsEvents.GoToOnboarding)
+        }
+    }
+
+    companion object {
+        private val TAG: String? = SettingsResetFragment::class.simpleName
+    }
+
+    @AssistedInject.Factory
+    interface Factory : SimpleCWAViewModelFactory<SettingsResetViewModel>
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataRetentionHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
similarity index 72%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataRetentionHelper.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
index 1ce93fc10fafda64feb5c1adc377f8eaa848e38a..b0aae90040901a60c89c9ff37e0f68e07aacc983 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataRetentionHelper.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/DataReset.kt
@@ -21,25 +21,37 @@ package de.rki.coronawarnapp.util
 
 import android.annotation.SuppressLint
 import android.content.Context
+import de.rki.coronawarnapp.appconfig.AppConfigProvider
+import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
 import de.rki.coronawarnapp.storage.AppDatabase
 import de.rki.coronawarnapp.storage.RiskLevelRepository
-import de.rki.coronawarnapp.util.di.AppInjector
+import de.rki.coronawarnapp.storage.interoperability.InteroperabilityRepository
+import de.rki.coronawarnapp.util.di.AppContext
 import de.rki.coronawarnapp.util.security.SecurityHelper
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.sync.Mutex
+import kotlinx.coroutines.sync.withLock
 import timber.log.Timber
+import javax.inject.Inject
+import javax.inject.Singleton
 
 /**
  * Helper for supplying functionality regarding Data Retention
  */
-object DataRetentionHelper {
-    private val TAG: String? = DataRetentionHelper::class.simpleName
+@Singleton
+class DataReset @Inject constructor(
+    @AppContext private val context: Context,
+    private val keyCacheRepository: KeyCacheRepository,
+    private val appConfigProvider: AppConfigProvider,
+    private val interoperabilityRepository: InteroperabilityRepository
+) {
 
+    private val mutex = Mutex()
     /**
      * Deletes all data known to the Application
      *
      */
     @SuppressLint("ApplySharedPref") // We need a commit here to ensure consistency
-    fun clearAllLocalData(context: Context) {
+    suspend fun clearAllLocalData() = mutex.withLock {
         Timber.w("CWA LOCAL DATA DELETION INITIATED.")
         // Database Reset
         AppDatabase.reset(context)
@@ -47,9 +59,9 @@ object DataRetentionHelper {
         SecurityHelper.resetSharedPrefs()
         // Reset the current risk level stored in LiveData
         RiskLevelRepository.reset()
-        // Export File Reset
-        // TODO runBlocking, but also all of the above is BLOCKING and should be called more nicely
-        runBlocking { AppInjector.component.keyCacheRepository.clear() }
+        keyCacheRepository.clear()
+        appConfigProvider.clear()
+        interoperabilityRepository.clear()
         Timber.w("CWA LOCAL DATA DELETION COMPLETED.")
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt
index 24c628143e943212db418d207f749ea758e383d5..2e2d2cb5a31ecd7bfd98cc95dc61793c22341a88 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigApiTest.kt
@@ -8,6 +8,7 @@ import io.mockk.MockKAnnotations
 import io.mockk.clearAllMocks
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
 import kotlinx.coroutines.runBlocking
 import okhttp3.ConnectionSpec
 import okhttp3.mockwebserver.MockResponse
@@ -31,6 +32,7 @@ class AppConfigApiTest : BaseIOTest() {
     private val cacheFiles = File(testDir, "cache")
     private val cacheDir = File(cacheFiles, "http_app-config")
 
+
     @BeforeEach
     fun setup() {
         MockKAnnotations.init(this)
@@ -59,14 +61,15 @@ class AppConfigApiTest : BaseIOTest() {
             .connectionSpecs(listOf(ConnectionSpec.CLEARTEXT, ConnectionSpec.MODERN_TLS))
             .build()
 
+        val cache = AppConfigModule().provideAppConfigCache(context)
         return AppConfigModule().provideAppConfigApi(
-            context = context,
             client = cdnHttpClient,
             url = serverAddress,
-            gsonConverterFactory = gsonConverterFactory
+            gsonConverterFactory = gsonConverterFactory,
+            cache = cache
         )
     }
-
+    
     @Test
     fun `application config download`() {
         val api = createAPI()
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt
index e744b2fc360cc3214b41a46243014e1b33ef77b6..972136da0c94f4ce8b65306cbfec4c9102c445d9 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigProviderTest.kt
@@ -7,10 +7,11 @@ import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.clearAllMocks
 import io.mockk.coEvery
+import io.mockk.coVerify
 import io.mockk.every
 import io.mockk.impl.annotations.MockK
+import io.mockk.mockk
 import io.mockk.verify
-import kotlinx.coroutines.runBlocking
 import okhttp3.ResponseBody.Companion.toResponseBody
 import okio.ByteString.Companion.decodeHex
 import org.junit.jupiter.api.AfterEach
@@ -38,9 +39,9 @@ class AppConfigProviderTest : BaseIOTest() {
         testDir.mkdirs()
         testDir.exists() shouldBe true
 
-        every { appConfigStorage.isAppConfigAvailable } answers { mockConfigStorage != null }
-        every { appConfigStorage.appConfigRaw } answers { mockConfigStorage }
-        every { appConfigStorage.appConfigRaw = any() } answers { mockConfigStorage = arg(0) }
+        coEvery { appConfigStorage.isAppConfigAvailable() } answers { mockConfigStorage != null }
+        coEvery { appConfigStorage.getAppConfigRaw() } answers { mockConfigStorage }
+        coEvery { appConfigStorage.setAppConfigRaw(any()) } answers { mockConfigStorage = arg(0) }
     }
 
     @AfterEach
@@ -55,27 +56,26 @@ class AppConfigProviderTest : BaseIOTest() {
         appConfigAPI = { api },
         verificationKeys = verificationKeys,
         homeCountry = homeCountry,
-        configStorage = appConfigStorage
+        configStorage = appConfigStorage,
+        cache = mockk()
     )
 
     @Test
-    fun `application config download`() {
+    suspend fun `application config download`() {
         coEvery { api.getApplicationConfiguration("DE") } returns APPCONFIG_BUNDLE.toResponseBody()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns false
 
         val downloadServer = createDownloadServer()
 
-        runBlocking {
-            val rawConfig = downloadServer.downloadAppConfig()
-            rawConfig shouldBe APPCONFIG_RAW.toByteArray()
-        }
+        val rawConfig = downloadServer.downloadAppConfig()
+        rawConfig shouldBe APPCONFIG_RAW.toByteArray()
 
         verify(exactly = 1) { verificationKeys.hasInvalidSignature(any(), any()) }
     }
 
     @Test
-    fun `application config data is faulty`() {
+    suspend fun `application config data is faulty`() {
         coEvery { api.getApplicationConfiguration("DE") } returns "123ABC".decodeHex()
             .toResponseBody()
 
@@ -83,15 +83,13 @@ class AppConfigProviderTest : BaseIOTest() {
 
         val downloadServer = createDownloadServer()
 
-        runBlocking {
-            shouldThrow<ApplicationConfigurationInvalidException> {
-                downloadServer.downloadAppConfig()
-            }
+        shouldThrow<ApplicationConfigurationInvalidException> {
+            downloadServer.downloadAppConfig()
         }
     }
 
     @Test
-    fun `application config verification fails`() {
+    suspend fun `application config verification fails`() {
         coEvery { api.getApplicationConfiguration("DE") } returns APPCONFIG_BUNDLE
             .toResponseBody()
 
@@ -99,126 +97,113 @@ class AppConfigProviderTest : BaseIOTest() {
 
         val downloadServer = createDownloadServer()
 
-        runBlocking {
-            shouldThrow<ApplicationConfigurationCorruptException> {
-                downloadServer.downloadAppConfig()
-            }
+        shouldThrow<ApplicationConfigurationCorruptException> {
+            downloadServer.downloadAppConfig()
         }
     }
 
     @Test
-    fun `successful download stores new config`() {
+    suspend fun `successful download stores new config`() {
         coEvery { api.getApplicationConfiguration("DE") } returns APPCONFIG_BUNDLE
             .toResponseBody()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns false
 
         val downloadServer = createDownloadServer()
+        downloadServer.getAppConfig()
 
-        runBlocking {
-            downloadServer.getAppConfig()
-
-            mockConfigStorage shouldBe APPCONFIG_RAW.toByteArray()
-            verify { appConfigStorage.appConfigRaw = APPCONFIG_RAW.toByteArray() }
-        }
+        mockConfigStorage shouldBe APPCONFIG_RAW.toByteArray()
+        coVerify { appConfigStorage.setAppConfigRaw(APPCONFIG_RAW.toByteArray()) }
     }
 
     @Test
-    fun `failed download doesn't overwrite valid config`() {
+    suspend fun `failed download doesn't overwrite valid config`() {
         mockConfigStorage = APPCONFIG_RAW.toByteArray()
         coEvery { api.getApplicationConfiguration("DE") } throws IOException()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns false
 
-        runBlocking {
-            createDownloadServer().getAppConfig()
-        }
+        createDownloadServer().getAppConfig()
 
-        verify(exactly = 0) { appConfigStorage.appConfigRaw = any() }
+        coVerify(exactly = 0) { appConfigStorage.setAppConfigRaw(any()) }
         mockConfigStorage shouldBe APPCONFIG_RAW.toByteArray()
     }
 
     @Test
-    fun `failed verification doesn't overwrite valid config`() {
+    suspend fun `failed verification doesn't overwrite valid config`() {
         mockConfigStorage = APPCONFIG_RAW.toByteArray()
         coEvery { api.getApplicationConfiguration("DE") } returns APPCONFIG_BUNDLE
             .toResponseBody()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns true
 
-        runBlocking {
-            createDownloadServer().getAppConfig()
-        }
+        createDownloadServer().getAppConfig()
 
-        verify(exactly = 0) { appConfigStorage.appConfigRaw = any() }
+        coVerify(exactly = 0) { appConfigStorage.setAppConfigRaw(any()) }
         mockConfigStorage shouldBe APPCONFIG_RAW.toByteArray()
     }
 
     @Test
-    fun `fallback to last config if verification fails`() {
+    suspend fun `fallback to last config if verification fails`() {
         mockConfigStorage = APPCONFIG_RAW.toByteArray()
 
         coEvery { api.getApplicationConfiguration("DE") } returns "123ABC".decodeHex()
             .toResponseBody()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } throws Exception()
-        runBlocking {
-            createDownloadServer().getAppConfig().minRiskScore shouldBe 11
-        }
+        createDownloadServer().getAppConfig().minRiskScore shouldBe 11
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns true
-        runBlocking {
-            createDownloadServer().getAppConfig().minRiskScore shouldBe 11
-        }
+        createDownloadServer().getAppConfig().minRiskScore shouldBe 11
     }
 
     @Test
-    fun `fallback to last config if download fails`() {
+    suspend fun `fallback to last config if download fails`() {
         mockConfigStorage = APPCONFIG_RAW.toByteArray()
 
         coEvery { api.getApplicationConfiguration("DE") } throws Exception()
 
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns false
 
-        runBlocking {
-            createDownloadServer().getAppConfig().minRiskScore shouldBe 11
-        }
+        createDownloadServer().getAppConfig().minRiskScore shouldBe 11
     }
 
     // Because the UI requires this to detect when to show alternative UI elements
     @Test
-    fun `if supportedCountryList is empty, we do not insert DE as fallback`() {
+    suspend fun `if supportedCountryList is empty, we do not insert DE as fallback`() {
         coEvery { api.getApplicationConfiguration("DE") } returns APPCONFIG_BUNDLE.toResponseBody()
         every { verificationKeys.hasInvalidSignature(any(), any()) } returns false
 
-        runBlocking {
-            createDownloadServer().getAppConfig().supportedCountriesList shouldBe emptyList()
-        }
+        createDownloadServer().getAppConfig().supportedCountriesList shouldBe emptyList()
     }
 
     companion object {
         private val APPCONFIG_BUNDLE =
-            ("504b0304140008080800856b22510000000000000000000000000a0000006578706f72742e62696ee3e016" +
-                "f2e552e662f6f10f97e05792ca28292928b6d2d72f2f2fd74bce2fcacf4b2c4f2ccad34b2c28e0" +
-                "52e362f1f074f710e097f0c0a74e2a854b80835180498259814583d580cd82dd814390010c3c1d" +
-                "a4b8141835180d182d181d181561825a021cac02ac12ac0aac40f5ac16ac0eac86102913072b3e" +
-                "01460946841e47981e25192e160e73017b21214e88d0077ba8250fec1524b5a4b8b8b858043824" +
-                "98849804588578806a19255884c02400504b0708df2c788daf000000f1000000504b0304140008" +
-                "080800856b22510000000000000000000000000a0000006578706f72742e736967018a0075ff0a" +
-                "87010a380a1864652e726b692e636f726f6e617761726e6170702d6465761a0276312203323632" +
-                "2a13312e322e3834302e31303034352e342e332e321001180122473045022100cf32ff24ea18a1" +
-                "ffcc7ff4c9fe8d1808cecbc5a37e3e1d4c9ce682120450958c022064bf124b6973a9b510a43d47" +
-                "9ff93e0ef97a5b893c7af4abc4a8d399969cd8a0504b070813c517c68f0000008a000000504b01" +
-                "021400140008080800856b2251df2c788daf000000f10000000a00000000000000000000000000" +
-                "000000006578706f72742e62696e504b01021400140008080800856b225113c517c68f0000008a" +
-                "0000000a00000000000000000000000000e70000006578706f72742e736967504b050600000000" +
-                "0200020070000000ae0100000000").decodeHex()
+            (
+                "504b0304140008080800856b22510000000000000000000000000a0000006578706f72742e62696ee3e016" +
+                    "f2e552e662f6f10f97e05792ca28292928b6d2d72f2f2fd74bce2fcacf4b2c4f2ccad34b2c28e0" +
+                    "52e362f1f074f710e097f0c0a74e2a854b80835180498259814583d580cd82dd814390010c3c1d" +
+                    "a4b8141835180d182d181d181561825a021cac02ac12ac0aac40f5ac16ac0eac86102913072b3e" +
+                    "01460946841e47981e25192e160e73017b21214e88d0077ba8250fec1524b5a4b8b8b858043824" +
+                    "98849804588578806a19255884c02400504b0708df2c788daf000000f1000000504b0304140008" +
+                    "080800856b22510000000000000000000000000a0000006578706f72742e736967018a0075ff0a" +
+                    "87010a380a1864652e726b692e636f726f6e617761726e6170702d6465761a0276312203323632" +
+                    "2a13312e322e3834302e31303034352e342e332e321001180122473045022100cf32ff24ea18a1" +
+                    "ffcc7ff4c9fe8d1808cecbc5a37e3e1d4c9ce682120450958c022064bf124b6973a9b510a43d47" +
+                    "9ff93e0ef97a5b893c7af4abc4a8d399969cd8a0504b070813c517c68f0000008a000000504b01" +
+                    "021400140008080800856b2251df2c788daf000000f10000000a00000000000000000000000000" +
+                    "000000006578706f72742e62696e504b01021400140008080800856b225113c517c68f0000008a" +
+                    "0000000a00000000000000000000000000e70000006578706f72742e736967504b050600000000" +
+                    "0200020070000000ae0100000000"
+                ).decodeHex()
         private val APPCONFIG_RAW =
-            ("080b124d0a230a034c4f57180f221a68747470733a2f2f777777" +
-                "2e636f726f6e617761726e2e6170700a260a0448494748100f1848221a68747470733a2f2f7777772e636f7" +
-                "26f6e617761726e2e6170701a640a10080110021803200428053006380740081100000000000049401a0a20" +
-                "0128013001380140012100000000000049402a1008051005180520052805300538054005310000000000003" +
-                "4403a0e1001180120012801300138014001410000000000004940221c0a040837103f121209000000000000" +
-                "f03f11000000000000e03f20192a1a0a0a0a041008180212021005120c0a0408011804120408011804").decodeHex()
+            (
+                "080b124d0a230a034c4f57180f221a68747470733a2f2f777777" +
+                    "2e636f726f6e617761726e2e6170700a260a0448494748100f1848221a68747470733a2f2f7777772e636f7" +
+                    "26f6e617761726e2e6170701a640a10080110021803200428053006380740081100000000000049401a0a20" +
+                    "0128013001380140012100000000000049402a1008051005180520052805300538054005310000000000003" +
+                    "4403a0e1001180120012801300138014001410000000000004940221c0a040837103f121209000000000000" +
+                    "f03f11000000000000e03f20192a1a0a0a0a041008180212021005120c0a0408011804120408011804"
+                ).decodeHex()
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigStorageTest.kt
index dd637fbb7269ad1f404b5c58282f9a6f405c1a8b..aea45f221b950b4b2ac503079adb1c0691004e64 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigStorageTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/appconfig/AppConfigStorageTest.kt
@@ -37,51 +37,51 @@ class AppConfigStorageTest : BaseIOTest() {
     private fun createStorage() = AppConfigStorage(context)
 
     @Test
-    fun `config availability is determined by file existence and min size`() {
+    suspend fun `config availability is determined by file existence and min size`() {
         storageDir.mkdirs()
         val storage = createStorage()
-        storage.isAppConfigAvailable shouldBe false
+        storage.isAppConfigAvailable() shouldBe false
         configPath.createNewFile()
-        storage.isAppConfigAvailable shouldBe false
+        storage.isAppConfigAvailable() shouldBe false
 
         configPath.writeBytes(ByteArray(128) { 1 })
-        storage.isAppConfigAvailable shouldBe false
+        storage.isAppConfigAvailable() shouldBe false
 
         configPath.writeBytes(ByteArray(129) { 1 })
-        storage.isAppConfigAvailable shouldBe true
+        storage.isAppConfigAvailable() shouldBe true
     }
 
     @Test
-    fun `simple read and write config`() {
+    suspend fun `simple read and write config`() {
         configPath.exists() shouldBe false
         val storage = createStorage()
         configPath.exists() shouldBe false
 
-        storage.appConfigRaw = testByteArray
+        storage.setAppConfigRaw(testByteArray)
 
         configPath.exists() shouldBe true
         configPath.readBytes() shouldBe testByteArray
 
-        storage.appConfigRaw shouldBe testByteArray
+        storage.getAppConfigRaw() shouldBe testByteArray
     }
 
     @Test
-    fun `nulling and overwriting`() {
+    suspend fun `nulling and overwriting`() {
         val storage = createStorage()
         configPath.exists() shouldBe false
 
-        storage.appConfigRaw shouldBe null
-        storage.appConfigRaw = null
+        storage.getAppConfigRaw() shouldBe null
+        storage.setAppConfigRaw(null)
         configPath.exists() shouldBe false
 
-        storage.appConfigRaw shouldBe null
-        storage.appConfigRaw = testByteArray
-        storage.appConfigRaw shouldBe testByteArray
+        storage.getAppConfigRaw() shouldBe null
+        storage.setAppConfigRaw(testByteArray)
+        storage.getAppConfigRaw() shouldBe testByteArray
         configPath.exists() shouldBe true
         configPath.readBytes() shouldBe testByteArray
 
-        storage.appConfigRaw = null
-        storage.appConfigRaw shouldBe null
+        storage.setAppConfigRaw(null)
+        storage.getAppConfigRaw() shouldBe null
         configPath.exists() shouldBe false
     }
 }