diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt b/Corona-Warn-App/src/device/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt
similarity index 100%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt
rename to Corona-Warn-App/src/device/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt
diff --git a/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-increased-risk-random.json b/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-increased-risk-random.json
new file mode 100644
index 0000000000000000000000000000000000000000..544b74346319309e35780eccde28274cb969fd73
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-increased-risk-random.json
@@ -0,0 +1,690 @@
+[
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 2,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 299,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 2,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 2,
+    "scanInstances": [
+      {
+        "minAttenuation": 73,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 73,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 2,
+    "scanInstances": [
+      {
+        "minAttenuation": 72,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 72,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 2,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 1,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 299,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 1,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 3,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 2,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 4,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 1,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 2,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 3,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 2,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 4,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 1,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 2,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 2,
+    "calibrationConfidence": 0,
+    "infectiousness": 1,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 4,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 420,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 2,
+    "scanInstances": []
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 0,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 70,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  },
+  {
+    "ageInDays": 1,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 3,
+    "scanInstances": [
+      {
+        "minAttenuation": 70,
+        "secondsSinceLastScan": 0,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 70,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 70,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-lowrisk-random.json b/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-lowrisk-random.json
new file mode 100644
index 0000000000000000000000000000000000000000..675781ac432d5542f2d43c257997c715d78f3f6d
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/assets/exposure-windows-lowrisk-random.json
@@ -0,0 +1,38 @@
+[
+  {
+    "ageInDays": 1,
+    "reportType": 2,
+    "infectiousness": 2,
+    "calibrationConfidence": 0,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "typicalAttenuation": 25,
+        "secondsSinceLastScan": 300
+      },
+      {
+        "minAttenuation": 30,
+        "typicalAttenuation": 25,
+        "secondsSinceLastScan": 300
+      }
+    ]
+  },
+  {
+    "ageInDays": 4,
+    "calibrationConfidence": 0,
+    "infectiousness": 2,
+    "reportType": 1,
+    "scanInstances": [
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      },
+      {
+        "minAttenuation": 30,
+        "secondsSinceLastScan": 300,
+        "typicalAttenuation": 25
+      }
+    ]
+  }
+]
\ No newline at end of file
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..dd7306b0b1f347ba356fbbbca101f5abd95dc827
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/DefaultExposureWindowProvider.kt
@@ -0,0 +1,31 @@
+package de.rki.coronawarnapp.nearby.modules.exposurewindow
+
+import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
+import com.google.android.gms.nearby.exposurenotification.ExposureWindow
+import de.rki.coronawarnapp.storage.TestSettings
+import javax.inject.Inject
+import javax.inject.Singleton
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+import kotlin.coroutines.suspendCoroutine
+
+@Singleton
+class DefaultExposureWindowProvider @Inject constructor(
+    private val client: ExposureNotificationClient,
+    private val testSettings: TestSettings,
+    private val fakeExposureWindowProvider: FakeExposureWindowProvider
+) : ExposureWindowProvider {
+
+    override suspend fun exposureWindows(): List<ExposureWindow> = suspendCoroutine { cont ->
+        when (val fakeSetting = testSettings.fakeExposureWindows.value) {
+            TestSettings.FakeExposureWindowTypes.DISABLED -> {
+                client.exposureWindows
+                    .addOnSuccessListener { cont.resume(it) }
+                    .addOnFailureListener { cont.resumeWithException(it) }
+            }
+            else -> {
+                fakeExposureWindowProvider.getExposureWindows(fakeSetting).let { cont.resume(it) }
+            }
+        }
+    }
+}
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/FakeExposureWindowProvider.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/FakeExposureWindowProvider.kt
new file mode 100644
index 0000000000000000000000000000000000000000..ce213e8e03432873ea37ac457889cc65c92da657
--- /dev/null
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/FakeExposureWindowProvider.kt
@@ -0,0 +1,63 @@
+package de.rki.coronawarnapp.nearby.modules.exposurewindow
+
+import android.content.Context
+import com.google.android.gms.nearby.exposurenotification.ExposureWindow
+import com.google.android.gms.nearby.exposurenotification.ScanInstance
+import com.google.gson.Gson
+import com.google.gson.annotations.SerializedName
+import dagger.Reusable
+import de.rki.coronawarnapp.storage.TestSettings.FakeExposureWindowTypes
+import de.rki.coronawarnapp.util.TimeStamper
+import de.rki.coronawarnapp.util.di.AppContext
+import de.rki.coronawarnapp.util.serialization.BaseGson
+import de.rki.coronawarnapp.util.serialization.fromJson
+import org.joda.time.Duration
+import javax.inject.Inject
+
+@Reusable
+class FakeExposureWindowProvider @Inject constructor(
+    @AppContext val context: Context,
+    @BaseGson val gson: Gson,
+    val timeStamper: TimeStamper
+) {
+
+    fun getExposureWindows(testSettings: FakeExposureWindowTypes): List<ExposureWindow> {
+        val jsonInput = when (testSettings) {
+            FakeExposureWindowTypes.INCREASED_RISK_DEFAULT -> "exposure-windows-increased-risk-random.json"
+            FakeExposureWindowTypes.LOW_RISK_DEFAULT -> "exposure-windows-lowrisk-random.json"
+            else -> throw NotImplementedError()
+        }.let { context.assets.open(it) }.readBytes().toString(Charsets.UTF_8)
+        val jsonWindows: List<JsonWindow> = gson.fromJson(jsonInput)
+        val nowUTC = timeStamper.nowUTC
+        return jsonWindows.map { jWindow ->
+            ExposureWindow.Builder().apply {
+                setDateMillisSinceEpoch(nowUTC.minus(Duration.standardDays(jWindow.ageInDays.toLong())).millis)
+                setCalibrationConfidence(jWindow.calibrationConfidence)
+                setInfectiousness(jWindow.infectiousness)
+                setReportType(jWindow.reportType)
+
+                jWindow.scanInstances.map { jScanInstance ->
+                    ScanInstance.Builder().apply {
+                        setMinAttenuationDb(jScanInstance.minAttenuation)
+                        setSecondsSinceLastScan(jScanInstance.secondsSinceLastScan)
+                        setTypicalAttenuationDb(jScanInstance.typicalAttenuation)
+                    }.build()
+                }.let { setScanInstances(it) }
+            }.build()
+        }
+    }
+}
+
+private data class JsonScanInstance(
+    @SerializedName("minAttenuation") val minAttenuation: Int,
+    @SerializedName("secondsSinceLastScan") val secondsSinceLastScan: Int,
+    @SerializedName("typicalAttenuation") val typicalAttenuation: Int
+)
+
+private data class JsonWindow(
+    @SerializedName("ageInDays") val ageInDays: Int,
+    @SerializedName("calibrationConfidence") val calibrationConfidence: Int,
+    @SerializedName("infectiousness") val infectiousness: Int,
+    @SerializedName("reportType") val reportType: Int,
+    @SerializedName("scanInstances") val scanInstances: List<JsonScanInstance>
+)
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 b0e40407ce9b0895cc283865456e43f7303882d0..7087a044d87e8f1341a2ec5b785883fa78d8bc5e 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
@@ -2,12 +2,17 @@ package de.rki.coronawarnapp.test.risklevel.ui
 
 import android.os.Bundle
 import android.view.View
+import android.widget.RadioButton
+import android.widget.RadioGroup
 import android.widget.Toast
+import androidx.core.view.ViewCompat
+import androidx.core.view.children
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
 import androidx.navigation.fragment.navArgs
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentTestRiskLevelCalculationBinding
+import de.rki.coronawarnapp.storage.TestSettings
 import de.rki.coronawarnapp.test.menu.ui.TestMenuItem
 import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel
 import de.rki.coronawarnapp.util.di.AutoInject
@@ -79,6 +84,37 @@ class TestRiskLevelCalculationFragment : Fragment(R.layout.fragment_test_risk_le
         vm.backendParameters.observe2(this) {
             binding.labelBackendParameters.text = it
         }
+
+        vm.fakeWindowsState.observe2(this) { currentType ->
+            binding.apply {
+                if (fakeWindowsToggleGroup.childCount != TestSettings.FakeExposureWindowTypes.values().size) {
+                    fakeWindowsToggleGroup.removeAllViews()
+                    TestSettings.FakeExposureWindowTypes.values().forEach { type ->
+                        RadioButton(requireContext()).apply {
+                            id = ViewCompat.generateViewId()
+                            text = type.name
+                            layoutParams = RadioGroup.LayoutParams(
+                                RadioGroup.LayoutParams.MATCH_PARENT,
+                                RadioGroup.LayoutParams.WRAP_CONTENT
+                            )
+                            fakeWindowsToggleGroup.addView(this)
+                        }
+                    }
+                }
+
+                fakeWindowsToggleGroup.children.forEach {
+                    it as RadioButton
+                    it.isChecked = it.text == currentType.name
+                }
+            }
+        }
+        binding.fakeWindowsToggleGroup.apply {
+            setOnCheckedChangeListener { group, checkedId ->
+                val chip = group.findViewById<RadioButton>(checkedId)
+                if (!chip.isPressed) return@setOnCheckedChangeListener
+                vm.selectFakeExposureWindowMode(TestSettings.FakeExposureWindowTypes.valueOf(chip.text.toString()))
+            }
+        }
     }
 
     companion object {
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 35c5e63f9b8c3f63e4c06d605c7b1ec59e867b08..dd93a0e154d8e7ece32be12a0984a78c197c3560 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
@@ -21,6 +21,7 @@ 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.TestSettings
 import de.rki.coronawarnapp.task.TaskController
 import de.rki.coronawarnapp.task.common.DefaultTaskRequest
 import de.rki.coronawarnapp.task.submitBlocking
@@ -51,11 +52,14 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     private val keyCacheRepository: KeyCacheRepository,
     private val appConfigProvider: AppConfigProvider,
     tracingCardStateProvider: TracingCardStateProvider,
-    private val exposureResultStore: ExposureResultStore
+    private val exposureResultStore: ExposureResultStore,
+    private val testSettings: TestSettings
 ) : CWAViewModel(
     dispatcherProvider = dispatcherProvider
 ) {
 
+    val fakeWindowsState = testSettings.fakeExposureWindows.flow.asLiveData()
+
     init {
         Timber.d("CWAViewModel: %s", this)
         Timber.d("SavedStateHandle: %s", handle)
@@ -219,6 +223,10 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         launch { keyCacheRepository.clear() }
     }
 
+    fun selectFakeExposureWindowMode(newMode: TestSettings.FakeExposureWindowTypes) {
+        testSettings.fakeExposureWindows.update { newMode }
+    }
+
     @AssistedInject.Factory
     interface Factory : CWAViewModelFactory<TestRiskLevelCalculationFragmentCWAViewModel> {
         fun create(
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 7d94e7c3dca3a8e044acb4aac523109b7266aa7c..0ea69b5c0ecb6b649b3d2658e630ded2a4368f4d 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,23 +34,45 @@
             android:layout_height="wrap_content"
             android:orientation="vertical">
 
-            <TextView
-                style="@style/headline6"
-                android:accessibilityHeading="true"
+            <LinearLayout
+                android:id="@+id/environment_container"
+                style="@style/card"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="Preview (no interaction possible)" />
+                android:orientation="vertical"
+                android:layout_height="wrap_content">
+
+                <TextView
+                    android:id="@+id/fake_windows_title"
+                    style="@style/headline6"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="Fake exposure windows" />
+
+                <TextView
+                    android:layout_width="match_parent"
+                    style="@style/TextAppearance.AppCompat.Caption"
+                    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" />
+
+                <RadioGroup
+                    android:id="@+id/fake_windows_toggle_group"
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:layout_marginTop="@dimen/spacing_tiny"
+                    android:orientation="vertical" />
+            </LinearLayout>
 
             <FrameLayout
                 android:id="@+id/test_risk_card"
                 style="@style/card"
+                gone="@{showRiskStatusCard == null || !showRiskStatusCard}"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
-                gone="@{showRiskStatusCard == null || !showRiskStatusCard}"
-                android:focusable="true"
                 android:backgroundTint="@{tracingCard.getRiskInfoContainerBackgroundTint(context)}"
-                android:backgroundTintMode="src_over">
+                android:backgroundTintMode="src_over"
+                android:focusable="true">
 
                 <include
                     android:id="@+id/risk_card_content"
@@ -94,10 +116,10 @@
             <TextView
                 android:id="@+id/label_aggregated_risk_result_title"
                 style="@style/headline6"
-                android:accessibilityHeading="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginTop="@dimen/spacing_normal"
+                android:accessibilityHeading="true"
                 android:text="Aggregated Risk Result" />
 
             <TextView
@@ -109,9 +131,9 @@
             <TextView
                 android:id="@+id/label_risk_additional_info_title"
                 style="@style/headline6"
-                android:accessibilityHeading="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:accessibilityHeading="true"
                 android:text="Risk Calculation Additional Information" />
 
             <TextView
@@ -123,9 +145,9 @@
             <TextView
                 android:id="@+id/label_backend_parameters_title"
                 style="@style/headline6"
-                android:accessibilityHeading="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:accessibilityHeading="true"
                 android:text="Backend Parameters" />
 
             <TextView
@@ -137,9 +159,9 @@
             <TextView
                 android:id="@+id/label_exposure_window_title"
                 style="@style/headline6"
-                android:accessibilityHeading="true"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
+                android:accessibilityHeading="true"
                 android:text="Exposure Windows" />
 
             <TextView
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
index 64f2e75d7addfca50ab691aa7d42bc7d8cd3f4cd..2c5924ec9f54992538ea1c132b41c43fe5aece47 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProvider.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider
 
 import com.google.android.gms.common.api.ApiException
+import com.google.android.gms.nearby.exposurenotification.DiagnosisKeyFileProvider
 import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
 import de.rki.coronawarnapp.exception.reporting.ReportingConstants
 import de.rki.coronawarnapp.nearby.modules.version.ENFVersion
@@ -35,10 +36,19 @@ class DefaultDiagnosisKeyProvider @Inject constructor(
 //            return false
         }
 
+        val keyFilesList = keyFiles.toList()
+        val provideDiagnosisKeysTask = if (enfVersion.isAtLeast(ENFVersion.V1_7)) {
+            Timber.i("Provide diagnosis keys with DiagnosisKeyFileProvider")
+            val diagnosisKeyFileProvider = DiagnosisKeyFileProvider(keyFilesList)
+            enfClient.provideDiagnosisKeys(diagnosisKeyFileProvider)
+        } else {
+            Timber.i("Provide diagnosis keys as list")
+            enfClient.provideDiagnosisKeys(keyFilesList)
+        }
+
         return suspendCoroutine { cont ->
             Timber.d("Performing key submission.")
-            enfClient
-                .provideDiagnosisKeys(keyFiles.toList())
+            provideDiagnosisKeysTask
                 .addOnSuccessListener { cont.resume(true) }
                 .addOnFailureListener {
                     val wrappedException =
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
index 7652fb055bdb64e4cbd76d4528a7047ffbfeef91..3ce93d4823d6b61d3c6a2634937087ee742f9a31 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersion.kt
@@ -9,6 +9,7 @@ import javax.inject.Singleton
 import kotlin.coroutines.resume
 import kotlin.coroutines.resumeWithException
 import kotlin.coroutines.suspendCoroutine
+import kotlin.math.abs
 
 @Singleton
 class DefaultENFVersion @Inject constructor(
@@ -39,9 +40,28 @@ class DefaultENFVersion @Inject constructor(
         }
     }
 
+    override suspend fun isAtLeast(compareVersion: Long): Boolean {
+        if (!compareVersion.isCorrectVersionLength) throw IllegalArgumentException("given version has incorrect length")
+
+        getENFClientVersion()?.let { currentENFClientVersion ->
+            Timber.i("Comparing current ENF client version $currentENFClientVersion with $compareVersion")
+            return currentENFClientVersion >= compareVersion
+        }
+
+        return false
+    }
+
     private suspend fun internalGetENFClientVersion(): Long = suspendCoroutine { cont ->
         client.version
             .addOnSuccessListener { cont.resume(it) }
             .addOnFailureListener { cont.resumeWithException(it) }
     }
+
+    // check if a raw long has the correct length to be considered an API version
+    private val Long.isCorrectVersionLength
+        get(): Boolean = abs(this).toString().length == GOOGLE_API_VERSION_FIELD_LENGTH
+
+    companion object {
+        private const val GOOGLE_API_VERSION_FIELD_LENGTH = 8
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
index b7d16994a91e0742d3910f3c22fd7323b31b6857..7b1b8d8303560d5d1fd2e93dd5a4d90dc301e23f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/nearby/modules/version/ENFVersion.kt
@@ -12,7 +12,15 @@ interface ENFVersion {
      */
     suspend fun requireMinimumVersion(required: Long)
 
+    /**
+     * Indicates if the client runs above a certain version
+     *
+     * @return isAboveVersion, if connected to an old unsupported version, return false
+     */
+    suspend fun isAtLeast(compareVersion: Long): Boolean
+
     companion object {
         const val V1_6 = 16000000L
+        const val V1_7 = 17000000L
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TestSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TestSettings.kt
index 261fd5fcacf20a08176f1db87dbc42bb1e44b8a1..5f9b38fcb3cf3949be5d8fbd5af640560911d62c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TestSettings.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/TestSettings.kt
@@ -1,14 +1,19 @@
 package de.rki.coronawarnapp.storage
 
 import android.content.Context
+import com.google.gson.Gson
+import com.google.gson.annotations.SerializedName
 import de.rki.coronawarnapp.util.di.AppContext
+import de.rki.coronawarnapp.util.preferences.FlowPreference
 import de.rki.coronawarnapp.util.preferences.createFlowPreference
+import de.rki.coronawarnapp.util.serialization.BaseGson
 import javax.inject.Inject
 import javax.inject.Singleton
 
 @Singleton
 class TestSettings @Inject constructor(
-    @AppContext private val context: Context
+    @AppContext private val context: Context,
+    @BaseGson private val gson: Gson
 ) {
     private val prefs by lazy {
         context.getSharedPreferences("test_settings", Context.MODE_PRIVATE)
@@ -18,4 +23,22 @@ class TestSettings @Inject constructor(
         key = "connections.metered.fake",
         defaultValue = false
     )
+
+    val fakeExposureWindows = FlowPreference(
+        preferences = prefs,
+        key = "riskleve.exposurewindows.fake",
+        reader = FlowPreference.gsonReader<FakeExposureWindowTypes>(gson, FakeExposureWindowTypes.DISABLED),
+        writer = FlowPreference.gsonWriter(gson)
+    )
+
+    enum class FakeExposureWindowTypes {
+        @SerializedName("DISABLED")
+        DISABLED,
+
+        @SerializedName("INCREASED_RISK_DEFAULT")
+        INCREASED_RISK_DEFAULT,
+
+        @SerializedName("LOW_RISK_DEFAULT")
+        LOW_RISK_DEFAULT
+    }
 }
diff --git a/Corona-Warn-App/src/main/res/values-de/strings.xml b/Corona-Warn-App/src/main/res/values-de/strings.xml
index 3af087fa8882737e837c5feac5090e1674eb3a07..d024cca935d97319b2a2836ee1d2c7a278b7cd78 100644
--- a/Corona-Warn-App/src/main/res/values-de/strings.xml
+++ b/Corona-Warn-App/src/main/res/values-de/strings.xml
@@ -448,7 +448,7 @@
     <!-- YTXT: onboarding(together) - inform about the app -->
     <string name="onboarding_body">"Machen Sie Ihr Smartphone zum Corona-Warn-System. Überblicken Sie Ihren Risikostatus und erfahren Sie, ob in den letzten 14 Tagen Corona-positiv getestete Personen in Ihrer Nähe waren."</string>
     <!-- YTXT: onboarding(together) - explain application -->
-    <string name="onboarding_body_emphasized">"Die App merkt sich Begegnungen zwischen Menschen, indem ihre Smartphones verschlüsselte Zufalls-IDs austauschen. Und zwar, ohne dabei auf persönliche Daten zuzugreifen."</string>
+    <string name="onboarding_body_emphasized">"Die App merkt sich Begegnungen zwischen Menschen, indem ihre Smartphones verschlüsselte Zufalls-IDs austauschen. Persönliche Daten werden dabei nicht ausgetauscht."</string>
     <!-- XACT: onboarding(together) - illustraction description, header image -->
     <string name="onboarding_illustration_description">"Eine vielfältige Gruppe in einer Stadt benutzt Smartphones."</string>
     <!-- XACT: Onboarding (privacy) page title -->
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
index da15e57215243c887afb317a87160ca618a71619..d484333fe3dfab9d173bb2e716572ff21f19a9e6 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/diagnosiskeyprovider/DefaultDiagnosisKeyProviderTest.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.nearby.modules.diagnosiskeyprovider
 
+import com.google.android.gms.nearby.exposurenotification.DiagnosisKeyFileProvider
 import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
 import de.rki.coronawarnapp.nearby.modules.version.ENFVersion
 import de.rki.coronawarnapp.nearby.modules.version.OutdatedENFVersionException
@@ -36,6 +37,8 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
 
         coEvery { googleENFClient.provideDiagnosisKeys(any<List<File>>()) } returns MockGMSTask.forValue(null)
 
+        coEvery { googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>()) } returns MockGMSTask.forValue(null)
+
         coEvery { enfVersion.requireMinimumVersion(any()) } returns Unit
     }
 
@@ -70,7 +73,23 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
     }
 
     @Test
-    fun `key provision is used on newer ENF versions`() {
+    fun `key provision is used with DiagnosisKeyFileProvider on ENF versions from 1_7 upwards`() {
+        coEvery { enfVersion.isAtLeast(any()) } returns true
+
+        val provider = createProvider()
+
+        runBlocking { provider.provideDiagnosisKeys(exampleKeyFiles) } shouldBe true
+
+        coVerifySequence {
+            submissionQuota.consumeQuota(1)
+            googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>())
+        }
+    }
+
+    @Test
+    fun `key provision is used with key list on ENF versions 1_6`() {
+        coEvery { enfVersion.isAtLeast(any()) } returns false
+
         val provider = createProvider()
 
         runBlocking { provider.provideDiagnosisKeys(exampleKeyFiles) } shouldBe true
@@ -81,9 +100,11 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
         }
     }
 
+
     @Test
     fun `quota is just monitored`() {
         coEvery { submissionQuota.consumeQuota(any()) } returns false
+        coEvery { enfVersion.isAtLeast(any()) } returns true
 
         val provider = createProvider()
 
@@ -91,7 +112,7 @@ class DefaultDiagnosisKeyProviderTest : BaseTest() {
 
         coVerifySequence {
             submissionQuota.consumeQuota(1)
-            googleENFClient.provideDiagnosisKeys(exampleKeyFiles)
+            googleENFClient.provideDiagnosisKeys(any<DiagnosisKeyFileProvider>())
         }
     }
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
index 6d9e563b93dda599d64478739717998339f72d63..48a020e449e2a295afbef3b536432b2f928ffa57 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/modules/version/DefaultENFVersionTest.kt
@@ -109,4 +109,54 @@ internal class DefaultENFVersionTest {
             }
         }
     }
+
+    @Test
+    fun `isAtLeast is true for newer version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_7)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_6) shouldBe true
+        }
+    }
+
+    @Test
+    fun `isAtLeast is true for equal version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_6)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_6) shouldBe true
+        }
+    }
+
+    @Test
+    fun `isAtLeast is false for older version`() {
+        every { client.version } returns MockGMSTask.forValue(ENFVersion.V1_6)
+
+        runBlockingTest {
+            createInstance().isAtLeast(ENFVersion.V1_7) shouldBe false
+        }
+    }
+
+    @Test
+    fun `invalid input for isAtLeast throws IllegalArgumentException`() {
+        runBlockingTest {
+            shouldThrow<IllegalArgumentException> {
+                createInstance().isAtLeast(16)
+            }
+        }
+    }
+
+    @Test
+    fun `isAtLeast returns false when client not connected`() {
+        every { client.version } returns MockGMSTask.forError(ApiException(Status(API_NOT_CONNECTED)))
+
+        runBlockingTest {
+            createInstance().apply {
+                shouldNotThrowAny {
+                    isAtLeast(ENFVersion.V1_6) shouldBe false
+                    isAtLeast(ENFVersion.V1_7) shouldBe false
+                }
+            }
+        }
+    }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/TestSettingsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/TestSettingsTest.kt
index f904964bfddde385950bac6c95efad7247fda59b..75dd982d0f4a0bc20c580e3bdc0bd08bf693e334 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/TestSettingsTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/storage/TestSettingsTest.kt
@@ -1,6 +1,7 @@
 package de.rki.coronawarnapp.storage
 
 import android.content.Context
+import com.google.gson.Gson
 import de.rki.coronawarnapp.util.CWADebug
 import io.mockk.MockKAnnotations
 import io.mockk.clearAllMocks
@@ -16,6 +17,7 @@ class TestSettingsTest : BaseTest() {
 
     @MockK lateinit var context: Context
     private lateinit var mockPreferences: MockSharedPreferences
+    private val gson = Gson()
 
     @BeforeEach
     fun setup() {
@@ -35,6 +37,7 @@ class TestSettingsTest : BaseTest() {
     }
 
     private fun buildInstance(): TestSettings = TestSettings(
-        context = context
+        context = context,
+        gson = gson
     )
 }
diff --git a/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..9d495c8c22e32f7ad4fcaa7548c38ac13ebda676
--- /dev/null
+++ b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt
@@ -0,0 +1,43 @@
+package de.rki.coronawarnapp.nearby.modules.exposurewindow
+
+import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
+import de.rki.coronawarnapp.util.CWADebug
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.clearAllMocks
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+import testhelpers.gms.MockGMSTask
+import java.io.File
+
+class ExposureWindowProviderTest : BaseTest() {
+    @MockK lateinit var googleENFClient: ExposureNotificationClient
+
+    private val exampleKeyFiles = listOf(File("file1"), File("file2"))
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+
+        coEvery { googleENFClient.exposureWindows } returns MockGMSTask.forValue(emptyList())
+    }
+
+    @AfterEach
+    fun teardown() {
+        clearAllMocks()
+    }
+
+    private fun createProvider() = DefaultExposureWindowProvider(
+        client = googleENFClient
+    )
+
+    @Test
+    fun `fake exposure windows only in tester builds`() {
+        val instance = createProvider()
+        CWADebug.isDeviceForTestersBuild shouldBe false
+    }
+}
diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f9b77f2560de2a6f1a7d0e0f84642cd3a9085468
--- /dev/null
+++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/nearby/modules/exposurewindow/ExposureWindowProviderTest.kt
@@ -0,0 +1,48 @@
+package de.rki.coronawarnapp.nearby.modules.exposurewindow
+
+import com.google.android.gms.nearby.exposurenotification.ExposureNotificationClient
+import de.rki.coronawarnapp.storage.TestSettings
+import de.rki.coronawarnapp.util.CWADebug
+import io.kotest.matchers.shouldBe
+import io.mockk.MockKAnnotations
+import io.mockk.clearAllMocks
+import io.mockk.coEvery
+import io.mockk.impl.annotations.MockK
+import org.junit.jupiter.api.AfterEach
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+import testhelpers.BaseTest
+import testhelpers.gms.MockGMSTask
+import java.io.File
+
+class ExposureWindowProviderTest : BaseTest() {
+    @MockK lateinit var googleENFClient: ExposureNotificationClient
+    @MockK lateinit var testSettings: TestSettings
+    @MockK lateinit var fakeExposureWindowProvider: FakeExposureWindowProvider
+
+    private val exampleKeyFiles = listOf(File("file1"), File("file2"))
+
+    @BeforeEach
+    fun setup() {
+        MockKAnnotations.init(this)
+
+        coEvery { googleENFClient.exposureWindows } returns MockGMSTask.forValue(emptyList())
+    }
+
+    @AfterEach
+    fun teardown() {
+        clearAllMocks()
+    }
+
+    private fun createProvider() = DefaultExposureWindowProvider(
+        client = googleENFClient,
+        testSettings = testSettings,
+        fakeExposureWindowProvider = fakeExposureWindowProvider
+    )
+
+    @Test
+    fun `fake exposure windows only in tester builds`() {
+        val instance = createProvider()
+        CWADebug.isDeviceForTestersBuild shouldBe true
+    }
+}