From 3e5563ca161779a1dbc178da358e4943e9f51c5e Mon Sep 17 00:00:00 2001
From: Chilja Gossow <49635654+chiljamgossow@users.noreply.github.com>
Date: Thu, 1 Apr 2021 16:09:29 +0200
Subject: [PATCH] Risk state (EXPOSUREAPP-5697) (#2728)

* risk calculation

* risk calculation

* risk calculation

* refactoring

* mapping tests

* move risk calculation to different change list

* detekt

* klint

* correct name

* rename to presence tracing

* add test

* refactoring

* repo

* db

* compare byte array

* refactor guidHash

* merge 2.0.x

* clean up

* improve performance

* klint

* provide risk result

* add tests

* detekt and klint

* detekt and klint

* failing checks

* merge 2.0.x

* adapt tests

* adapt tests

* adapt tests

* revert unintended change

* remove db

* clean up

* more tests

* klint

* db json

* change package

* klint

* change package

* change package

* add tests

* fix import

* clean up naming

* clean up naming, moe package, more tests

* klint

* parallel matching

* parallel matching

* matching

* comments

* comments

* klint

* logging

* limit parallel processing to max of 4

* merge

* move into one file

* comments

* klint

* change visibility

* change visibility

* update test menu output

* klint

* fix test

* renaming, refactoring

* add column info
change warningPackageId type to string

* fix test

* fix test

* db

* db

* db move

* fix refactoring

* refactor date

* fix tests

* fix tests

* klint

* detekt

* fix test

* clean up

* clean up
---
 .../1.json                                    |  56 +-----
 .../1.json                                    |  90 ++++++++++
 .../RiskResultDatabaseMigrationTest.kt        |   4 +-
 .../risk/storage/RiskResultDatabaseTest.kt    |   6 +-
 .../coronawarnapp/ui/main/home/HomeData.kt    |   5 +-
 .../coronawarnapp/ui/tracing/TracingData.kt   |   9 +-
 .../risk/storage/DefaultRiskLevelStorage.kt   |   4 +-
 .../risk/storage/DefaultRiskLevelStorage.kt   |   6 +-
 ...iskLevelCalculationFragmentCWAViewModel.kt |  14 +-
 .../ContactDiaryRetentionCalculation.kt       |   2 +-
 .../tabs/location/DiaryLocationViewHolder.kt  |   2 +-
 .../overview/ContactDiaryOverviewViewModel.kt |   8 +-
 .../analytics/common/PpaDataExtensions.kt     |   4 +-
 .../ExposureRiskMetadataDonor.kt              |  10 +-
 .../AnalyticsKeySubmissionCollector.kt        |   6 +-
 .../registeredtest/TestResultDataCollector.kt |   6 +-
 .../storage/TestResultDonorSettings.kt        |   6 +-
 .../storage/TraceLocationDatabase.kt          |   6 +-
 .../risk/CheckInWarningMatcher.kt             |  49 +++--
 .../risk/PresenceTracingRiskCalculator.kt     |  11 ++
 .../risk/PresenceTracingRiskDatabase.kt       |  32 ++++
 .../risk/PresenceTracingRiskRepository.kt     | 133 ++++++++++++--
 .../presencetracing/risk/PtRiskLevelResult.kt |  40 +++++
 .../coronawarnapp/risk/DefaultRiskLevels.kt   |  18 +-
 ...iskLevelResult.kt => EwRiskLevelResult.kt} |  26 +--
 ...TaskResult.kt => EwRiskLevelTaskResult.kt} |  20 +--
 .../risk/RiskLevelChangeDetector.kt           |  66 +++++--
 .../risk/RiskLevelResultExtensions.kt         |  60 +++++--
 .../coronawarnapp/risk/RiskLevelSettings.kt   |  10 ++
 .../rki/coronawarnapp/risk/RiskLevelTask.kt   |  32 ++--
 .../de/rki/coronawarnapp/risk/RiskLevels.kt   |   4 +-
 ...iskResult.kt => EwAggregatedRiskResult.kt} |   4 +-
 ...DateResult.kt => ExposureWindowDayRisk.kt} |   2 +-
 .../risk/storage/BaseRiskLevelStorage.kt      | 168 ++++++++++++++----
 .../risk/storage/RiskLevelStorage.kt          | 104 ++++++++---
 .../PersistedAggregatedRiskPerDateResult.kt   |   8 +-
 .../PersistedRiskLevelResultDao.kt            |  14 +-
 .../PersistedRiskResultDaoExtensions.kt       |  27 +--
 .../tracing/states/TracingState.kt            |  10 +-
 .../tracing/states/TracingStateProvider.kt    |   2 +-
 .../TracingDetailsFragmentViewModel.kt        |   2 +-
 .../ui/details/TracingDetailsItemProvider.kt  |   9 +-
 .../riskdetails/DetailsIncreasedRiskBox.kt    |   4 +-
 .../EventRegistrationUIModule.kt              |   4 +-
 .../attendee/checkins/items/PastCheckInVH.kt  |   2 +-
 .../scan/ScanCheckInQrCodeFragment.kt         |   2 +-
 .../scan/SubmissionQRCodeScanFragment.kt      |   2 +-
 .../contact_diary_overview_list_item.xml      |   1 -
 ...ontactDiaryDataRetentionCalculationTest.kt |   8 +-
 .../ContactDiaryOverviewViewModelTest.kt      |  26 +--
 .../modules/ExposureRiskMetadataDonorTest.kt  |  42 ++---
 .../AnalyticsKeySubmissionCollectorTest.kt    |  12 +-
 .../TestResultDataCollectorTest.kt            |   6 +-
 .../windows/ExposureWindowsCalculationTest.kt |  14 +-
 .../risk/CheckInWarningMatcherTest.kt         |  20 ++-
 ....kt => EwRiskLevelResultExtensionsTest.kt} |  22 +--
 ...ResultTest.kt => EwRiskLevelResultTest.kt} |  20 +--
 .../risk/RiskLevelChangeDetectorTest.kt       |  41 ++---
 .../coronawarnapp/risk/RiskLevelTaskTest.kt   |  46 ++---
 ...ltTest.kt => ExposureWindowDayRiskTest.kt} |   4 +-
 .../risk/storage/BaseRiskLevelStorageTest.kt  |  24 +--
 .../risk/storage/CombineRiskTest.kt           |  14 +-
 .../risk/storage/RiskStorageTestData.kt       |  18 +-
 .../internal/PersistedRiskResultDaoTest.kt    |  16 +-
 .../tracing/states/IncreasedRiskTest.kt       |   3 +-
 .../tracing/states/LowRiskTest.kt             |   5 +-
 .../details/TracingDetailsItemProviderTest.kt |  34 ++--
 .../storage/DefaultRiskLevelStorageTest.kt    |  10 +-
 .../storage/DefaultRiskLevelStorageTest.kt    |  10 +-
 69 files changed, 1001 insertions(+), 504 deletions(-)
 create mode 100644 Corona-Warn-App/schemas/de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskDatabase/1.json
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskDatabase.kt
 create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PtRiskLevelResult.kt
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/{RiskLevelResult.kt => EwRiskLevelResult.kt} (58%)
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/{RiskLevelTaskResult.kt => EwRiskLevelTaskResult.kt} (62%)
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/{AggregatedRiskResult.kt => EwAggregatedRiskResult.kt} (86%)
 rename Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/{AggregatedRiskPerDateResult.kt => ExposureWindowDayRisk.kt} (93%)
 rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/{RiskLevelResultExtensionsTest.kt => EwRiskLevelResultExtensionsTest.kt} (79%)
 rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/{RiskLevelResultTest.kt => EwRiskLevelResultTest.kt} (55%)
 rename Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/{AggregatedRiskPerDateResultTest.kt => ExposureWindowDayRiskTest.kt} (95%)

diff --git a/Corona-Warn-App/schemas/de.rki.coronawarnapp.eventregistration.storage.TraceLocationDatabase/1.json b/Corona-Warn-App/schemas/de.rki.coronawarnapp.eventregistration.storage.TraceLocationDatabase/1.json
index ae4411da4..1ad43a6bf 100644
--- a/Corona-Warn-App/schemas/de.rki.coronawarnapp.eventregistration.storage.TraceLocationDatabase/1.json
+++ b/Corona-Warn-App/schemas/de.rki.coronawarnapp.eventregistration.storage.TraceLocationDatabase/1.json
@@ -2,7 +2,7 @@
   "formatVersion": 1,
   "database": {
     "version": 1,
-    "identityHash": "06285e5c436b0ac4ef046ec858b505c0",
+    "identityHash": "3017a63593d3342376a56b56743815b7",
     "entities": [
       {
         "tableName": "checkin",
@@ -169,62 +169,12 @@
         },
         "indices": [],
         "foreignKeys": []
-      },
-      {
-        "tableName": "TraceTimeIntervalMatchEntity",
-        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `checkInId` INTEGER NOT NULL, `traceWarningPackageId` TEXT NOT NULL, `transmissionRiskLevel` INTEGER NOT NULL, `startTimeMillis` INTEGER NOT NULL, `endTimeMillis` INTEGER NOT NULL)",
-        "fields": [
-          {
-            "fieldPath": "id",
-            "columnName": "id",
-            "affinity": "INTEGER",
-            "notNull": false
-          },
-          {
-            "fieldPath": "checkInId",
-            "columnName": "checkInId",
-            "affinity": "INTEGER",
-            "notNull": true
-          },
-          {
-            "fieldPath": "traceWarningPackageId",
-            "columnName": "traceWarningPackageId",
-            "affinity": "TEXT",
-            "notNull": true
-          },
-          {
-            "fieldPath": "transmissionRiskLevel",
-            "columnName": "transmissionRiskLevel",
-            "affinity": "INTEGER",
-            "notNull": true
-          },
-          {
-            "fieldPath": "startTimeMillis",
-            "columnName": "startTimeMillis",
-            "affinity": "INTEGER",
-            "notNull": true
-          },
-          {
-            "fieldPath": "endTimeMillis",
-            "columnName": "endTimeMillis",
-            "affinity": "INTEGER",
-            "notNull": true
-          }
-        ],
-        "primaryKey": {
-          "columnNames": [
-            "id"
-          ],
-          "autoGenerate": true
-        },
-        "indices": [],
-        "foreignKeys": []
       }
     ],
     "views": [],
     "setupQueries": [
       "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
-      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '06285e5c436b0ac4ef046ec858b505c0')"
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '3017a63593d3342376a56b56743815b7')"
     ]
   }
-}
\ No newline at end of file
+}
diff --git a/Corona-Warn-App/schemas/de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskDatabase/1.json b/Corona-Warn-App/schemas/de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskDatabase/1.json
new file mode 100644
index 000000000..59228f0a7
--- /dev/null
+++ b/Corona-Warn-App/schemas/de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskDatabase/1.json
@@ -0,0 +1,90 @@
+{
+  "formatVersion": 1,
+  "database": {
+    "version": 1,
+    "identityHash": "8f6fbeaffe608cffaae19b13399cdd3a",
+    "entities": [
+      {
+        "tableName": "TraceTimeIntervalMatchEntity",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `checkInId` INTEGER NOT NULL, `traceWarningPackageId` TEXT NOT NULL, `transmissionRiskLevel` INTEGER NOT NULL, `startTimeMillis` INTEGER NOT NULL, `endTimeMillis` INTEGER NOT NULL)",
+        "fields": [
+          {
+            "fieldPath": "id",
+            "columnName": "id",
+            "affinity": "INTEGER",
+            "notNull": false
+          },
+          {
+            "fieldPath": "checkInId",
+            "columnName": "checkInId",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "traceWarningPackageId",
+            "columnName": "traceWarningPackageId",
+            "affinity": "TEXT",
+            "notNull": true
+          },
+          {
+            "fieldPath": "transmissionRiskLevel",
+            "columnName": "transmissionRiskLevel",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "startTimeMillis",
+            "columnName": "startTimeMillis",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "endTimeMillis",
+            "columnName": "endTimeMillis",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "id"
+          ],
+          "autoGenerate": true
+        },
+        "indices": [],
+        "foreignKeys": []
+      },
+      {
+        "tableName": "PresenceTracingRiskLevelResultEntity",
+        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`calculatedAtMillis` INTEGER NOT NULL, `riskStateCode` INTEGER NOT NULL, PRIMARY KEY(`calculatedAtMillis`))",
+        "fields": [
+          {
+            "fieldPath": "calculatedAtMillis",
+            "columnName": "calculatedAtMillis",
+            "affinity": "INTEGER",
+            "notNull": true
+          },
+          {
+            "fieldPath": "riskState",
+            "columnName": "riskStateCode",
+            "affinity": "INTEGER",
+            "notNull": true
+          }
+        ],
+        "primaryKey": {
+          "columnNames": [
+            "calculatedAtMillis"
+          ],
+          "autoGenerate": false
+        },
+        "indices": [],
+        "foreignKeys": []
+      }
+    ],
+    "views": [],
+    "setupQueries": [
+      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8f6fbeaffe608cffaae19b13399cdd3a')"
+    ]
+  }
+}
\ No newline at end of file
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt
index 3b699ab53..7535a1c30 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseMigrationTest.kt
@@ -7,7 +7,7 @@ import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.platform.app.InstrumentationRegistry
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration1To2
 import de.rki.coronawarnapp.risk.storage.internal.migrations.RiskResultDatabaseMigration2To3
@@ -129,7 +129,7 @@ class RiskResultDatabaseMigrationTest : BaseTestInstrumentation() {
             monotonicId = 3,
             id = "0235fef8-4332-4a43-b7d8-f5eacb54a6ee",
             calculatedAt = Instant.parse("2020-12-31T16:28:25.400Z"),
-            failureReason = RiskLevelResult.FailureReason.TRACING_OFF,
+            failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF,
             aggregatedRiskResult = null
         )
 
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt
index 3c3b2446d..9dc220c35 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/risk/storage/RiskResultDatabaseTest.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.risk.storage
 
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedAggregatedRiskPerDateResult
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao
@@ -53,7 +53,7 @@ class RiskResultDatabaseTest {
             numberOfDaysWithLowRisk = 4,
             numberOfDaysWithHighRisk = 5
         ),
-        failureReason = RiskLevelResult.FailureReason.TRACING_OFF
+        failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF
     )
 
     private val newestEntryFailed = PersistedRiskLevelResultDao(
@@ -69,7 +69,7 @@ class RiskResultDatabaseTest {
             numberOfDaysWithLowRisk = 4,
             numberOfDaysWithHighRisk = 5
         ),
-        failureReason = RiskLevelResult.FailureReason.TRACING_OFF
+        failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF
     )
 
     @Test
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
index 125fccb81..2a8bc98f6 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/main/home/HomeData.kt
@@ -28,6 +28,7 @@ import de.rki.coronawarnapp.tracing.ui.homecards.LowRiskCard
 import de.rki.coronawarnapp.tracing.ui.homecards.TracingDisabledCard
 import de.rki.coronawarnapp.tracing.ui.homecards.TracingFailedCard
 import de.rki.coronawarnapp.tracing.ui.homecards.TracingProgressCard
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import org.joda.time.Instant
 import java.util.Date
 
@@ -71,7 +72,7 @@ object HomeData {
                 riskState = RiskState.LOW_RISK,
                 isInDetailsMode = false,
                 lastExposureDetectionTime = todayAtNineFiftyFive,
-                lastEncounterAt = todayAtNineFiftyFive,
+                lastEncounterAt = todayAtNineFiftyFive.toLocalDateUtc(),
                 allowManualUpdate = false,
                 daysWithEncounters = 1,
                 daysSinceInstallation = 4
@@ -87,7 +88,7 @@ object HomeData {
                 lastExposureDetectionTime = todayAtNineFiftyFive,
                 allowManualUpdate = false,
                 daysWithEncounters = 1,
-                lastEncounterAt = todayAtNineFiftyFive
+                lastEncounterAt = todayAtNineFiftyFive.toLocalDateUtc()
             ),
             onCardClick = {},
             onUpdateClick = {}
diff --git a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingData.kt b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingData.kt
index 8608e00b8..c1b30a16e 100644
--- a/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingData.kt
+++ b/Corona-Warn-App/src/androidTest/java/de/rki/coronawarnapp/ui/tracing/TracingData.kt
@@ -17,6 +17,7 @@ import de.rki.coronawarnapp.tracing.ui.details.items.risk.TracingFailedBox
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsFailedCalculationBox
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsIncreasedRiskBox
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsLowRiskBox
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import org.joda.time.Instant
 
 object TracingData {
@@ -97,7 +98,7 @@ object TracingData {
                     allowManualUpdate = false,
                     daysWithEncounters = 1,
                     daysSinceInstallation = 4,
-                    lastEncounterAt = todayAtNineFiftyFive
+                    lastEncounterAt = todayAtNineFiftyFive.toLocalDateUtc()
                 )
             ),
             BehaviorNormalRiskBox.Item(
@@ -128,7 +129,7 @@ object TracingData {
                     allowManualUpdate = false,
                     daysWithEncounters = 2,
                     daysSinceInstallation = 4,
-                    lastEncounterAt = todayAtNineFiftyFive
+                    lastEncounterAt = todayAtNineFiftyFive.toLocalDateUtc()
                 )
             ),
             BehaviorNormalRiskBox.Item(
@@ -158,7 +159,7 @@ object TracingData {
                     lastExposureDetectionTime = todayAtNineFiftyFive,
                     allowManualUpdate = false,
                     daysWithEncounters = 1,
-                    lastEncounterAt = todayAtNineFiftyFive
+                    lastEncounterAt = todayAtNineFiftyFive.toLocalDateUtc()
                 )
             ),
             BehaviorIncreasedRiskBox.Item,
@@ -168,7 +169,7 @@ object TracingData {
             ),
             DetailsIncreasedRiskBox.Item(
                 riskState = RiskState.INCREASED_RISK,
-                lastEncounteredAt = todayAtNineFiftyFive
+                lastEncounteredAt = todayAtNineFiftyFive.toLocalDateUtc()
             )
         )
     )
diff --git a/Corona-Warn-App/src/device/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt b/Corona-Warn-App/src/device/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
index 0b6153801..c88e6445b 100644
--- a/Corona-Warn-App/src/device/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
+++ b/Corona-Warn-App/src/device/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.risk.storage
 
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.util.coroutine.AppScope
 import kotlinx.coroutines.CoroutineScope
@@ -20,7 +20,7 @@ class DefaultRiskLevelStorage @Inject constructor(
     // Taken from TimeVariables.MAX_STALE_EXPOSURE_RISK_RANGE
     override val storedResultLimit: Int = 2 * 6
 
-    override suspend fun storeExposureWindows(storedResultId: String, result: RiskLevelResult) {
+    override suspend fun storeExposureWindows(storedResultId: String, result: EwRiskLevelResult) {
         Timber.d("storeExposureWindows(): NOOP")
         // NOOP
     }
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
index f3d38f4c1..ee15ef5ab 100644
--- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
+++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/risk/storage/DefaultRiskLevelStorage.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.risk.storage
 
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDao.PersistedScanInstance
 import de.rki.coronawarnapp.risk.storage.internal.windows.toPersistedExposureWindow
@@ -24,11 +24,11 @@ class DefaultRiskLevelStorage @Inject constructor(
     // For testers keep all the results!
     override val storedResultLimit: Int = 14 * 6
 
-    override suspend fun storeExposureWindows(storedResultId: String, result: RiskLevelResult) {
+    override suspend fun storeExposureWindows(storedResultId: String, resultEw: EwRiskLevelResult) {
         Timber.d("Storing exposure windows for storedResultId=%s", storedResultId)
         try {
             val startTime = System.currentTimeMillis()
-            val exposureWindows = result.exposureWindows ?: emptyList()
+            val exposureWindows = resultEw.exposureWindows ?: emptyList()
             val windowIds = exposureWindows
                 .map { it.toPersistedExposureWindow(riskLevelResultId = storedResultId) }
                 .let { exposureWindowsTables.insertWindows(it) }
diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragmentCWAViewModel.kt
index f2fbf7ae8..ed246e65b 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
@@ -16,9 +16,9 @@ import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTra
 import de.rki.coronawarnapp.nearby.modules.detectiontracker.latestSubmission
 import de.rki.coronawarnapp.risk.RiskLevelTask
 import de.rki.coronawarnapp.risk.RiskState
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
-import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults
+import de.rki.coronawarnapp.risk.tryLatestEwResultsWithDefaults
 import de.rki.coronawarnapp.storage.TestSettings
 import de.rki.coronawarnapp.task.TaskController
 import de.rki.coronawarnapp.task.common.DefaultTaskRequest
@@ -71,7 +71,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
     val dataResetEvent = SingleLiveEvent<String>()
     val shareFileEvent = SingleLiveEvent<File>()
 
-    private val lastRiskResult = riskLevelStorage.allRiskLevelResults.map { results ->
+    private val lastRiskResult = riskLevelStorage.allEwRiskLevelResults.map { results ->
         results.maxByOrNull { it.calculatedAt }
     }
 
@@ -85,7 +85,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
 
             if (it.wasSuccessfullyCalculated) {
                 // wasSuccessfullyCalculated check for aggregatedRiskResult != null
-                it.aggregatedRiskResult!!.toReadableString()
+                it.ewAggregatedRiskResult!!.toReadableString()
             } else {
                 var notAvailable = "Aggregated risk result is not available"
                 it.failureReason?.let { failureReason -> notAvailable += " because ${failureReason.failureCode}" }
@@ -94,7 +94,7 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         }
         .asLiveData()
 
-    private fun AggregatedRiskResult.toReadableString(): String = StringBuilder()
+    private fun EwAggregatedRiskResult.toReadableString(): String = StringBuilder()
         .appendLine("Total RiskLevel: $totalRiskLevel")
         .appendLine("Total Minimum Distinct Encounters With High Risk: $totalMinimumDistinctEncountersWithHighRisk")
         .appendLine("Total Minimum Distinct Encounters With Low Risk: $totalMinimumDistinctEncountersWithLowRisk")
@@ -110,11 +110,11 @@ class TestRiskLevelCalculationFragmentCWAViewModel @AssistedInject constructor(
         .asLiveData()
 
     val additionalRiskCalcInfo = combine(
-        riskLevelStorage.latestAndLastSuccessful,
+        riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult,
         exposureDetectionTracker.latestSubmission()
     ) { riskLevelResults, latestSubmission ->
 
-        val (latestCalc, latestSuccessfulCalc) = riskLevelResults.tryLatestResultsWithDefaults()
+        val (latestCalc, latestSuccessfulCalc) = riskLevelResults.tryLatestEwResultsWithDefaults()
 
         createAdditionalRiskCalcInfo(
             latestCalc.calculatedAt,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt
index 778d3c9e4..5502a5e70 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryRetentionCalculation.kt
@@ -53,7 +53,7 @@ class ContactDiaryRetentionCalculation @Inject constructor(
     }
 
     suspend fun clearObsoleteRiskPerDate() {
-        val list = riskLevelStorage.aggregatedRiskPerDateResults.first()
+        val list = riskLevelStorage.ewDayRiskStates.first()
         Timber.d("Aggregated Risk Per Date Results total count: ${list.size}")
         val toDeleteList = list.filter { risk -> isOutOfRetention(risk.localDateUtc) }
         Timber.d("AggregatedRiskPerDateResult to be deleted count: ${toDeleteList.size}")
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt
index f7998681b..e9c93ba31 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/day/tabs/location/DiaryLocationViewHolder.kt
@@ -5,8 +5,8 @@ import android.view.accessibility.AccessibilityEvent
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.contactdiary.util.hideKeyboard
 import de.rki.coronawarnapp.contactdiary.util.setClickLabel
-import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat
 import de.rki.coronawarnapp.databinding.ContactDiaryLocationListItemBinding
+import de.rki.coronawarnapp.ui.durationpicker.toContactDiaryFormat
 import de.rki.coronawarnapp.ui.lists.BaseAdapter
 import de.rki.coronawarnapp.util.lists.BindableVH
 import de.rki.coronawarnapp.util.ui.setOnClickListenerThrottled
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
index 36b4bb566..3409ad4b6 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModel.kt
@@ -21,7 +21,7 @@ import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.day.riskevent.RiskE
 import de.rki.coronawarnapp.contactdiary.ui.overview.adapter.subheader.OverviewSubHeaderItem
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TraceLocationCheckInRisk
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import de.rki.coronawarnapp.task.TaskController
@@ -55,7 +55,7 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
     private val locationVisitsFlow = contactDiaryRepository.locationVisits
     private val personEncountersFlow = contactDiaryRepository.personEncounters
 
-    private val riskLevelPerDateFlow = riskLevelStorage.aggregatedRiskPerDateResults
+    private val riskLevelPerDateFlow = riskLevelStorage.ewDayRiskStates
     private val traceLocationCheckInRiskFlow = riskLevelStorage.traceLocationCheckInRiskStates
 
     val listItems = combine(
@@ -92,7 +92,7 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         dateList: List<LocalDate>,
         visits: List<ContactDiaryLocationVisit>,
         encounters: List<ContactDiaryPersonEncounter>,
-        riskLevelPerDateList: List<AggregatedRiskPerDateResult>,
+        riskLevelPerDateList: List<ExposureWindowDayRisk>,
         traceLocationCheckInRiskList: List<TraceLocationCheckInRisk>
     ): List<DiaryOverviewItem> {
         Timber.v(
@@ -145,7 +145,7 @@ class ContactDiaryOverviewViewModel @AssistedInject constructor(
         }
     }
 
-    private fun AggregatedRiskPerDateResult.toRisk(locationOrPerson: Boolean): RiskEnfItem {
+    private fun ExposureWindowDayRisk.toRisk(locationOrPerson: Boolean): RiskEnfItem {
         @StringRes val title: Int
         @StringRes var body: Int = R.string.contact_diary_risk_body
         @DrawableRes val drawableId: Int
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt
index 2a898e5a0..c8006b7c3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/common/PpaDataExtensions.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.datadonation.analytics.common
 
 import androidx.annotation.StringRes
 import de.rki.coronawarnapp.R
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 
@@ -67,7 +67,7 @@ val PpaData.PPAFederalState.federalStateShortName: String
         )
     }
 
-fun RiskLevelResult.toMetadataRiskLevel(): PpaData.PPARiskLevel =
+fun EwRiskLevelResult.toMetadataRiskLevel(): PpaData.PPARiskLevel =
     when (riskState) {
         RiskState.INCREASED_RISK -> PpaData.PPARiskLevel.RISK_LEVEL_HIGH
         else -> PpaData.PPARiskLevel.RISK_LEVEL_LOW
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt
index 47b17821a..011d089bc 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/exposureriskmetadata/ExposureRiskMetadataDonor.kt
@@ -2,10 +2,10 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.exposureriskmetadata
 
 import de.rki.coronawarnapp.datadonation.analytics.modules.DonorModule
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
-import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults
+import de.rki.coronawarnapp.risk.tryLatestEwResultsWithDefaults
 import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds
 import kotlinx.coroutines.flow.first
@@ -22,9 +22,9 @@ class ExposureRiskMetadataDonor @Inject constructor(
         val previousMetadata = analyticsSettings.previousExposureRiskMetadata.value
 
         val lastRiskResult = riskLevelStorage
-            .latestAndLastSuccessful
+            .latestAndLastSuccessfulEwRiskLevelResult
             .first()
-            .tryLatestResultsWithDefaults()
+            .tryLatestEwResultsWithDefaults()
             .lastCalculated
 
         val riskLevelForMetadata = lastRiskResult.toMetadataRiskLevel()
@@ -75,7 +75,7 @@ class ExposureRiskMetadataDonor @Inject constructor(
     }
 }
 
-private fun RiskLevelResult.toMetadataRiskLevel(): PpaData.PPARiskLevel =
+private fun EwRiskLevelResult.toMetadataRiskLevel(): PpaData.PPARiskLevel =
     when (riskState) {
         RiskState.LOW_RISK -> PpaData.PPARiskLevel.RISK_LEVEL_LOW
         RiskState.INCREASED_RISK -> PpaData.PPARiskLevel.RISK_LEVEL_HIGH
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollector.kt
index 815291463..a7133bdb0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollector.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollector.kt
@@ -5,7 +5,7 @@ import de.rki.coronawarnapp.datadonation.analytics.common.toMetadataRiskLevel
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
 import de.rki.coronawarnapp.risk.RiskLevelSettings
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
-import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults
+import de.rki.coronawarnapp.risk.tryLatestEwResultsWithDefaults
 import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 import de.rki.coronawarnapp.util.TimeStamper
 import kotlinx.coroutines.flow.first
@@ -35,9 +35,9 @@ class AnalyticsKeySubmissionCollector @Inject constructor(
         analyticsKeySubmissionStorage.testRegisteredAt.update { testRegisteredAt.millis }
 
         val lastRiskResult = riskLevelStorage
-            .latestAndLastSuccessful
+            .latestAndLastSuccessfulEwRiskLevelResult
             .first()
-            .tryLatestResultsWithDefaults()
+            .tryLatestEwResultsWithDefaults()
             .lastCalculated
         val riskLevelAtRegistration = lastRiskResult.toMetadataRiskLevel()
         analyticsKeySubmissionStorage.riskLevelAtTestRegistration.update {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
index 487f34207..5335b72d1 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollector.kt
@@ -3,7 +3,7 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.registeredtest
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
 import de.rki.coronawarnapp.datadonation.analytics.storage.TestResultDonorSettings
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
-import de.rki.coronawarnapp.risk.tryLatestResultsWithDefaults
+import de.rki.coronawarnapp.risk.tryLatestEwResultsWithDefaults
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.formatter.TestResult
 import kotlinx.coroutines.flow.first
@@ -33,9 +33,9 @@ class TestResultDataCollector @Inject constructor(
         // User consented to donate analytics data
         if (analyticsSettings.analyticsEnabled.value) {
             val lastRiskResult = riskLevelStorage
-                .latestAndLastSuccessful
+                .latestAndLastSuccessfulEwRiskLevelResult
                 .first()
-                .tryLatestResultsWithDefaults()
+                .tryLatestEwResultsWithDefaults()
                 .lastCalculated
             Timber.d("saveTestResultDonorDataAtRegistration($testResult, $lastRiskResult)")
             testResultDonorSettings.saveTestResultDonorDataAtRegistration(testResult, lastRiskResult)
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/storage/TestResultDonorSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/storage/TestResultDonorSettings.kt
index f97c06968..3cec71475 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/storage/TestResultDonorSettings.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/datadonation/analytics/storage/TestResultDonorSettings.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.datadonation.analytics.storage
 
 import android.content.Context
 import de.rki.coronawarnapp.datadonation.analytics.common.toMetadataRiskLevel
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.di.AppContext
@@ -95,14 +95,14 @@ class TestResultDonorSettings @Inject constructor(
         }
     )
 
-    fun saveTestResultDonorDataAtRegistration(testResult: TestResult, lastRiskResult: RiskLevelResult) {
+    fun saveTestResultDonorDataAtRegistration(testResult: TestResult, lastEwRiskResult: EwRiskLevelResult) {
         testScannedAfterConsent.update { true }
         testResultAtRegistration.update { testResult }
         if (testResult in listOf(TestResult.POSITIVE, TestResult.NEGATIVE)) {
             finalTestResultReceivedAt.update { timeStamper.nowUTC }
         }
 
-        riskLevelAtTestRegistration.update { lastRiskResult.toMetadataRiskLevel() }
+        riskLevelAtTestRegistration.update { lastEwRiskResult.toMetadataRiskLevel() }
     }
 
     fun clear() = prefs.clearAndNotify()
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/storage/TraceLocationDatabase.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/storage/TraceLocationDatabase.kt
index 86ab94da5..30e508cf4 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/storage/TraceLocationDatabase.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/eventregistration/storage/TraceLocationDatabase.kt
@@ -10,8 +10,6 @@ import de.rki.coronawarnapp.eventregistration.storage.dao.TraceLocationDao
 import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationCheckInEntity
 import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationConverters
 import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationEntity
-import de.rki.coronawarnapp.presencetracing.risk.TraceTimeIntervalMatchDao
-import de.rki.coronawarnapp.presencetracing.risk.TraceTimeIntervalMatchEntity
 import de.rki.coronawarnapp.util.database.CommonConverters
 import de.rki.coronawarnapp.util.di.AppContext
 import javax.inject.Inject
@@ -19,8 +17,7 @@ import javax.inject.Inject
 @Database(
     entities = [
         TraceLocationCheckInEntity::class,
-        TraceLocationEntity::class,
-        TraceTimeIntervalMatchEntity::class
+        TraceLocationEntity::class
     ],
     version = 1,
     exportSchema = true
@@ -30,7 +27,6 @@ abstract class TraceLocationDatabase : RoomDatabase() {
 
     abstract fun eventCheckInDao(): CheckInDao
     abstract fun traceLocationDao(): TraceLocationDao
-    abstract fun traceTimeIntervalMatchDao(): TraceTimeIntervalMatchDao
 
     class Factory @Inject constructor(@AppContext private val context: Context) {
         fun create() = Room
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcher.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcher.kt
index ea28693fc..fed793d67 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcher.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcher.kt
@@ -28,29 +28,47 @@ class CheckInWarningMatcher @Inject constructor(
 ) {
     suspend fun execute(): List<CheckInWarningOverlap> {
 
+        presenceTracingRiskRepository.deleteStaleData()
+
         val checkIns = checkInsRepository.allCheckIns.firstOrNull()
+        if (checkIns.isNullOrEmpty()) {
+            Timber.i("No check-ins available. Deleting all matches.")
+            presenceTracingRiskRepository.deleteAllMatches()
+            presenceTracingRiskRepository.reportSuccessfulCalculation(emptyList())
+            return emptyList()
+        }
+
         val warningPackages = traceTimeIntervalWarningRepository.allWarningPackages.firstOrNull()
 
-        if (checkIns.isNullOrEmpty() || warningPackages.isNullOrEmpty()) {
-            Timber.i("No check-ins or packages available. Deleting all matches.")
-            presenceTracingRiskRepository.deleteAllMatches()
+        if (warningPackages.isNullOrEmpty()) {
+            // nothing to be done here
             return emptyList()
         }
 
         val splitCheckIns = checkIns.flatMap { it.splitByMidnightUTC() }
 
-        val matchLists = createMatchingLaunchers(splitCheckIns, warningPackages, dispatcherProvider.IO)
+        val matchLists = createMatchingLaunchers(
+            splitCheckIns,
+            warningPackages,
+            dispatcherProvider.IO
+        )
             .awaitAll()
 
         if (matchLists.contains(null)) {
-            Timber.e("Error occurred during matching. Deleting all stale matches.")
-            presenceTracingRiskRepository.deleteAllMatches()
-            // TODO report calculation failed to show on home card
+            Timber.e("Error occurred during matching. Abort calculation.")
+            presenceTracingRiskRepository.reportFailedCalculation()
             return emptyList()
         }
 
+        // delete stale matches from new packages and mark packages as processed
+        warningPackages.forEach {
+            presenceTracingRiskRepository.deleteMatchesOfPackage(it.warningPackageId)
+            presenceTracingRiskRepository.markPackageProcessed(it.warningPackageId)
+        }
         val matches = matchLists.filterNotNull().flatten()
-        presenceTracingRiskRepository.replaceAllMatches(matches)
+
+        presenceTracingRiskRepository.reportSuccessfulCalculation(matches)
+
         return matches
     }
 }
@@ -69,7 +87,6 @@ internal suspend fun createMatchingLaunchers(
         { list, packageChunk ->
             async {
                 try {
-
                     packageChunk.flatMap {
                         findMatches(list, it)
                     }
@@ -119,12 +136,12 @@ internal fun CheckIn.calculateOverlap(
 
     if (warning.locationIdHash != locationGuidHash) return null
 
-    val warningStartTimestamp = warning.startIntervalNumber.tenMinIntervalToMillis()
-    val warningEndTimestamp = (warning.startIntervalNumber + warning.period).tenMinIntervalToMillis()
+    val warningStartMillis = warning.startIntervalNumber.tenMinIntervalToMillis()
+    val warningEndMillis = (warning.startIntervalNumber + warning.period).tenMinIntervalToMillis()
 
-    val overlapStartTimestamp = kotlin.math.max(checkInStart.millis, warningStartTimestamp)
-    val overlapEndTimestamp = kotlin.math.min(checkInEnd.millis, warningEndTimestamp)
-    val overlapMillis = overlapEndTimestamp - overlapStartTimestamp
+    val overlapStartMillis = kotlin.math.max(checkInStart.millis, warningStartMillis)
+    val overlapEndMillis = kotlin.math.min(checkInEnd.millis, warningEndMillis)
+    val overlapMillis = overlapEndMillis - overlapStartMillis
 
     if (overlapMillis <= 0) return null
 
@@ -132,7 +149,7 @@ internal fun CheckIn.calculateOverlap(
         checkInId = id,
         transmissionRiskLevel = warning.transmissionRiskLevel,
         traceWarningPackageId = traceWarningPackageId,
-        startTime = Instant.ofEpochMilli(overlapStartTimestamp),
-        endTime = Instant.ofEpochMilli(overlapEndTimestamp)
+        startTime = Instant.ofEpochMilli(overlapStartMillis),
+        endTime = Instant.ofEpochMilli(overlapEndMillis)
     )
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskCalculator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskCalculator.kt
index 36f86c905..1c2bd002f 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskCalculator.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskCalculator.kt
@@ -1,5 +1,6 @@
 package de.rki.coronawarnapp.presencetracing.risk
 
+import de.rki.coronawarnapp.risk.RiskState
 import javax.inject.Inject
 
 class PresenceTracingRiskCalculator @Inject constructor(
@@ -49,4 +50,14 @@ class PresenceTracingRiskCalculator @Inject constructor(
                 )
             }
         }
+
+    suspend fun calculateTotalRisk(list: List<CheckInNormalizedTime>): RiskState {
+        if (list.isEmpty()) return RiskState.LOW_RISK
+        val riskPerDay = calculateCheckInRiskPerDay(list)
+        if (riskPerDay.find { it.riskState == RiskState.INCREASED_RISK } != null)
+            return RiskState.INCREASED_RISK
+        if (riskPerDay.find { it.riskState == RiskState.LOW_RISK } != null)
+            return RiskState.LOW_RISK
+        return RiskState.CALCULATION_FAILED
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskDatabase.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskDatabase.kt
new file mode 100644
index 000000000..2d39bd5ea
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskDatabase.kt
@@ -0,0 +1,32 @@
+package de.rki.coronawarnapp.presencetracing.risk
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import de.rki.coronawarnapp.util.di.AppContext
+import javax.inject.Inject
+
+@Database(
+    entities = [
+        TraceTimeIntervalMatchEntity::class,
+        PresenceTracingRiskLevelResultEntity::class
+    ],
+    version = 1,
+    exportSchema = true
+)
+@TypeConverters(RiskStateConverter::class)
+abstract class PresenceTracingRiskDatabase : RoomDatabase() {
+
+    abstract fun presenceTracingRiskLevelResultDao(): PresenceTracingRiskLevelResultDao
+    abstract fun traceTimeIntervalMatchDao(): TraceTimeIntervalMatchDao
+
+    class Factory @Inject constructor(@AppContext private val context: Context) {
+        fun create() = Room
+            .databaseBuilder(context, PresenceTracingRiskDatabase::class.java, DATABASE_NAME)
+            .build()
+    }
+}
+
+private const val DATABASE_NAME = "PresenceTracingRisk_db"
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskRepository.kt
index ebc3ad35a..0d0f7add0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PresenceTracingRiskRepository.kt
@@ -5,13 +5,17 @@ import androidx.room.Dao
 import androidx.room.Entity
 import androidx.room.ForeignKey
 import androidx.room.Insert
+import androidx.room.OnConflictStrategy.REPLACE
 import androidx.room.PrimaryKey
 import androidx.room.Query
-import de.rki.coronawarnapp.eventregistration.storage.TraceLocationDatabase
+import androidx.room.TypeConverter
 import de.rki.coronawarnapp.eventregistration.storage.entity.TraceLocationCheckInEntity
+import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TraceLocationCheckInRisk
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import de.rki.coronawarnapp.util.TimeStamper
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
 import org.joda.time.Days
 import org.joda.time.Instant
@@ -21,7 +25,7 @@ import javax.inject.Singleton
 @Singleton
 class PresenceTracingRiskRepository @Inject constructor(
     private val presenceTracingRiskCalculator: PresenceTracingRiskCalculator,
-    private val databaseFactory: TraceLocationDatabase.Factory,
+    private val databaseFactory: PresenceTracingRiskDatabase.Factory,
     private val timeStamper: TimeStamper
 ) {
 
@@ -33,14 +37,23 @@ class PresenceTracingRiskRepository @Inject constructor(
         database.traceTimeIntervalMatchDao()
     }
 
-    private val normalizedTime = traceTimeIntervalMatchDao.allEntries().map {
-        it.map {
+    private val riskLevelResultDao by lazy {
+        database.presenceTracingRiskLevelResultDao()
+    }
+
+    private val allMatches = traceTimeIntervalMatchDao.allMatches().map { list ->
+        list.map {
             it.toModel()
         }
-    }.map {
+    }
+
+    private val normalizedTime = allMatches.map {
         presenceTracingRiskCalculator.calculateNormalizedTime(it)
     }
 
+    private val fifteenDaysAgo: Instant
+        get() = timeStamper.nowUTC.minus(Days.days(15).toStandardDuration())
+
     val traceLocationCheckInRiskStates: Flow<List<TraceLocationCheckInRisk>> =
         normalizedTime.map {
             presenceTracingRiskCalculator.calculateCheckInRiskPerDay(it)
@@ -51,30 +64,66 @@ class PresenceTracingRiskRepository @Inject constructor(
             presenceTracingRiskCalculator.calculateAggregatedRiskPerDay(it)
         }
 
-    suspend fun replaceAllMatches(list: List<CheckInWarningOverlap>) {
-        deleteAllMatches()
-        addAll(list)
+    internal suspend fun reportSuccessfulCalculation(list: List<CheckInWarningOverlap>) {
+        traceTimeIntervalMatchDao.insert(list.map { it.toEntity() })
+        val last14days = normalizedTime.first().filter { it.localDateUtc.isAfter(fifteenDaysAgo.toLocalDateUtc()) }
+        val risk = presenceTracingRiskCalculator.calculateTotalRisk(last14days)
+        add(
+            PtRiskLevelResult(
+                timeStamper.nowUTC,
+                risk
+            )
+        )
     }
 
-    private suspend fun addAll(list: List<CheckInWarningOverlap>) {
-        traceTimeIntervalMatchDao.insert(list.map { it.toEntity() })
+    internal suspend fun deleteStaleData() {
+        traceTimeIntervalMatchDao.deleteOlderThan(fifteenDaysAgo.millis)
+        riskLevelResultDao.deleteOlderThan(fifteenDaysAgo.millis)
     }
 
-    suspend fun deleteStaleMatches() {
-        val endTime = timeStamper.nowUTC.minus(Days.days(15).toStandardDuration())
-        traceTimeIntervalMatchDao.deleteOlderThan(endTime.millis)
+    internal suspend fun markPackageProcessed(warningPackageId: String) {
+        // TODO
+    }
+
+    internal suspend fun deleteMatchesOfPackage(warningPackageId: String) {
+        traceTimeIntervalMatchDao.deleteMatchesForPackage(warningPackageId)
     }
 
     suspend fun deleteAllMatches() {
         traceTimeIntervalMatchDao.deleteAll()
     }
+
+    fun latestAndLastSuccessful() = riskLevelResultDao.latestAndLastSuccessful().map { list ->
+        list.map { entity ->
+            entity.toModel()
+        }
+    }
+
+    fun latestEntries(limit: Int) = riskLevelResultDao.latestEntries(limit).map { list ->
+        list.map { entity ->
+            entity.toModel()
+        }
+    }
+
+    fun add(riskLevelResult: PtRiskLevelResult) {
+        riskLevelResultDao.insert(riskLevelResult.toEntity())
+    }
+
+    fun reportFailedCalculation() {
+        add(
+            PtRiskLevelResult(
+                timeStamper.nowUTC,
+                RiskState.CALCULATION_FAILED
+            )
+        )
+    }
 }
 
 @Dao
 interface TraceTimeIntervalMatchDao {
 
     @Query("SELECT * FROM TraceTimeIntervalMatchEntity")
-    fun allEntries(): Flow<List<TraceTimeIntervalMatchEntity>>
+    fun allMatches(): Flow<List<TraceTimeIntervalMatchEntity>>
 
     @Query("DELETE FROM TraceTimeIntervalMatchEntity")
     suspend fun deleteAll()
@@ -82,6 +131,9 @@ interface TraceTimeIntervalMatchDao {
     @Query("DELETE FROM TraceTimeIntervalMatchEntity WHERE endTimeMillis < :endTimeMillis")
     suspend fun deleteOlderThan(endTimeMillis: Long)
 
+    @Query("DELETE FROM TraceTimeIntervalMatchEntity WHERE traceWarningPackageId = :warningPackageId")
+    suspend fun deleteMatchesForPackage(warningPackageId: String)
+
     @Insert
     suspend fun insert(entities: List<TraceTimeIntervalMatchEntity>)
 }
@@ -117,3 +169,56 @@ private fun TraceTimeIntervalMatchEntity.toModel() = CheckInWarningOverlap(
     startTime = Instant.ofEpochMilli(startTimeMillis),
     endTime = Instant.ofEpochMilli(endTimeMillis)
 )
+
+@Suppress("MaxLineLength")
+@Dao
+interface PresenceTracingRiskLevelResultDao {
+    @Query("SELECT * FROM (SELECT * FROM PresenceTracingRiskLevelResultEntity ORDER BY calculatedAtMillis DESC LIMIT 1) UNION ALL SELECT * FROM (SELECT * FROM PresenceTracingRiskLevelResultEntity where riskStateCode is not 0 ORDER BY calculatedAtMillis DESC LIMIT 1)")
+    fun latestAndLastSuccessful(): Flow<List<PresenceTracingRiskLevelResultEntity>>
+
+    @Query("SELECT * FROM PresenceTracingRiskLevelResultEntity ORDER BY calculatedAtMillis DESC LIMIT :limit")
+    fun latestEntries(limit: Int): Flow<List<PresenceTracingRiskLevelResultEntity>>
+
+    @Insert(onConflict = REPLACE)
+    fun insert(entity: PresenceTracingRiskLevelResultEntity)
+
+    @Query("DELETE FROM PresenceTracingRiskLevelResultEntity WHERE calculatedAtMillis < :calculatedAtMillis")
+    suspend fun deleteOlderThan(calculatedAtMillis: Long)
+}
+
+@Entity
+data class PresenceTracingRiskLevelResultEntity(
+    @PrimaryKey @ColumnInfo(name = "calculatedAtMillis") val calculatedAtMillis: Long,
+    @ColumnInfo(name = "riskStateCode")val riskState: RiskState
+)
+
+private fun PresenceTracingRiskLevelResultEntity.toModel() = PtRiskLevelResult(
+    calculatedAt = Instant.ofEpochMilli((calculatedAtMillis)),
+    riskState = riskState
+)
+
+private fun PtRiskLevelResult.toEntity() = PresenceTracingRiskLevelResultEntity(
+    calculatedAtMillis = calculatedAt.millis,
+    riskState = riskState
+)
+
+class RiskStateConverter {
+    @TypeConverter
+    fun toRiskStateCode(value: Int?): RiskState? = value?.toRiskState()
+
+    @TypeConverter
+    fun fromRiskStateCode(code: RiskState?): Int? = code?.toCode()
+
+    private fun RiskState.toCode() = when (this) {
+        RiskState.CALCULATION_FAILED -> 0
+        RiskState.LOW_RISK -> 1
+        RiskState.INCREASED_RISK -> 2
+    }
+
+    private fun Int.toRiskState() = when (this) {
+        0 -> RiskState.CALCULATION_FAILED
+        1 -> RiskState.LOW_RISK
+        2 -> RiskState.INCREASED_RISK
+        else -> null
+    }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PtRiskLevelResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PtRiskLevelResult.kt
new file mode 100644
index 000000000..1fa338aab
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/risk/PtRiskLevelResult.kt
@@ -0,0 +1,40 @@
+package de.rki.coronawarnapp.presencetracing.risk
+
+import de.rki.coronawarnapp.risk.RiskState
+import org.joda.time.Instant
+import org.joda.time.LocalDate
+
+data class PtRiskLevelResult(
+    val calculatedAt: Instant,
+    val riskState: RiskState,
+    val presenceTracingDayRisk: List<PresenceTracingDayRisk>? = null
+) {
+
+    val wasSuccessfullyCalculated: Boolean
+        get() = riskState != RiskState.CALCULATION_FAILED
+
+    val numberOfDaysWithHighRisk: Int
+        get() = presenceTracingDayRisk?.count { it.riskState == RiskState.INCREASED_RISK } ?: 0
+
+    val numberOfDaysWithLowRisk: Int
+        get() = presenceTracingDayRisk?.count { it.riskState == RiskState.LOW_RISK } ?: 0
+
+    val mostRecentDateWithHighRisk: LocalDate?
+        get() = presenceTracingDayRisk
+            ?.filter { it.riskState == RiskState.INCREASED_RISK }
+            ?.maxByOrNull { it.localDateUtc }
+            ?.localDateUtc
+
+    val mostRecentDateWithLowRisk: LocalDate?
+        get() = presenceTracingDayRisk
+            ?.filter { it.riskState == RiskState.LOW_RISK }
+            ?.maxByOrNull { it.localDateUtc }
+            ?.localDateUtc
+
+    val daysWithEncounters: Int
+        get() = when (riskState) {
+            RiskState.INCREASED_RISK -> numberOfDaysWithHighRisk
+            RiskState.LOW_RISK -> numberOfDaysWithLowRisk
+            else -> 0
+        }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
index b7d4b4694..f94a7ef3c 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/DefaultRiskLevels.kt
@@ -4,8 +4,8 @@ import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import com.google.android.gms.nearby.exposurenotification.Infectiousness
 import com.google.android.gms.nearby.exposurenotification.ReportType
 import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.result.RiskResult
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import org.joda.time.Instant
@@ -155,7 +155,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
     override fun aggregateResults(
         appConfig: ExposureWindowRiskCalculationConfig,
         exposureWindowResultMap: Map<ExposureWindow, RiskResult>
-    ): AggregatedRiskResult {
+    ): EwAggregatedRiskResult {
         val uniqueDatesMillisSinceEpoch = exposureWindowResultMap.keys
             .map { it.dateMillisSinceEpoch }
             .toSet()
@@ -222,7 +222,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
 
         Timber.d("numberOfDaysWithHighRisk: %d", numberOfDaysWithHighRisk)
 
-        return AggregatedRiskResult(
+        return EwAggregatedRiskResult(
             totalRiskLevel = totalRiskLevel,
             totalMinimumDistinctEncountersWithLowRisk = totalMinimumDistinctEncountersWithLowRisk,
             totalMinimumDistinctEncountersWithHighRisk = totalMinimumDistinctEncountersWithHighRisk,
@@ -230,16 +230,16 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
             mostRecentDateWithHighRisk = mostRecentDateWithHighRisk,
             numberOfDaysWithLowRisk = numberOfDaysWithLowRisk,
             numberOfDaysWithHighRisk = numberOfDaysWithHighRisk,
-            aggregatedRiskPerDateResults = exposureHistory
+            exposureWindowDayRisks = exposureHistory
         )
     }
 
-    private fun List<AggregatedRiskPerDateResult>.mostRecentDateForRisk(riskLevel: ProtoRiskLevel): Instant? =
+    private fun List<ExposureWindowDayRisk>.mostRecentDateForRisk(riskLevel: ProtoRiskLevel): Instant? =
         filter { it.riskLevel == riskLevel }
             .maxOfOrNull { it.dateMillisSinceEpoch }
             ?.let { Instant.ofEpochMilli(it) }
 
-    private fun List<AggregatedRiskPerDateResult>.numberOfDaysForRisk(riskLevel: ProtoRiskLevel): Int =
+    private fun List<ExposureWindowDayRisk>.numberOfDaysForRisk(riskLevel: ProtoRiskLevel): Int =
         filter { it.riskLevel == riskLevel }
             .size
 
@@ -247,7 +247,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
         appConfig: ExposureWindowRiskCalculationConfig,
         dateMillisSinceEpoch: Long,
         exposureWindowsAndResult: Map<ExposureWindow, RiskResult>
-    ): AggregatedRiskPerDateResult? {
+    ): ExposureWindowDayRisk? {
         // 1. Group `Exposure Windows by Date`
         val exposureWindowsAndResultForDate = exposureWindowsAndResult
             .filter { it.key.dateMillisSinceEpoch == dateMillisSinceEpoch }
@@ -291,7 +291,7 @@ class DefaultRiskLevels @Inject constructor() : RiskLevels {
 
         Timber.d("minimumDistinctEncountersWithHighRisk: %d", minimumDistinctEncountersWithHighRisk)
 
-        return AggregatedRiskPerDateResult(
+        return ExposureWindowDayRisk(
             dateMillisSinceEpoch = dateMillisSinceEpoch,
             riskLevel = riskLevel,
             minimumDistinctEncountersWithLowRisk = minimumDistinctEncountersWithLowRisk,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelResult.kt
similarity index 58%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResult.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelResult.kt
index 1feb415e7..952b994c4 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResult.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelResult.kt
@@ -1,21 +1,21 @@
 package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import org.joda.time.Instant
 
-interface RiskLevelResult {
+interface EwRiskLevelResult {
     val calculatedAt: Instant
 
     val riskState: RiskState
         get() = when {
-            aggregatedRiskResult?.isIncreasedRisk() == true -> RiskState.INCREASED_RISK
-            aggregatedRiskResult?.isLowRisk() == true -> RiskState.LOW_RISK
+            ewAggregatedRiskResult?.isIncreasedRisk() == true -> RiskState.INCREASED_RISK
+            ewAggregatedRiskResult?.isLowRisk() == true -> RiskState.LOW_RISK
             else -> RiskState.CALCULATION_FAILED
         }
 
     val failureReason: FailureReason?
-    val aggregatedRiskResult: AggregatedRiskResult?
+    val ewAggregatedRiskResult: EwAggregatedRiskResult?
 
     /**
      * This will only be filled in deviceForTester builds
@@ -23,30 +23,30 @@ interface RiskLevelResult {
     val exposureWindows: List<ExposureWindow>?
 
     val wasSuccessfullyCalculated: Boolean
-        get() = aggregatedRiskResult != null
+        get() = ewAggregatedRiskResult != null
 
     val isIncreasedRisk: Boolean
-        get() = aggregatedRiskResult?.isIncreasedRisk() ?: false
+        get() = ewAggregatedRiskResult?.isIncreasedRisk() ?: false
 
     val matchedKeyCount: Int
         get() = if (isIncreasedRisk) {
-            aggregatedRiskResult?.totalMinimumDistinctEncountersWithHighRisk ?: 0
+            ewAggregatedRiskResult?.totalMinimumDistinctEncountersWithHighRisk ?: 0
         } else {
-            aggregatedRiskResult?.totalMinimumDistinctEncountersWithLowRisk ?: 0
+            ewAggregatedRiskResult?.totalMinimumDistinctEncountersWithLowRisk ?: 0
         }
 
     val daysWithEncounters: Int
         get() = if (isIncreasedRisk) {
-            aggregatedRiskResult?.numberOfDaysWithHighRisk ?: 0
+            ewAggregatedRiskResult?.numberOfDaysWithHighRisk ?: 0
         } else {
-            aggregatedRiskResult?.numberOfDaysWithLowRisk ?: 0
+            ewAggregatedRiskResult?.numberOfDaysWithLowRisk ?: 0
         }
 
     val lastRiskEncounterAt: Instant?
         get() = if (isIncreasedRisk) {
-            aggregatedRiskResult?.mostRecentDateWithHighRisk
+            ewAggregatedRiskResult?.mostRecentDateWithHighRisk
         } else {
-            aggregatedRiskResult?.mostRecentDateWithLowRisk
+            ewAggregatedRiskResult?.mostRecentDateWithLowRisk
         }
 
     enum class FailureReason(val failureCode: String) {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTaskResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelTaskResult.kt
similarity index 62%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTaskResult.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelTaskResult.kt
index deddc698d..40827be36 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTaskResult.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/EwRiskLevelTaskResult.kt
@@ -1,42 +1,42 @@
 package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.task.Task
 import org.joda.time.Instant
 
-data class RiskLevelTaskResult(
+data class EwRiskLevelTaskResult(
     override val calculatedAt: Instant,
-    override val failureReason: RiskLevelResult.FailureReason?,
-    override val aggregatedRiskResult: AggregatedRiskResult?,
+    override val failureReason: EwRiskLevelResult.FailureReason?,
+    override val ewAggregatedRiskResult: EwAggregatedRiskResult?,
     override val exposureWindows: List<ExposureWindow>?
-) : Task.Result, RiskLevelResult {
+) : Task.Result, EwRiskLevelResult {
 
     constructor(
         calculatedAt: Instant,
-        aggregatedRiskResult: AggregatedRiskResult,
+        ewAggregatedRiskResult: EwAggregatedRiskResult,
         exposureWindows: List<ExposureWindow>?
     ) : this(
         calculatedAt = calculatedAt,
-        aggregatedRiskResult = aggregatedRiskResult,
+        ewAggregatedRiskResult = ewAggregatedRiskResult,
         exposureWindows = exposureWindows,
         failureReason = null
     )
 
     constructor(
         calculatedAt: Instant,
-        failureReason: RiskLevelResult.FailureReason
+        failureReason: EwRiskLevelResult.FailureReason
     ) : this(
         calculatedAt = calculatedAt,
         failureReason = failureReason,
-        aggregatedRiskResult = null,
+        ewAggregatedRiskResult = null,
         exposureWindows = null
     )
 
     override fun toString(): String = "RiskLevelTaskResult(" +
         "calculatedAt=$calculatedAt, " +
         "failureReason=$failureReason, " +
-        "aggregatedRiskResult=$aggregatedRiskResult, " +
+        "ewAggregatedRiskResult=$ewAggregatedRiskResult, " +
         "exposureWindows.size=${exposureWindows?.size}" +
         ")"
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt
index 7b68e5789..d09e184b9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetector.kt
@@ -8,6 +8,7 @@ import de.rki.coronawarnapp.datadonation.analytics.storage.TestResultDonorSettin
 import de.rki.coronawarnapp.datadonation.survey.Surveys
 import de.rki.coronawarnapp.notification.GeneralNotifications
 import de.rki.coronawarnapp.notification.NotificationConstants.NEW_MESSAGE_RISK_LEVEL_SCORE_NOTIFICATION_ID
+import de.rki.coronawarnapp.risk.storage.CombinedEwPtRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.storage.TracingSettings
 import de.rki.coronawarnapp.submission.SubmissionSettings
@@ -42,36 +43,65 @@ class RiskLevelChangeDetector @Inject constructor(
 
     fun launch() {
         Timber.v("Monitoring risk level changes.")
-        riskLevelStorage.latestRiskLevelResults
+        riskLevelStorage.latestEwRiskLevelResults
             .map { results ->
                 results.sortedBy { it.calculatedAt }.takeLast(2)
             }
             .filter { it.size == 2 }
             .onEach {
                 Timber.v("Checking for risklevel change.")
-                check(it)
+                checkEwRiskForStateChanges(it)
+            }
+            .catch { Timber.e(it, "App config change checks failed.") }
+            .launchIn(appScope)
+
+        riskLevelStorage.latestCombinedEwPtRiskLevelResults
+            .map { results ->
+                results.sortedBy { it.calculatedAt }.takeLast(2)
+            }
+            .filter { it.size == 2 }
+            .onEach {
+                Timber.v("Checking for risklevel change.")
+                checkCombinedRiskForStateChanges(it)
             }
             .catch { Timber.e(it, "App config change checks failed.") }
             .launchIn(appScope)
     }
 
-    private suspend fun check(changedLevels: List<RiskLevelResult>) {
-        val oldResult = changedLevels.first()
-        val newResult = changedLevels.last()
+    private suspend fun checkCombinedRiskForStateChanges(results: List<CombinedEwPtRiskLevelResult>) {
+        // TODO refactor
+        val oldResult = results.first()
+        val newResult = results.last()
 
-        val lastCheckedResult = riskLevelSettings.lastChangeCheckedRiskLevelTimestamp
+        val lastCheckedResult = riskLevelSettings.lastChangeCheckedRiskLevelCombinedTimestamp
         if (lastCheckedResult == newResult.calculatedAt) {
             Timber.d("We already checked this risk level change, skipping further checks.")
             return
         }
-        riskLevelSettings.lastChangeCheckedRiskLevelTimestamp = newResult.calculatedAt
+        riskLevelSettings.lastChangeCheckedRiskLevelCombinedTimestamp = newResult.calculatedAt
 
         val oldRiskState = oldResult.riskState
         val newRiskState = newResult.riskState
-        Timber.d("Last state was $oldRiskState and current state is $newRiskState")
+        Timber.d("Last combined state was $oldRiskState and current state is $newRiskState")
 
         // Check sending a notification when risk level changes
         checkSendingNotification(oldRiskState, newRiskState)
+    }
+
+    private fun checkEwRiskForStateChanges(results: List<EwRiskLevelResult>) {
+        val oldResult = results.first()
+        val newResult = results.last()
+
+        val lastCheckedResult = riskLevelSettings.lastChangeCheckedRiskLevelTimestamp
+        if (lastCheckedResult == newResult.calculatedAt) {
+            Timber.d("We already checked this risk level change, skipping further checks.")
+            return
+        }
+        riskLevelSettings.lastChangeCheckedRiskLevelTimestamp = newResult.calculatedAt
+
+        val oldRiskState = oldResult.riskState
+        val newRiskState = newResult.riskState
+        Timber.d("Last state was $oldRiskState and current state is $newRiskState")
 
         // Save Survey related data based on the risk state
         saveSurveyRiskState(oldRiskState, newRiskState, newResult)
@@ -81,30 +111,30 @@ class RiskLevelChangeDetector @Inject constructor(
     }
 
     private fun saveTestDonorRiskLevelAnalytics(
-        newRiskState: RiskLevelResult
+        newEwRiskState: EwRiskLevelResult
     ) {
         // Save riskLevelTurnedRedTime if not already set before for high risk detection
         Timber.i("riskLevelTurnedRedTime=%s", testResultDonorSettings.riskLevelTurnedRedTime.value)
         if (testResultDonorSettings.riskLevelTurnedRedTime.value == null) {
-            if (newRiskState.isIncreasedRisk) {
+            if (newEwRiskState.isIncreasedRisk) {
                 testResultDonorSettings.riskLevelTurnedRedTime.update {
-                    newRiskState.calculatedAt
+                    newEwRiskState.calculatedAt
                 }
                 Timber.i(
                     "riskLevelTurnedRedTime: newRiskState=%s, riskLevelTurnedRedTime=%s",
-                    newRiskState.riskState,
-                    newRiskState.calculatedAt
+                    newEwRiskState.riskState,
+                    newEwRiskState.calculatedAt
                 )
             }
         }
 
         // Save most recent date of high or low risks
-        if (newRiskState.riskState in listOf(RiskState.INCREASED_RISK, RiskState.LOW_RISK)) {
-            Timber.d("newRiskState=$newRiskState")
-            val lastRiskEncounterAt = newRiskState.lastRiskEncounterAt
+        if (newEwRiskState.riskState in listOf(RiskState.INCREASED_RISK, RiskState.LOW_RISK)) {
+            Timber.d("newRiskState=$newEwRiskState")
+            val lastRiskEncounterAt = newEwRiskState.lastRiskEncounterAt
             Timber.i(
                 "mostRecentDateWithHighOrLowRiskLevel: newRiskState=%s, lastRiskEncounterAt=%s",
-                newRiskState.riskState,
+                newEwRiskState.riskState,
                 lastRiskEncounterAt
             )
 
@@ -142,7 +172,7 @@ class RiskLevelChangeDetector @Inject constructor(
     private fun saveSurveyRiskState(
         oldRiskState: RiskState,
         newRiskState: RiskState,
-        newResult: RiskLevelResult
+        newResult: EwRiskLevelResult
     ) {
         if (oldRiskState == RiskState.INCREASED_RISK && newRiskState == RiskState.LOW_RISK) {
             tracingSettings.isUserToBeNotifiedOfLoweredRiskLevel.update { true }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensions.kt
index 8911020fc..c47ff8c40 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensions.kt
@@ -1,15 +1,35 @@
 package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
+import de.rki.coronawarnapp.risk.storage.CombinedEwPtRiskLevelResult
 import org.joda.time.Instant
 
-fun List<RiskLevelResult>.tryLatestResultsWithDefaults(): DisplayableRiskResults {
+fun List<EwRiskLevelResult>.tryLatestEwResultsWithDefaults(): DisplayableEwRiskResults {
     val latestCalculation = this.maxByOrNull { it.calculatedAt }
-        ?: InitialLowLevelRiskLevelResult
+        ?: EwInitialLowRiskLevelResult
 
     val lastSuccessfullyCalculated = this.filter { it.wasSuccessfullyCalculated }
-        .maxByOrNull { it.calculatedAt } ?: UndeterminedRiskLevelResult
+        .maxByOrNull { it.calculatedAt } ?: EwUndeterminedRiskLevelResult
+
+    return DisplayableEwRiskResults(
+        lastCalculated = latestCalculation,
+        lastSuccessfullyCalculated = lastSuccessfullyCalculated
+    )
+}
+
+data class DisplayableEwRiskResults(
+    val lastCalculated: EwRiskLevelResult,
+    val lastSuccessfullyCalculated: EwRiskLevelResult
+)
+
+fun List<CombinedEwPtRiskLevelResult>.tryLatestResultsWithDefaults(): DisplayableRiskResults {
+    val latestCalculation = this.maxByOrNull { it.calculatedAt }
+        ?: initialLowLevelEwRiskLevelResult
+
+    val lastSuccessfullyCalculated = this.filter { it.wasSuccessfullyCalculated }
+        .maxByOrNull { it.calculatedAt } ?: undeterminedEwRiskLevelResult
 
     return DisplayableRiskResults(
         lastCalculated = latestCalculation,
@@ -18,25 +38,41 @@ fun List<RiskLevelResult>.tryLatestResultsWithDefaults(): DisplayableRiskResults
 }
 
 data class DisplayableRiskResults(
-    val lastCalculated: RiskLevelResult,
-    val lastSuccessfullyCalculated: RiskLevelResult
+    val lastCalculated: CombinedEwPtRiskLevelResult,
+    val lastSuccessfullyCalculated: CombinedEwPtRiskLevelResult
+)
+
+private val undeterminedEwRiskLevelResult = CombinedEwPtRiskLevelResult(
+    PtRiskLevelResult(
+        calculatedAt = Instant.EPOCH,
+        riskState = RiskState.CALCULATION_FAILED
+    ),
+    EwUndeterminedRiskLevelResult
+)
+
+private val initialLowLevelEwRiskLevelResult = CombinedEwPtRiskLevelResult(
+    PtRiskLevelResult(
+        calculatedAt = Instant.now(),
+        riskState = RiskState.LOW_RISK
+    ),
+    EwInitialLowRiskLevelResult
 )
 
-private object InitialLowLevelRiskLevelResult : RiskLevelResult {
+private object EwInitialLowRiskLevelResult : EwRiskLevelResult {
     override val calculatedAt: Instant = Instant.now()
     override val riskState: RiskState = RiskState.LOW_RISK
-    override val failureReason: RiskLevelResult.FailureReason? = null
-    override val aggregatedRiskResult: AggregatedRiskResult? = null
+    override val failureReason: EwRiskLevelResult.FailureReason? = null
+    override val ewAggregatedRiskResult: EwAggregatedRiskResult? = null
     override val exposureWindows: List<ExposureWindow>? = null
     override val matchedKeyCount: Int = 0
     override val daysWithEncounters: Int = 0
 }
 
-private object UndeterminedRiskLevelResult : RiskLevelResult {
+private object EwUndeterminedRiskLevelResult : EwRiskLevelResult {
     override val calculatedAt: Instant = Instant.EPOCH
     override val riskState: RiskState = RiskState.CALCULATION_FAILED
-    override val failureReason: RiskLevelResult.FailureReason? = null
-    override val aggregatedRiskResult: AggregatedRiskResult? = null
+    override val failureReason: EwRiskLevelResult.FailureReason? = null
+    override val ewAggregatedRiskResult: EwAggregatedRiskResult? = null
     override val exposureWindows: List<ExposureWindow>? = null
     override val matchedKeyCount: Int = 0
     override val daysWithEncounters: Int = 0
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelSettings.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelSettings.kt
index 652905a0d..d11ccaa23 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelSettings.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelSettings.kt
@@ -41,11 +41,21 @@ class RiskLevelSettings @Inject constructor(
             putLong(PKEY_LAST_CHANGE_TO_HIGH_RISKLEVEL_TIMESTAMP, value?.millis ?: 0L)
         }
 
+    var lastChangeCheckedRiskLevelCombinedTimestamp: Instant?
+        get() = prefs.getLong(PKEY_LAST_CHANGE_CHECKED_RISKLEVEL_TIMESTAMP_COMBINED, 0L).let {
+            if (it != 0L) Instant.ofEpochMilli(it) else null
+        }
+        set(value) = prefs.edit {
+            putLong(PKEY_LAST_CHANGE_CHECKED_RISKLEVEL_TIMESTAMP_COMBINED, value?.millis ?: 0L)
+        }
+
     companion object {
         private const val NAME_SHARED_PREFS = "risklevel_localdata"
         private const val PKEY_RISKLEVEL_CALC_LAST_CONFIG_ID = "risklevel.config.identifier.last"
         private const val PKEY_LAST_CHANGE_CHECKED_RISKLEVEL_TIMESTAMP = "PKEY_RISKLEVEL_CALC_LAST_CONFIG_ID"
         private const val PKEY_LAST_CHANGE_TO_HIGH_RISKLEVEL_TIMESTAMP =
             "PKEY_RISKLEVEL_CALC_LAST_CHANGE_TO_HIGH_RISKLEVEL"
+        private const val PKEY_LAST_CHANGE_CHECKED_RISKLEVEL_TIMESTAMP_COMBINED =
+            "PKEY_LAST_CHANGE_CHECKED_RISKLEVEL_TIMESTAMP_COMBINED"
     }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt
index dbb6257fc..c79f789b7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevelTask.kt
@@ -13,8 +13,8 @@ import de.rki.coronawarnapp.nearby.ENFClient
 import de.rki.coronawarnapp.nearby.modules.detectiontracker.ExposureDetectionTracker
 import de.rki.coronawarnapp.nearby.modules.detectiontracker.TrackedExposureDetection
 import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut
-import de.rki.coronawarnapp.risk.RiskLevelResult.FailureReason
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult.FailureReason
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.submission.SubmissionSettings
 import de.rki.coronawarnapp.task.Task
@@ -49,14 +49,14 @@ class RiskLevelTask @Inject constructor(
     private val submissionSettings: SubmissionSettings,
     private val analyticsExposureWindowCollector: AnalyticsExposureWindowCollector,
     private val autoCheckOut: AutoCheckOut,
-) : Task<DefaultProgress, RiskLevelTaskResult> {
+) : Task<DefaultProgress, EwRiskLevelTaskResult> {
 
     private val internalProgress = ConflatedBroadcastChannel<DefaultProgress>()
     override val progress: Flow<DefaultProgress> = internalProgress.asFlow()
 
     private var isCanceled = false
 
-    override suspend fun run(arguments: Task.Arguments): RiskLevelTaskResult = try {
+    override suspend fun run(arguments: Task.Arguments): EwRiskLevelTaskResult = try {
         Timber.d("Running with arguments=%s", arguments)
 
         autoCheckOut.apply {
@@ -86,14 +86,14 @@ class RiskLevelTask @Inject constructor(
         internalProgress.close()
     }
 
-    private suspend fun determineRiskLevelResult(configData: ConfigData): RiskLevelTaskResult {
+    private suspend fun determineRiskLevelResult(configData: ConfigData): EwRiskLevelTaskResult {
         val nowUTC = timeStamper.nowUTC.also {
             Timber.d("The current time is %s", it)
         }
 
         if (submissionSettings.isAllowedToSubmitKeys && submissionSettings.hasViewedTestResult.value) {
             Timber.i("Positive test result and user has seen it, skip risk calculation")
-            return RiskLevelTaskResult(
+            return EwRiskLevelTaskResult(
                 calculatedAt = nowUTC,
                 failureReason = FailureReason.POSITIVE_TEST_RESULT
             )
@@ -101,7 +101,7 @@ class RiskLevelTask @Inject constructor(
 
         if (!configData.isDeviceTimeCorrect) {
             Timber.w("Device time is incorrect, offset: %s", configData.localOffset)
-            return RiskLevelTaskResult(
+            return EwRiskLevelTaskResult(
                 calculatedAt = nowUTC,
                 failureReason = FailureReason.INCORRECT_DEVICE_TIME
             )
@@ -109,7 +109,7 @@ class RiskLevelTask @Inject constructor(
 
         if (!isNetworkEnabled(context)) {
             Timber.i("Risk not calculated, internet unavailable.")
-            return RiskLevelTaskResult(
+            return EwRiskLevelTaskResult(
                 calculatedAt = nowUTC,
                 failureReason = FailureReason.NO_INTERNET
             )
@@ -117,7 +117,7 @@ class RiskLevelTask @Inject constructor(
 
         if (!enfClient.isTracingEnabled.first()) {
             Timber.i("Risk not calculated, tracing is disabled.")
-            return RiskLevelTaskResult(
+            return EwRiskLevelTaskResult(
                 calculatedAt = nowUTC,
                 failureReason = FailureReason.TRACING_OFF
             )
@@ -125,7 +125,7 @@ class RiskLevelTask @Inject constructor(
 
         if (areKeyPkgsOutDated(nowUTC)) {
             Timber.i("Risk not calculated, results are outdated.")
-            return RiskLevelTaskResult(
+            return EwRiskLevelTaskResult(
                 calculatedAt = nowUTC,
                 failureReason = when (backgroundJobsEnabled()) {
                     true -> FailureReason.OUTDATED_RESULTS
@@ -162,7 +162,7 @@ class RiskLevelTask @Inject constructor(
         }
     }
 
-    private suspend fun calculateRiskLevel(configData: ExposureWindowRiskCalculationConfig): RiskLevelTaskResult {
+    private suspend fun calculateRiskLevel(configData: ExposureWindowRiskCalculationConfig): EwRiskLevelTaskResult {
         Timber.tag(TAG).d("Calculating risklevel")
         val exposureWindows = enfClient.exposureWindows()
 
@@ -174,9 +174,9 @@ class RiskLevelTask @Inject constructor(
                 Timber.tag(TAG).d("Risk is not increased, continuing evaluating.")
             }
 
-            RiskLevelTaskResult(
+            EwRiskLevelTaskResult(
                 calculatedAt = timeStamper.nowUTC,
-                aggregatedRiskResult = it,
+                ewAggregatedRiskResult = it,
                 exposureWindows = exposureWindows
             )
         }
@@ -185,7 +185,7 @@ class RiskLevelTask @Inject constructor(
     private suspend fun determineRisk(
         appConfig: ExposureWindowRiskCalculationConfig,
         exposureWindows: List<ExposureWindow>
-    ): AggregatedRiskResult {
+    ): EwAggregatedRiskResult {
         val riskResultsPerWindow =
             exposureWindows.mapNotNull { window ->
                 riskLevels.calculateRisk(appConfig, window)?.let { window to it }
@@ -235,10 +235,10 @@ class RiskLevelTask @Inject constructor(
     class Factory @Inject constructor(
         private val taskByDagger: Provider<RiskLevelTask>,
         private val exposureDetectionTracker: ExposureDetectionTracker
-    ) : TaskFactory<DefaultProgress, RiskLevelTaskResult> {
+    ) : TaskFactory<DefaultProgress, EwRiskLevelTaskResult> {
 
         override suspend fun createConfig(): TaskFactory.Config = Config(exposureDetectionTracker)
-        override val taskProvider: () -> Task<DefaultProgress, RiskLevelTaskResult> = {
+        override val taskProvider: () -> Task<DefaultProgress, EwRiskLevelTaskResult> = {
             taskByDagger.get()
         }
     }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevels.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevels.kt
index 30089d352..0344e1c8b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevels.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/RiskLevels.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import de.rki.coronawarnapp.appconfig.ExposureWindowRiskCalculationConfig
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.result.RiskResult
 
 interface RiskLevels {
@@ -15,5 +15,5 @@ interface RiskLevels {
     fun aggregateResults(
         appConfig: ExposureWindowRiskCalculationConfig,
         exposureWindowResultMap: Map<ExposureWindow, RiskResult>
-    ): AggregatedRiskResult
+    ): EwAggregatedRiskResult
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/EwAggregatedRiskResult.kt
similarity index 86%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/EwAggregatedRiskResult.kt
index 45130825e..b17b64e0d 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskResult.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/EwAggregatedRiskResult.kt
@@ -4,7 +4,7 @@ import de.rki.coronawarnapp.risk.ProtoRiskLevel
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import org.joda.time.Instant
 
-data class AggregatedRiskResult(
+data class EwAggregatedRiskResult(
     val totalRiskLevel: RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel,
     val totalMinimumDistinctEncountersWithLowRisk: Int,
     val totalMinimumDistinctEncountersWithHighRisk: Int,
@@ -12,7 +12,7 @@ data class AggregatedRiskResult(
     val mostRecentDateWithHighRisk: Instant?,
     val numberOfDaysWithLowRisk: Int,
     val numberOfDaysWithHighRisk: Int,
-    var aggregatedRiskPerDateResults: List<AggregatedRiskPerDateResult>? = null
+    var exposureWindowDayRisks: List<ExposureWindowDayRisk>? = null
 ) {
 
     fun isIncreasedRisk(): Boolean = totalRiskLevel == ProtoRiskLevel.HIGH
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRisk.kt
similarity index 93%
rename from Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt
rename to Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRisk.kt
index b2aed66ee..a4dc9cbe7 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResult.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRisk.kt
@@ -4,7 +4,7 @@ import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParamete
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import org.joda.time.Instant
 
-data class AggregatedRiskPerDateResult(
+data class ExposureWindowDayRisk(
     val dateMillisSinceEpoch: Long,
     val riskLevel: RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel,
     val minimumDistinctEncountersWithLowRisk: Int,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt
index 1362d76cc..a0844cd0b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorage.kt
@@ -3,25 +3,30 @@ package de.rki.coronawarnapp.risk.storage
 import androidx.annotation.VisibleForTesting
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingDayRisk
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
+import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult
 import de.rki.coronawarnapp.presencetracing.risk.mapToRiskState
-import de.rki.coronawarnapp.risk.RiskLevelResult
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TraceLocationCheckInRisk
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.toPersistedAggregatedRiskPerDateResult
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.toPersistedRiskResult
 import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDaoWrapper
-import de.rki.coronawarnapp.util.flow.combine
 import de.rki.coronawarnapp.util.flow.shareLatest
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.map
+import org.joda.time.Instant
+import org.joda.time.LocalDate
 import timber.log.Timber
 import java.lang.reflect.Modifier.PRIVATE
+import kotlin.math.max
+import de.rki.coronawarnapp.util.flow.combine as flowCombine
 
 abstract class BaseRiskLevelStorage constructor(
     private val riskResultDatabaseFactory: RiskResultDatabase.Factory,
@@ -38,7 +43,7 @@ abstract class BaseRiskLevelStorage constructor(
 
     private suspend fun List<PersistedRiskLevelResultDao>.combineWithWindows(
         givenWindows: List<PersistedExposureWindowDaoWrapper>?
-    ): List<RiskLevelTaskResult> {
+    ): List<EwRiskLevelTaskResult> {
         if (this.isEmpty()) return emptyList()
 
         val windows = if (givenWindows != null) {
@@ -61,7 +66,7 @@ abstract class BaseRiskLevelStorage constructor(
         }
     }
 
-    final override val allRiskLevelResults: Flow<List<RiskLevelResult>> = combine(
+    final override val allEwRiskLevelResults: Flow<List<EwRiskLevelResult>> = combine(
         riskResultsTables.allEntries(),
         exposureWindowsTables.allEntries()
     ) { allRiskResults, allWindows ->
@@ -74,42 +79,35 @@ abstract class BaseRiskLevelStorage constructor(
     }
         .shareLatest(tag = TAG, scope = scope)
 
-    override val latestRiskLevelResults: Flow<List<RiskLevelResult>> = riskResultsTables.latestEntries(2)
+    override val latestEwRiskLevelResults: Flow<List<EwRiskLevelResult>> = riskResultsTables.latestEntries(2)
         .map { results ->
             Timber.v("Mapping latestRiskLevelResults:\n%s", results.joinToString("\n"))
             results.combineWithWindows(null)
         }
         .shareLatest(tag = TAG, scope = scope)
 
-    override val latestAndLastSuccessful: Flow<List<RiskLevelResult>> = riskResultsTables.latestAndLastSuccessful()
-        .map { results ->
-            Timber.v("Mapping latestAndLastSuccessful:\n%s", results.joinToString("\n"))
-            results.combineWithWindows(null)
-        }
-        .shareLatest(tag = TAG, scope = scope)
-
-    override suspend fun storeResult(result: RiskLevelResult) {
-        Timber.d("Storing result (exposureWindows.size=%s)", result.exposureWindows?.size)
+    override suspend fun storeResult(resultEw: EwRiskLevelResult) {
+        Timber.d("Storing result (exposureWindows.size=%s)", resultEw.exposureWindows?.size)
 
         val storedResultId = try {
             val startTime = System.currentTimeMillis()
 
-            require(result.aggregatedRiskResult == null || result.failureReason == null) {
+            require(resultEw.ewAggregatedRiskResult == null || resultEw.failureReason == null) {
                 "A result needs to have either an aggregatedRiskResult or a failureReason, not both!"
             }
 
-            val resultToPersist = result.toPersistedRiskResult()
+            val resultToPersist = resultEw.toPersistedRiskResult()
             riskResultsTables.insertEntry(resultToPersist).also {
                 Timber.d("Storing RiskLevelResult took %dms.", (System.currentTimeMillis() - startTime))
             }
 
-            result.aggregatedRiskResult?.aggregatedRiskPerDateResults?.let {
+            resultEw.ewAggregatedRiskResult?.exposureWindowDayRisks?.let {
                 insertAggregatedRiskPerDateResults(it)
             }
 
             resultToPersist.id
         } catch (e: Exception) {
-            Timber.e(e, "Failed to store latest result: %s", result)
+            Timber.e(e, "Failed to store latest result: %s", resultEw)
             throw e
         }
 
@@ -125,13 +123,13 @@ abstract class BaseRiskLevelStorage constructor(
         }
 
         Timber.d("Storing exposure windows.")
-        storeExposureWindows(storedResultId = storedResultId, result)
+        storeExposureWindows(storedResultId = storedResultId, resultEw)
 
         Timber.d("Deleting orphaned exposure windows.")
         deletedOrphanedExposureWindows()
     }
 
-    override val aggregatedRiskPerDateResults: Flow<List<AggregatedRiskPerDateResult>> by lazy {
+    override val ewDayRiskStates: Flow<List<ExposureWindowDayRisk>> by lazy {
         aggregatedRiskPerDateResultTables.allEntries()
             .map {
                 it.map { persistedAggregatedRiskPerDateResult ->
@@ -142,12 +140,12 @@ abstract class BaseRiskLevelStorage constructor(
     }
 
     private suspend fun insertAggregatedRiskPerDateResults(
-        aggregatedRiskPerDateResults: List<AggregatedRiskPerDateResult>
+        exposureWindowDayRisks: List<ExposureWindowDayRisk>
     ) {
-        Timber.d("insertAggregatedRiskPerDateResults(aggregatedRiskPerDateResults=%s)", aggregatedRiskPerDateResults)
+        Timber.d("insertAggregatedRiskPerDateResults(aggregatedRiskPerDateResults=%s)", exposureWindowDayRisks)
         try {
             aggregatedRiskPerDateResultTables.insertRisk(
-                aggregatedRiskPerDateResults.map {
+                exposureWindowDayRisks.map {
                     it.toPersistedAggregatedRiskPerDateResult()
                 }
             )
@@ -156,7 +154,7 @@ abstract class BaseRiskLevelStorage constructor(
         }
     }
 
-    override suspend fun deleteAggregatedRiskPerDateResults(results: List<AggregatedRiskPerDateResult>) {
+    override suspend fun deleteAggregatedRiskPerDateResults(results: List<ExposureWindowDayRisk>) {
         Timber.d("deleteAggregatedRiskPerDateResults(results=%s)", results)
         try {
             aggregatedRiskPerDateResultTables.delete(results.map { it.toPersistedAggregatedRiskPerDateResult() })
@@ -168,23 +166,105 @@ abstract class BaseRiskLevelStorage constructor(
     override val traceLocationCheckInRiskStates: Flow<List<TraceLocationCheckInRisk>> =
         presenceTracingRiskRepository.traceLocationCheckInRiskStates
 
-    override val presenceTracingDayRisk: Flow<List<PresenceTracingDayRisk>> =
+    override val ptDayRiskStates: Flow<List<PresenceTracingDayRisk>> =
         presenceTracingRiskRepository.presenceTracingDayRisk
 
-    override val aggregatedDayRisk: Flow<List<AggregatedDayRisk>>
-        get() = combine(
-            presenceTracingDayRisk,
-            aggregatedRiskPerDateResults
+    override val combinedEwPtDayRisk: Flow<List<CombinedEwPtDayRisk>>
+        get() = flowCombine(
+            ptDayRiskStates,
+            ewDayRiskStates
         ) { ptRiskList, ewRiskList ->
             combineRisk(ptRiskList, ewRiskList)
         }
 
-    internal abstract suspend fun storeExposureWindows(storedResultId: String, result: RiskLevelResult)
+    override val latestAndLastSuccessfulEwRiskLevelResult: Flow<List<EwRiskLevelResult>> = riskResultsTables
+        .latestAndLastSuccessful()
+        .map { results ->
+            Timber.v("Mapping latestAndLastSuccessful:\n%s", results.joinToString("\n"))
+            results.combineWithWindows(null)
+        }
+        .shareLatest(tag = TAG, scope = scope)
+
+    private val latestAndLastSuccessfulPtRiskLevelResult: Flow<List<PtRiskLevelResult>> =
+        presenceTracingRiskRepository
+            .latestAndLastSuccessful()
+            .shareLatest(tag = TAG, scope = scope)
+
+    // TODO maybe refactor
+    override val latestAndLastSuccessfulCombinedEwPtRiskLevelResult: Flow<List<CombinedEwPtRiskLevelResult>>
+        get() = combine(
+            latestAndLastSuccessfulEwRiskLevelResult,
+            latestAndLastSuccessfulPtRiskLevelResult
+        ) { ewRiskLevelResults, ptRiskLevelResults ->
+            val latestEwResult = ewRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val latestPtResult = ptRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val combinedList = mutableListOf<CombinedEwPtRiskLevelResult>()
+            if (latestEwResult != null && latestPtResult != null) {
+                combinedList.add(
+                    CombinedEwPtRiskLevelResult(
+                        ewRiskLevelResult = latestEwResult,
+                        ptRiskLevelResult = latestPtResult
+                    )
+                )
+            }
+            val lastSuccessfulEwResult = ewRiskLevelResults
+                .filter { it.wasSuccessfullyCalculated }.maxByOrNull { it.calculatedAt }
+            val lastSuccessfulPtResult = ptRiskLevelResults
+                .filter { it.wasSuccessfullyCalculated }.maxByOrNull { it.calculatedAt }
+            if (lastSuccessfulEwResult != null && lastSuccessfulPtResult != null) {
+                combinedList.add(
+                    CombinedEwPtRiskLevelResult(
+                        ewRiskLevelResult = lastSuccessfulEwResult,
+                        // current ptDayRiskStates belong to the last successful calculation - ugly
+                        ptRiskLevelResult = lastSuccessfulPtResult.copy(
+                            presenceTracingDayRisk = ptDayRiskStates.first()
+                        )
+                    )
+                )
+            }
+            combinedList
+        }
+
+    private val latestPtRiskLevelResults: Flow<List<PtRiskLevelResult>> =
+        presenceTracingRiskRepository
+            .latestEntries(2)
+            .shareLatest(tag = TAG, scope = scope)
+
+    override val latestCombinedEwPtRiskLevelResults: Flow<List<CombinedEwPtRiskLevelResult>>
+        get() = combine(
+            latestEwRiskLevelResults,
+            latestPtRiskLevelResults
+        ) { ewRiskLevelResults, ptRiskLevelResults ->
+            val latestEwResult = ewRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val latestPtResult = ptRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val olderEwResult = ewRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val olderPtResult = ptRiskLevelResults.maxByOrNull { it.calculatedAt }
+            val combinedList = mutableListOf<CombinedEwPtRiskLevelResult>()
+            if (latestEwResult != null && latestPtResult != null) {
+                combinedList.add(
+                    CombinedEwPtRiskLevelResult(
+                        ewRiskLevelResult = latestEwResult,
+                        ptRiskLevelResult = latestPtResult
+                    )
+                )
+            }
+            if (olderEwResult != null && olderPtResult != null) {
+                combinedList.add(
+                    CombinedEwPtRiskLevelResult(
+                        ewRiskLevelResult = olderEwResult,
+                        ptRiskLevelResult = olderPtResult
+                    )
+                )
+            }
+            combinedList
+        }
+
+    internal abstract suspend fun storeExposureWindows(storedResultId: String, resultEw: EwRiskLevelResult)
 
     internal abstract suspend fun deletedOrphanedExposureWindows()
 
     override suspend fun clear() {
-        Timber.w("clear() - Clearing stored riskleve/exposure-detection results.")
+        Timber.w("clear() - Clearing stored risklevel/exposure-detection results.")
         database.clearAllTables()
     }
 
@@ -196,13 +276,13 @@ abstract class BaseRiskLevelStorage constructor(
 @VisibleForTesting(otherwise = PRIVATE)
 internal fun combineRisk(
     ptRiskList: List<PresenceTracingDayRisk>,
-    ewRiskList: List<AggregatedRiskPerDateResult>
-): List<AggregatedDayRisk> {
-    val allDates = ptRiskList.map { it.localDateUtc }.plus(ewRiskList.map { it.localDateUtc }).distinct()
+    exposureWindowDayRiskList: List<ExposureWindowDayRisk>
+): List<CombinedEwPtDayRisk> {
+    val allDates = ptRiskList.map { it.localDateUtc }.plus(exposureWindowDayRiskList.map { it.localDateUtc }).distinct()
     return allDates.map { date ->
         val ptRisk = ptRiskList.find { it.localDateUtc == date }
-        val ewRisk = ewRiskList.find { it.localDateUtc == date }
-        AggregatedDayRisk(
+        val ewRisk = exposureWindowDayRiskList.find { it.localDateUtc == date }
+        CombinedEwPtDayRisk(
             date,
             max(
                 ptRisk?.riskState,
@@ -212,9 +292,19 @@ internal fun combineRisk(
     }
 }
 
-@VisibleForTesting(otherwise = PRIVATE)
 internal fun max(left: RiskState?, right: RiskState?): RiskState {
     return if (left == RiskState.INCREASED_RISK || right == RiskState.INCREASED_RISK) RiskState.INCREASED_RISK
     else if (left == RiskState.LOW_RISK || right == RiskState.LOW_RISK) RiskState.LOW_RISK
     else RiskState.CALCULATION_FAILED
 }
+
+internal fun max(left: Instant, right: Instant): Instant {
+    return Instant.ofEpochMilli(max(left.millis, right.millis))
+}
+
+internal fun max(left: LocalDate?, right: LocalDate?): LocalDate? {
+    if (left == null) return right
+    if (right == null) return left
+    return if (left.isAfter(right)) left
+    else right
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt
index a458989c8..46d4366ef 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/RiskLevelStorage.kt
@@ -1,71 +1,129 @@
 package de.rki.coronawarnapp.risk.storage
 
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingDayRisk
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TraceLocationCheckInRisk
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import kotlinx.coroutines.flow.Flow
+import org.joda.time.Instant
 import org.joda.time.LocalDate
 
 interface RiskLevelStorage {
 
-    /**
+    /** EXPOSURE WINDOW RISK RESULT
      * All currently available risk results.
      * This is an expensive operation on tester builds due to mapping all available windows.
      * Newest item first.
      */
-    val allRiskLevelResults: Flow<List<RiskLevelResult>>
+    val allEwRiskLevelResults: Flow<List<EwRiskLevelResult>>
 
-    /**
+    /** EXPOSURE WINDOW RISK RESULT
      * The newest 2 results.
-     * Use by the risklevel detector to check for state changes (LOW/INCREASED RISK).
+     * Use by the risk level detector to check for state changes (LOW/INCREASED RISK),
+     * collecting data for analytics and survey.
      * Can be 0-2 entries.
      * Newest item first.
      */
-    val latestRiskLevelResults: Flow<List<RiskLevelResult>>
+    val latestEwRiskLevelResults: Flow<List<EwRiskLevelResult>>
 
-    /**
-     * The newest result, and the last successfully result result.
-     * Used by the tracing info cards in home and details screen.
+    /** COMBINED RISK RESULT
+     * The newest 2 results.
+     * Use by the risk level detector to check for state changes (LOW/INCREASED RISK) triggering NOTIFICATION.
+     * Can be 0-2 entries.
+     * Newest item first.
+     */
+    val latestCombinedEwPtRiskLevelResults: Flow<List<CombinedEwPtRiskLevelResult>>
+
+    /** EXPOSURE WINDOW RISK RESULT
+     * The newest result, and the last successfully result.
+     * Used only for analytics
+     * Can be 0-2 entries.
+     * Newest item first.
+     */
+    val latestAndLastSuccessfulEwRiskLevelResult: Flow<List<EwRiskLevelResult>>
+
+    /** COMBINED RISK RESULT
+     * The newest result, and the last successfully result for ew and pt combined.
+     * Used for TRACING info cards in HOME and DETAILS SCREEN.
      * Can be 0-2 entries.
      * Newest item first.
      */
-    val latestAndLastSuccessful: Flow<List<RiskLevelResult>>
+    val latestAndLastSuccessfulCombinedEwPtRiskLevelResult: Flow<List<CombinedEwPtRiskLevelResult>>
 
-    /**
+    /** EXPOSURE WINDOW RISK RESULT
      * Risk level per date/day
      * Used by contact diary overview
      * Item with newest date first.
      */
-    val aggregatedRiskPerDateResults: Flow<List<AggregatedRiskPerDateResult>>
+    val ewDayRiskStates: Flow<List<ExposureWindowDayRisk>>
 
-    /**
+    /** PRESENCE TRACING RISK RESULT
      * Risk level per date/day and checkIn
      * Used by contact diary overview
      */
     val traceLocationCheckInRiskStates: Flow<List<TraceLocationCheckInRisk>>
 
-    /**
+    /** PRESENCE TRACING RISK RESULT
      * Risk level per date/day aggregated over check-ins
-     * Used by contact diary overview
      */
-    val presenceTracingDayRisk: Flow<List<PresenceTracingDayRisk>>
+    val ptDayRiskStates: Flow<List<PresenceTracingDayRisk>>
 
-    /**
+    /** COMBINED RISK RESULT
      * Risk level per date/day aggregated form Exposure Windows and Presence Tracing
-     * Used by contact diary overview
      */
-    val aggregatedDayRisk: Flow<List<AggregatedDayRisk>>
+    val combinedEwPtDayRisk: Flow<List<CombinedEwPtDayRisk>>
 
-    suspend fun deleteAggregatedRiskPerDateResults(results: List<AggregatedRiskPerDateResult>)
+    suspend fun deleteAggregatedRiskPerDateResults(results: List<ExposureWindowDayRisk>)
 
-    suspend fun storeResult(result: RiskLevelResult)
+    suspend fun storeResult(resultEw: EwRiskLevelResult)
 
     suspend fun clear()
 }
 
-data class AggregatedDayRisk(
+data class CombinedEwPtDayRisk(
     val localDate: LocalDate,
     val riskState: RiskState
 )
+
+data class CombinedEwPtRiskLevelResult(
+    val ptRiskLevelResult: PtRiskLevelResult,
+    val ewRiskLevelResult: EwRiskLevelResult
+) {
+
+    val riskState: RiskState = max(ptRiskLevelResult.riskState, ewRiskLevelResult.riskState)
+
+    val wasSuccessfullyCalculated: Boolean
+        get() = ewRiskLevelResult.ewAggregatedRiskResult != null &&
+            ptRiskLevelResult.riskState != RiskState.CALCULATION_FAILED
+
+    val calculatedAt: Instant = max(ewRiskLevelResult.calculatedAt, ptRiskLevelResult.calculatedAt)
+
+    val daysWithEncounters: Int
+        get() = when (riskState) {
+            RiskState.INCREASED_RISK -> {
+                (ewRiskLevelResult.ewAggregatedRiskResult?.numberOfDaysWithHighRisk ?: 0) +
+                    ptRiskLevelResult.numberOfDaysWithHighRisk
+            }
+            RiskState.LOW_RISK -> {
+                (ewRiskLevelResult.ewAggregatedRiskResult?.numberOfDaysWithLowRisk ?: 0) +
+                    ptRiskLevelResult.numberOfDaysWithLowRisk
+            }
+            else -> 0
+        }
+
+    val lastRiskEncounterAt: LocalDate?
+        get() = if (riskState == RiskState.INCREASED_RISK) {
+            max(
+                ewRiskLevelResult.ewAggregatedRiskResult?.mostRecentDateWithHighRisk?.toLocalDateUtc(),
+                ptRiskLevelResult.mostRecentDateWithHighRisk
+            )
+        } else {
+            max(
+                ewRiskLevelResult.ewAggregatedRiskResult?.mostRecentDateWithLowRisk?.toLocalDateUtc(),
+                ptRiskLevelResult.mostRecentDateWithLowRisk
+            )
+        }
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt
index c36936025..dcd8a3bca 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedAggregatedRiskPerDateResult.kt
@@ -3,7 +3,7 @@ package de.rki.coronawarnapp.risk.storage.internal.riskresults
 import androidx.room.ColumnInfo
 import androidx.room.Entity
 import androidx.room.PrimaryKey
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 
 @Suppress("MaxLineLength")
@@ -14,8 +14,8 @@ data class PersistedAggregatedRiskPerDateResult(
     @ColumnInfo(name = "minimumDistinctEncountersWithLowRisk") val minimumDistinctEncountersWithLowRisk: Int,
     @ColumnInfo(name = "minimumDistinctEncountersWithHighRisk") val minimumDistinctEncountersWithHighRisk: Int
 ) {
-    fun toAggregatedRiskPerDateResult(): AggregatedRiskPerDateResult =
-        AggregatedRiskPerDateResult(
+    fun toAggregatedRiskPerDateResult(): ExposureWindowDayRisk =
+        ExposureWindowDayRisk(
             dateMillisSinceEpoch = dateMillisSinceEpoch,
             riskLevel = riskLevel,
             minimumDistinctEncountersWithLowRisk = minimumDistinctEncountersWithLowRisk,
@@ -23,7 +23,7 @@ data class PersistedAggregatedRiskPerDateResult(
         )
 }
 
-fun AggregatedRiskPerDateResult.toPersistedAggregatedRiskPerDateResult(): PersistedAggregatedRiskPerDateResult =
+fun ExposureWindowDayRisk.toPersistedAggregatedRiskPerDateResult(): PersistedAggregatedRiskPerDateResult =
     PersistedAggregatedRiskPerDateResult(
         dateMillisSinceEpoch = dateMillisSinceEpoch,
         riskLevel = riskLevel,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskLevelResultDao.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskLevelResultDao.kt
index 1be333a7f..b3d26710e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskLevelResultDao.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskLevelResultDao.kt
@@ -5,9 +5,9 @@ import androidx.room.Embedded
 import androidx.room.Entity
 import androidx.room.PrimaryKey
 import androidx.room.TypeConverter
-import de.rki.coronawarnapp.risk.RiskLevelResult.FailureReason
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult.FailureReason
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDaoWrapper
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping
 import org.joda.time.Instant
@@ -24,9 +24,9 @@ data class PersistedRiskLevelResultDao(
 
     fun toRiskResult(exposureWindows: List<PersistedExposureWindowDaoWrapper>? = null) = when {
         aggregatedRiskResult != null -> {
-            RiskLevelTaskResult(
+            EwRiskLevelTaskResult(
                 calculatedAt = calculatedAt,
-                aggregatedRiskResult = aggregatedRiskResult.toAggregatedRiskResult(),
+                ewAggregatedRiskResult = aggregatedRiskResult.toAggregatedRiskResult(),
                 exposureWindows = exposureWindows?.map { it.toExposureWindow() }
             )
         }
@@ -34,7 +34,7 @@ data class PersistedRiskLevelResultDao(
             if (failureReason == null) {
                 Timber.e("Entry contained no aggregateResult and no failure reason, shouldn't happen.")
             }
-            RiskLevelTaskResult(
+            EwRiskLevelTaskResult(
                 calculatedAt = calculatedAt,
                 failureReason = failureReason ?: FailureReason.UNKNOWN
             )
@@ -58,7 +58,7 @@ data class PersistedRiskLevelResultDao(
         val numberOfDaysWithHighRisk: Int
     ) {
 
-        fun toAggregatedRiskResult() = AggregatedRiskResult(
+        fun toAggregatedRiskResult() = EwAggregatedRiskResult(
             totalRiskLevel = totalRiskLevel,
             totalMinimumDistinctEncountersWithLowRisk = totalMinimumDistinctEncountersWithLowRisk,
             totalMinimumDistinctEncountersWithHighRisk = totalMinimumDistinctEncountersWithHighRisk,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskResultDaoExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskResultDaoExtensions.kt
index 75d9d80c8..51341811a 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskResultDaoExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/risk/storage/internal/riskresults/PersistedRiskResultDaoExtensions.kt
@@ -1,24 +1,25 @@
 package de.rki.coronawarnapp.risk.storage.internal.riskresults
 
-import de.rki.coronawarnapp.risk.RiskLevelResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import java.util.UUID
 
-fun RiskLevelResult.toPersistedRiskResult(
+fun EwRiskLevelResult.toPersistedRiskResult(
     id: String = UUID.randomUUID().toString()
 ) = PersistedRiskLevelResultDao(
     id = id,
     calculatedAt = calculatedAt,
-    aggregatedRiskResult = aggregatedRiskResult?.toPersistedAggregatedRiskResult(),
+    aggregatedRiskResult = ewAggregatedRiskResult?.toPersistedAggregatedRiskResult(),
     failureReason = failureReason
 )
 
-fun AggregatedRiskResult.toPersistedAggregatedRiskResult() = PersistedRiskLevelResultDao.PersistedAggregatedRiskResult(
-    totalRiskLevel = totalRiskLevel,
-    totalMinimumDistinctEncountersWithLowRisk = totalMinimumDistinctEncountersWithLowRisk,
-    totalMinimumDistinctEncountersWithHighRisk = totalMinimumDistinctEncountersWithHighRisk,
-    mostRecentDateWithLowRisk = mostRecentDateWithLowRisk,
-    mostRecentDateWithHighRisk = mostRecentDateWithHighRisk,
-    numberOfDaysWithLowRisk = numberOfDaysWithLowRisk,
-    numberOfDaysWithHighRisk = numberOfDaysWithHighRisk
-)
+fun EwAggregatedRiskResult.toPersistedAggregatedRiskResult() =
+    PersistedRiskLevelResultDao.PersistedAggregatedRiskResult(
+        totalRiskLevel = totalRiskLevel,
+        totalMinimumDistinctEncountersWithLowRisk = totalMinimumDistinctEncountersWithLowRisk,
+        totalMinimumDistinctEncountersWithHighRisk = totalMinimumDistinctEncountersWithHighRisk,
+        mostRecentDateWithLowRisk = mostRecentDateWithLowRisk,
+        mostRecentDateWithHighRisk = mostRecentDateWithHighRisk,
+        numberOfDaysWithLowRisk = numberOfDaysWithLowRisk,
+        numberOfDaysWithHighRisk = numberOfDaysWithHighRisk
+    )
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingState.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingState.kt
index 8c391b14a..a12be64f0 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingState.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingState.kt
@@ -7,8 +7,8 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.tracing.TracingProgress
 import de.rki.coronawarnapp.util.ContextExtensions.getColorCompat
-import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import org.joda.time.Instant
+import org.joda.time.LocalDate
 import org.joda.time.format.DateTimeFormat
 
 sealed class TracingState {
@@ -30,7 +30,7 @@ data class IncreasedRisk(
     override val riskState: RiskState,
     override val isInDetailsMode: Boolean,
     val lastExposureDetectionTime: Instant?,
-    val lastEncounterAt: Instant?,
+    val lastEncounterAt: LocalDate?,
     val allowManualUpdate: Boolean,
     val daysWithEncounters: Int
 ) : TracingState() {
@@ -81,7 +81,7 @@ data class IncreasedRisk(
 
         return context.getString(
             stringRes,
-            lastEncounterAt.toLocalDateUtc().toString(DateTimeFormat.mediumDate())
+            lastEncounterAt.toString(DateTimeFormat.mediumDate())
         )
     }
 }
@@ -91,7 +91,7 @@ data class LowRisk(
     override val riskState: RiskState,
     override val isInDetailsMode: Boolean,
     val lastExposureDetectionTime: Instant?,
-    val lastEncounterAt: Instant?,
+    val lastEncounterAt: LocalDate?,
     val allowManualUpdate: Boolean,
     val daysWithEncounters: Int,
     val daysSinceInstallation: Long
@@ -149,7 +149,7 @@ data class LowRisk(
 
         return context.getString(
             stringRes,
-            lastEncounterAt.toLocalDateUtc().toString(DateTimeFormat.mediumDate())
+            lastEncounterAt.toString(DateTimeFormat.mediumDate())
         )
     }
 
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt
index c5168a647..c3d9018fc 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/states/TracingStateProvider.kt
@@ -36,7 +36,7 @@ class TracingStateProvider @AssistedInject constructor(
         tracingRepository.tracingProgress.onEach {
             Timber.v("tracingProgress: $it")
         },
-        riskLevelStorage.latestAndLastSuccessful.onEach {
+        riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult.onEach {
             Timber.v("riskLevelResults: $it")
         },
         exposureDetectionTracker.latestSubmission().onEach {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
index 66db20820..eba70d84b 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsFragmentViewModel.kt
@@ -75,7 +75,7 @@ class TracingDetailsFragmentViewModel @AssistedInject constructor(
 
     val buttonStates: LiveData<TracingDetailsState> = combine(
         tracingStatus.generalStatus,
-        riskLevelStorage.latestAndLastSuccessful,
+        riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult,
         backgroundModeStatus.isAutoModeEnabled
     ) { status,
         riskLevelResults,
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProvider.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProvider.kt
index 41de9b928..f58268644 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProvider.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProvider.kt
@@ -17,6 +17,7 @@ import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsFailedCa
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsIncreasedRiskBox
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsLowRiskBox
 import de.rki.coronawarnapp.tracing.ui.details.items.survey.UserSurveyBox
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.onCompletion
@@ -36,7 +37,7 @@ class TracingDetailsItemProvider @Inject constructor(
 
     val state: Flow<List<DetailsItem>> = combine(
         tracingStatus.generalStatus,
-        riskLevelStorage.latestAndLastSuccessful,
+        riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult,
         surveys.availableSurveys
     ) { status,
         riskLevelResults,
@@ -47,7 +48,7 @@ class TracingDetailsItemProvider @Inject constructor(
         mutableListOf<DetailsItem>().apply {
             if (status != Status.TRACING_INACTIVE &&
                 latestCalc.riskState == RiskState.LOW_RISK &&
-                latestCalc.matchedKeyCount > 0
+                latestCalc.ewRiskLevelResult.matchedKeyCount > 0
             ) {
                 add(AdditionalInfoLowRiskBox.Item)
             }
@@ -81,11 +82,11 @@ class TracingDetailsItemProvider @Inject constructor(
                 }
                 latestCalc.riskState == RiskState.LOW_RISK -> DetailsLowRiskBox.Item(
                     riskState = latestCalc.riskState,
-                    matchedKeyCount = latestCalc.matchedKeyCount
+                    matchedKeyCount = latestCalc.ewRiskLevelResult.matchedKeyCount
                 )
                 latestCalc.riskState == RiskState.INCREASED_RISK -> DetailsIncreasedRiskBox.Item(
                     riskState = latestCalc.riskState,
-                    lastEncounteredAt = latestCalc.lastRiskEncounterAt ?: Instant.EPOCH
+                    lastEncounteredAt = latestCalc.lastRiskEncounterAt ?: Instant.EPOCH.toLocalDateUtc()
                 )
                 else -> null
             }?.let { add(it) }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsIncreasedRiskBox.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsIncreasedRiskBox.kt
index 73ea588e2..fdfac32a3 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsIncreasedRiskBox.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/tracing/ui/details/items/riskdetails/DetailsIncreasedRiskBox.kt
@@ -8,7 +8,7 @@ import de.rki.coronawarnapp.databinding.TracingDetailsItemRiskdetailsIncreasedVi
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.tracing.ui.details.TracingDetailsAdapter
 import de.rki.coronawarnapp.tracing.ui.details.items.riskdetails.DetailsIncreasedRiskBox.Item
-import org.joda.time.Instant
+import org.joda.time.LocalDate
 
 class DetailsIncreasedRiskBox(
     parent: ViewGroup,
@@ -35,7 +35,7 @@ class DetailsIncreasedRiskBox(
 
     data class Item(
         val riskState: RiskState,
-        val lastEncounteredAt: Instant
+        val lastEncounteredAt: LocalDate
     ) : RiskDetailsStateItem {
 
         fun getRiskDetailsRiskLevelBody(context: Context): String {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt
index 4155d9059..aa58e0c95 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/EventRegistrationUIModule.kt
@@ -14,10 +14,10 @@ import de.rki.coronawarnapp.ui.eventregistration.organizer.category.TraceLocatio
 import de.rki.coronawarnapp.ui.eventregistration.organizer.category.TraceLocationCategoryFragmentModule
 import de.rki.coronawarnapp.ui.eventregistration.organizer.create.TraceLocationCreateFragment
 import de.rki.coronawarnapp.ui.eventregistration.organizer.create.TraceLocationCreateFragmentModule
-import de.rki.coronawarnapp.ui.eventregistration.organizer.qrinfo.TraceLocationQRInfoFragment
-import de.rki.coronawarnapp.ui.eventregistration.organizer.qrinfo.TraceLocationQRInfoFragmentModule
 import de.rki.coronawarnapp.ui.eventregistration.organizer.list.TraceLocationsFragment
 import de.rki.coronawarnapp.ui.eventregistration.organizer.list.TraceLocationsFragmentModule
+import de.rki.coronawarnapp.ui.eventregistration.organizer.qrinfo.TraceLocationQRInfoFragment
+import de.rki.coronawarnapp.ui.eventregistration.organizer.qrinfo.TraceLocationQRInfoFragmentModule
 
 @Module
 internal abstract class EventRegistrationUIModule {
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt
index 5088e56c2..7613181df 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/checkins/items/PastCheckInVH.kt
@@ -5,8 +5,8 @@ import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.TraceLocationAttendeeCheckinsItemPastBinding
 import de.rki.coronawarnapp.eventregistration.checkins.CheckIn
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.toUserTimeZone
-import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer
 import de.rki.coronawarnapp.util.list.SwipeConsumer
+import de.rki.coronawarnapp.util.lists.diffutil.HasPayloadDiffer
 import org.joda.time.format.DateTimeFormat
 
 class PastCheckInVH(parent: ViewGroup) :
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt
index 301541003..4da62b575 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/eventregistration/attendee/scan/ScanCheckInQrCodeFragment.kt
@@ -14,9 +14,9 @@ import com.journeyapps.barcodescanner.DefaultDecoderFactory
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.databinding.FragmentScanCheckInQrCodeBinding
 import de.rki.coronawarnapp.ui.eventregistration.attendee.checkins.CheckInsFragment
-import de.rki.coronawarnapp.util.permission.CameraPermissionHelper
 import de.rki.coronawarnapp.util.DialogHelper
 import de.rki.coronawarnapp.util.di.AutoInject
+import de.rki.coronawarnapp.util.permission.CameraPermissionHelper
 import de.rki.coronawarnapp.util.ui.observe2
 import de.rki.coronawarnapp.util.ui.popBackStack
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
index 0aac8920a..855e58104 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/submission/qrcode/scan/SubmissionQRCodeScanFragment.kt
@@ -18,10 +18,10 @@ import de.rki.coronawarnapp.ui.main.MainActivity
 import de.rki.coronawarnapp.ui.submission.ApiRequestState
 import de.rki.coronawarnapp.ui.submission.ScanStatus
 import de.rki.coronawarnapp.ui.submission.viewmodel.SubmissionNavigationEvents
-import de.rki.coronawarnapp.util.permission.CameraPermissionHelper
 import de.rki.coronawarnapp.util.DialogHelper
 import de.rki.coronawarnapp.util.di.AutoInject
 import de.rki.coronawarnapp.util.formatter.TestResult
+import de.rki.coronawarnapp.util.permission.CameraPermissionHelper
 import de.rki.coronawarnapp.util.ui.doNavigate
 import de.rki.coronawarnapp.util.ui.observe2
 import de.rki.coronawarnapp.util.ui.viewBindingLazy
diff --git a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
index e52dfe374..e3e49b04b 100644
--- a/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
+++ b/Corona-Warn-App/src/main/res/layout/contact_diary_overview_list_item.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
     android:id="@+id/day_element_body"
     style="@style/contactDiaryCardRipple"
     android:layout_width="match_parent"
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt
index b0fc4b2d9..6e72592e3 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/retention/ContactDiaryDataRetentionCalculationTest.kt
@@ -3,7 +3,7 @@ package de.rki.coronawarnapp.contactdiary.retention
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryLocationVisit
 import de.rki.coronawarnapp.contactdiary.model.ContactDiaryPersonEncounter
 import de.rki.coronawarnapp.contactdiary.storage.repo.DefaultContactDiaryRepository
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import de.rki.coronawarnapp.util.TimeStamper
@@ -124,10 +124,10 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() {
     @Test
     fun `test risk per date results`() = runBlockingTest {
         val instance = createInstance()
-        val list: List<AggregatedRiskPerDateResult> = testDates.map { createAggregatedRiskPerDateResult(Instant.parse(it)) }
+        val list: List<ExposureWindowDayRisk> = testDates.map { createAggregatedRiskPerDateResult(Instant.parse(it)) }
         val filteredList = list.filter { instance.isOutOfRetention(it.localDateUtc) }
 
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(list)
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(list)
         coEvery { riskLevelStorage.deleteAggregatedRiskPerDateResults(any()) } just runs
 
         filteredList.size shouldBe 1
@@ -135,7 +135,7 @@ class ContactDiaryDataRetentionCalculationTest : BaseTest() {
         coVerify { riskLevelStorage.deleteAggregatedRiskPerDateResults(filteredList) }
     }
 
-    private fun createAggregatedRiskPerDateResult(date: Instant) = AggregatedRiskPerDateResult(
+    private fun createAggregatedRiskPerDateResult(date: Instant) = ExposureWindowDayRisk(
         dateMillisSinceEpoch = date.millis,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         minimumDistinctEncountersWithLowRisk = 0,
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
index 807682783..bd06b47d2 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewViewModelTest.kt
@@ -17,7 +17,7 @@ import de.rki.coronawarnapp.contactdiary.util.ContactDiaryData
 import de.rki.coronawarnapp.contactdiary.util.mockStringsForContactDiaryExporterTests
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.TraceLocationCheckInRisk
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import de.rki.coronawarnapp.task.TaskController
@@ -63,7 +63,7 @@ open class ContactDiaryOverviewViewModelTest {
         every { taskController.submit(any()) } just runs
         every { contactDiaryRepository.locationVisits } returns flowOf(emptyList())
         every { contactDiaryRepository.personEncounters } returns flowOf(emptyList())
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(emptyList())
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(emptyList())
         every { riskLevelStorage.traceLocationCheckInRiskStates } returns flowOf(emptyList())
 
         mockStringsForContactDiaryExporterTests(context)
@@ -113,21 +113,21 @@ open class ContactDiaryOverviewViewModelTest {
         override val riskState: RiskState = RiskState.INCREASED_RISK
     }
 
-    private val aggregatedRiskPerDateResultLowRisk = AggregatedRiskPerDateResult(
+    private val aggregatedRiskPerDateResultLowRisk = ExposureWindowDayRisk(
         dateMillisSinceEpoch = dateMillis,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW,
         minimumDistinctEncountersWithLowRisk = 1,
         minimumDistinctEncountersWithHighRisk = 0
     )
 
-    private val aggregatedRiskPerDateResultHighRiskDueToHighRiskEncounter = AggregatedRiskPerDateResult(
+    private val aggregatedRiskPerDateResultHighRiskDueToHighRiskEncounter = ExposureWindowDayRisk(
         dateMillisSinceEpoch = dateMillis,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         minimumDistinctEncountersWithLowRisk = 0,
         minimumDistinctEncountersWithHighRisk = 1
     )
 
-    private val aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter = AggregatedRiskPerDateResult(
+    private val aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter = ExposureWindowDayRisk(
         dateMillisSinceEpoch = dateMillis,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         minimumDistinctEncountersWithLowRisk = 10,
@@ -199,7 +199,7 @@ open class ContactDiaryOverviewViewModelTest {
     fun `low risk enf with person and location`() {
         every { contactDiaryRepository.personEncounters } returns flowOf(listOf(personEncounter))
         every { contactDiaryRepository.locationVisits } returns flowOf(listOf(locationVisit))
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
 
         val item = createInstance().listItems.getOrAwaitValue().first {
             it is DayOverviewItem && it.date == date
@@ -221,7 +221,7 @@ open class ContactDiaryOverviewViewModelTest {
 
     @Test
     fun `low risk enf without person or location`() {
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
 
         val item = createInstance().listItems.getOrAwaitValue().first {
             it is DayOverviewItem && it.date == date
@@ -242,7 +242,7 @@ open class ContactDiaryOverviewViewModelTest {
     fun `high risk enf due to high risk encounter with person and location`() {
         every { contactDiaryRepository.personEncounters } returns flowOf(listOf(personEncounter))
         every { contactDiaryRepository.locationVisits } returns flowOf(listOf(locationVisit))
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(
             listOf(
                 aggregatedRiskPerDateResultHighRiskDueToHighRiskEncounter
             )
@@ -268,7 +268,7 @@ open class ContactDiaryOverviewViewModelTest {
 
     @Test
     fun `high risk enf due to high risk encounter without person or location`() {
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(
             listOf(
                 aggregatedRiskPerDateResultHighRiskDueToHighRiskEncounter
             )
@@ -293,7 +293,7 @@ open class ContactDiaryOverviewViewModelTest {
     fun `high risk enf due to low risk encounter with person and location`() {
         every { contactDiaryRepository.personEncounters } returns flowOf(listOf(personEncounter))
         every { contactDiaryRepository.locationVisits } returns flowOf(listOf(locationVisit))
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(
             listOf(
                 aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter
             )
@@ -319,7 +319,7 @@ open class ContactDiaryOverviewViewModelTest {
 
     @Test
     fun `high risk enf due to low risk encounter without person or location`() {
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(
             listOf(
                 aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter
             )
@@ -342,7 +342,7 @@ open class ContactDiaryOverviewViewModelTest {
 
     @Test
     fun `risk enf and risk event are independently of each other`() {
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(listOf(aggregatedRiskPerDateResultLowRisk))
         every { riskLevelStorage.traceLocationCheckInRiskStates } returns flowOf(listOf(traceLocationCheckInRiskHigh))
         every { contactDiaryRepository.locationVisits } returns flowOf(listOf(locationEventHighRiskVisit))
 
@@ -360,7 +360,7 @@ open class ContactDiaryOverviewViewModelTest {
             riskEventItem!!.validate(highRisk = true)
         }
 
-        every { riskLevelStorage.aggregatedRiskPerDateResults } returns flowOf(listOf(aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter))
+        every { riskLevelStorage.ewDayRiskStates } returns flowOf(listOf(aggregatedRiskPerDateResultHighRiskDueToLowRiskEncounter))
         every { riskLevelStorage.traceLocationCheckInRiskStates } returns flowOf(listOf(traceLocationCheckInRiskLow))
         every { contactDiaryRepository.locationVisits } returns flowOf(listOf(locationEventLowRiskVisit))
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/ExposureRiskMetadataDonorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/ExposureRiskMetadataDonorTest.kt
index 8e65fb16c..6451d5f74 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/ExposureRiskMetadataDonorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/ExposureRiskMetadataDonorTest.kt
@@ -4,8 +4,8 @@ import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import de.rki.coronawarnapp.appconfig.ConfigData
 import de.rki.coronawarnapp.datadonation.analytics.modules.exposureriskmetadata.ExposureRiskMetadataDonor
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
-import de.rki.coronawarnapp.risk.RiskLevelResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.server.protocols.internal.ppdd.PpaData
 import de.rki.coronawarnapp.util.TimeAndDateExtensions.seconds
@@ -25,8 +25,8 @@ import testhelpers.preferences.mockFlowPreference
 class ExposureRiskMetadataDonorTest : BaseTest() {
     @MockK lateinit var riskLevelStorage: RiskLevelStorage
     @MockK lateinit var analyticsSettings: AnalyticsSettings
-    @MockK lateinit var highAggregatedRiskResult: AggregatedRiskResult
-    @MockK lateinit var lowAggregatedRiskResult: AggregatedRiskResult
+    @MockK lateinit var highEwAggregatedRiskResult: EwAggregatedRiskResult
+    @MockK lateinit var lowEwAggregatedRiskResult: EwAggregatedRiskResult
 
     private val baseDate: Instant = Instant.ofEpochMilli(101010)
 
@@ -34,20 +34,20 @@ class ExposureRiskMetadataDonorTest : BaseTest() {
     fun setup() {
         MockKAnnotations.init(this)
 
-        every { highAggregatedRiskResult.isIncreasedRisk() } returns true
-        every { highAggregatedRiskResult.mostRecentDateWithHighRisk } returns baseDate
-        every { lowAggregatedRiskResult.isIncreasedRisk() } returns false
-        every { lowAggregatedRiskResult.mostRecentDateWithHighRisk } returns baseDate
+        every { highEwAggregatedRiskResult.isIncreasedRisk() } returns true
+        every { highEwAggregatedRiskResult.mostRecentDateWithHighRisk } returns baseDate
+        every { lowEwAggregatedRiskResult.isIncreasedRisk() } returns false
+        every { lowEwAggregatedRiskResult.mostRecentDateWithHighRisk } returns baseDate
     }
 
     private fun createRiskLevelResult(
-        aggregatedRiskResult: AggregatedRiskResult?,
-        failureReason: RiskLevelResult.FailureReason?,
+        ewAggregatedRiskResult: EwAggregatedRiskResult?,
+        failureReason: EwRiskLevelResult.FailureReason?,
         calculatedAt: Instant
-    ): RiskLevelResult = object : RiskLevelResult {
+    ): EwRiskLevelResult = object : EwRiskLevelResult {
         override val calculatedAt: Instant = calculatedAt
-        override val aggregatedRiskResult: AggregatedRiskResult? = aggregatedRiskResult
-        override val failureReason: RiskLevelResult.FailureReason? = failureReason
+        override val ewAggregatedRiskResult: EwAggregatedRiskResult? = ewAggregatedRiskResult
+        override val failureReason: EwRiskLevelResult.FailureReason? = failureReason
         override val exposureWindows: List<ExposureWindow>? = null
         override val matchedKeyCount: Int = 0
         override val daysWithEncounters: Int = 0
@@ -68,16 +68,16 @@ class ExposureRiskMetadataDonorTest : BaseTest() {
             .build()
 
         every { analyticsSettings.previousExposureRiskMetadata } returns mockFlowPreference(null)
-        every { riskLevelStorage.latestAndLastSuccessful } returns flowOf(
+        every { riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult } returns flowOf(
             listOf(
                 createRiskLevelResult(
-                    aggregatedRiskResult = highAggregatedRiskResult,
+                    ewAggregatedRiskResult = highEwAggregatedRiskResult,
                     failureReason = null,
                     calculatedAt = baseDate
                 ),
                 createRiskLevelResult(
-                    aggregatedRiskResult = lowAggregatedRiskResult,
-                    failureReason = RiskLevelResult.FailureReason.UNKNOWN,
+                    ewAggregatedRiskResult = lowEwAggregatedRiskResult,
+                    failureReason = EwRiskLevelResult.FailureReason.UNKNOWN,
                     calculatedAt = baseDate
                 )
             )
@@ -118,16 +118,16 @@ class ExposureRiskMetadataDonorTest : BaseTest() {
 
         every { analyticsSettings.previousExposureRiskMetadata } returns mockFlowPreference(initialMetadata)
 
-        every { riskLevelStorage.latestAndLastSuccessful } returns flowOf(
+        every { riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult } returns flowOf(
             listOf(
                 createRiskLevelResult(
-                    aggregatedRiskResult = highAggregatedRiskResult,
+                    ewAggregatedRiskResult = highEwAggregatedRiskResult,
                     failureReason = null,
                     calculatedAt = baseDate
                 ),
                 createRiskLevelResult(
-                    aggregatedRiskResult = lowAggregatedRiskResult,
-                    failureReason = RiskLevelResult.FailureReason.UNKNOWN,
+                    ewAggregatedRiskResult = lowEwAggregatedRiskResult,
+                    failureReason = EwRiskLevelResult.FailureReason.UNKNOWN,
                     calculatedAt = baseDate
                 )
             )
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
index 859cf6621..bb1a53945 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/keysubmission/AnalyticsKeySubmissionCollectorTest.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.datadonation.analytics.modules.keysubmission
 
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.RiskLevelSettings
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
@@ -31,7 +31,7 @@ class AnalyticsKeySubmissionCollectorTest : BaseTest() {
     @MockK lateinit var analyticsKeySubmissionStorage: AnalyticsKeySubmissionStorage
     @MockK lateinit var riskLevelStorage: RiskLevelStorage
     @MockK lateinit var riskLevelSettings: RiskLevelSettings
-    @MockK lateinit var riskLevelResult: RiskLevelResult
+    @MockK lateinit var ewRiskLevelResult: EwRiskLevelResult
 
     private val now = Instant.now()
 
@@ -44,16 +44,16 @@ class AnalyticsKeySubmissionCollectorTest : BaseTest() {
     @Test
     fun `save test registered`() {
         coEvery { analyticsSettings.analyticsEnabled.value } returns true
-        every { riskLevelResult.riskState } returns RiskState.INCREASED_RISK
+        every { ewRiskLevelResult.riskState } returns RiskState.INCREASED_RISK
         coEvery {
-            riskLevelStorage.latestAndLastSuccessful
-        } returns flowOf(listOf(riskLevelResult))
+            riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult
+        } returns flowOf(listOf(ewRiskLevelResult))
         every { riskLevelSettings.lastChangeToHighRiskLevelTimestamp } returns now.minus(
             Hours.hours(2).toStandardDuration()
         )
         val testRegisteredAt = mockFlowPreference(now.millis)
         coEvery { analyticsKeySubmissionStorage.testRegisteredAt } returns testRegisteredAt
-        every { riskLevelResult.wasSuccessfullyCalculated } returns true
+        every { ewRiskLevelResult.wasSuccessfullyCalculated } returns true
         val riskLevelAtTestRegistration = mockFlowPreference(-1)
         every { analyticsKeySubmissionStorage.riskLevelAtTestRegistration } returns riskLevelAtTestRegistration
         val hoursSinceHighRiskWarningAtTestRegistration = mockFlowPreference(-1)
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
index cf33dfdaa..fe6d8af0e 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/datadonation/analytics/modules/registeredtest/TestResultDataCollectorTest.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.datadonation.analytics.modules.registeredtest
 
 import de.rki.coronawarnapp.datadonation.analytics.storage.AnalyticsSettings
 import de.rki.coronawarnapp.datadonation.analytics.storage.TestResultDonorSettings
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.util.TimeStamper
 import de.rki.coronawarnapp.util.formatter.TestResult
@@ -62,11 +62,11 @@ class TestResultDataCollectorTest : BaseTest() {
         runBlockingTest {
             every { analyticsSettings.analyticsEnabled } returns mockFlowPreference(true)
 
-            val mockRiskLevelResult = mockk<RiskLevelResult>().apply {
+            val mockRiskLevelResult = mockk<EwRiskLevelResult>().apply {
                 every { calculatedAt } returns Instant.now()
                 every { wasSuccessfullyCalculated } returns true
             }
-            every { riskLevelStorage.latestAndLastSuccessful } returns flowOf(listOf(mockRiskLevelResult))
+            every { riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult } returns flowOf(listOf(mockRiskLevelResult))
             every { testResultDonorSettings.saveTestResultDonorDataAtRegistration(any(), any()) } just Runs
             testResultDataCollector.saveTestResultAnalyticsSettings(TestResult.POSITIVE)
 
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/windows/ExposureWindowsCalculationTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/windows/ExposureWindowsCalculationTest.kt
index d02dac3c4..4668c0e16 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/windows/ExposureWindowsCalculationTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/nearby/windows/ExposureWindowsCalculationTest.kt
@@ -17,7 +17,7 @@ import de.rki.coronawarnapp.nearby.windows.entities.configuration.JsonNormalized
 import de.rki.coronawarnapp.nearby.windows.entities.configuration.JsonTransmissionRiskValueMapping
 import de.rki.coronawarnapp.nearby.windows.entities.configuration.JsonTrlFilter
 import de.rki.coronawarnapp.risk.DefaultRiskLevels
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.result.RiskResult
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass
 import de.rki.coronawarnapp.util.TimeStamper
@@ -140,7 +140,7 @@ class ExposureWindowsCalculationTest : BaseTest() {
         return timeStamper.nowUTC - expAge * DateTimeConstants.MILLIS_PER_DAY
     }
 
-    private fun comparisonDebugTable(aggregated: AggregatedRiskResult, case: TestCase): String {
+    private fun comparisonDebugTable(ewAggregated: EwAggregatedRiskResult, case: TestCase): String {
         val result = StringBuilder()
         result.append("\n").append(case.description)
         result.append("\n").append("+----------------------+--------------------------+--------------------------+")
@@ -149,35 +149,35 @@ class ExposureWindowsCalculationTest : BaseTest() {
         result.append(
             addPropertyCheckToComparisonDebugTable(
                 "Total Risk",
-                aggregated.totalRiskLevel.number,
+                ewAggregated.totalRiskLevel.number,
                 case.expTotalRiskLevel
             )
         )
         result.append(
             addPropertyCheckToComparisonDebugTable(
                 "Date With High Risk",
-                aggregated.mostRecentDateWithHighRisk,
+                ewAggregated.mostRecentDateWithHighRisk,
                 getTestCaseDate(case.expAgeOfMostRecentDateWithHighRiskInDays)
             )
         )
         result.append(
             addPropertyCheckToComparisonDebugTable(
                 "Date With Low Risk",
-                aggregated.mostRecentDateWithLowRisk,
+                ewAggregated.mostRecentDateWithLowRisk,
                 getTestCaseDate(case.expAgeOfMostRecentDateWithLowRiskInDays)
             )
         )
         result.append(
             addPropertyCheckToComparisonDebugTable(
                 "Encounters High Risk",
-                aggregated.totalMinimumDistinctEncountersWithHighRisk,
+                ewAggregated.totalMinimumDistinctEncountersWithHighRisk,
                 case.expTotalMinimumDistinctEncountersWithHighRisk
             )
         )
         result.append(
             addPropertyCheckToComparisonDebugTable(
                 "Encounters Low Risk",
-                aggregated.totalMinimumDistinctEncountersWithLowRisk,
+                ewAggregated.totalMinimumDistinctEncountersWithLowRisk,
                 case.expTotalMinimumDistinctEncountersWithLowRisk
             )
         )
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcherTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcherTest.kt
index c417f1a01..26643e4b4 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcherTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/risk/CheckInWarningMatcherTest.kt
@@ -29,12 +29,16 @@ class CheckInWarningMatcherTest : BaseTest() {
     @BeforeEach
     fun setup() {
         MockKAnnotations.init(this)
-        coEvery { presenceTracingRiskRepository.replaceAllMatches(any()) } just Runs
+        coEvery { presenceTracingRiskRepository.reportSuccessfulCalculation(any()) } just Runs
         coEvery { presenceTracingRiskRepository.deleteAllMatches() } just Runs
+        coEvery { presenceTracingRiskRepository.deleteStaleData() } just Runs
+        // TODO tests
+        coEvery { presenceTracingRiskRepository.deleteMatchesOfPackage(any()) } just Runs
+        coEvery { presenceTracingRiskRepository.markPackageProcessed(any()) } just Runs
     }
 
     @Test
-    fun `replaces matches`() {
+    fun `reports new matches`() {
         val checkIn1 = createCheckIn(
             id = 2L,
             traceLocationGuid = "fe84394e73838590cc7707aba0350c130f6d0fb6f0f2535f9735f481dee61871",
@@ -77,13 +81,13 @@ class CheckInWarningMatcherTest : BaseTest() {
 
         runBlockingTest {
             createInstance().execute()
-            coVerify(exactly = 1) { presenceTracingRiskRepository.replaceAllMatches(any()) }
+            coVerify(exactly = 1) { presenceTracingRiskRepository.reportSuccessfulCalculation(any()) }
             coVerify(exactly = 0) { presenceTracingRiskRepository.deleteAllMatches() }
         }
     }
 
     @Test
-    fun `replace with empty list if no matches found`() {
+    fun `report empty list if no matches found`() {
         val checkIn1 = createCheckIn(
             id = 2L,
             traceLocationGuid = "fe84394e73838590cc7707aba0350c130f6d0fb6f0f2535f9735f481dee61871",
@@ -126,13 +130,13 @@ class CheckInWarningMatcherTest : BaseTest() {
 
         runBlockingTest {
             createInstance().execute()
-            coVerify(exactly = 1) { presenceTracingRiskRepository.replaceAllMatches(emptyList()) }
+            coVerify(exactly = 1) { presenceTracingRiskRepository.reportSuccessfulCalculation(emptyList()) }
             coVerify(exactly = 0) { presenceTracingRiskRepository.deleteAllMatches() }
         }
     }
 
     @Test
-    fun `replace with empty list if package is empty`() {
+    fun `report empty list if package is empty`() {
         val checkIn1 = createCheckIn(
             id = 2L,
             traceLocationGuid = "fe84394e73838590cc7707aba0350c130f6d0fb6f0f2535f9735f481dee61871",
@@ -161,7 +165,7 @@ class CheckInWarningMatcherTest : BaseTest() {
 
         runBlockingTest {
             createInstance().execute()
-            coVerify(exactly = 1) { presenceTracingRiskRepository.replaceAllMatches(emptyList()) }
+            coVerify(exactly = 1) { presenceTracingRiskRepository.reportSuccessfulCalculation(emptyList()) }
             coVerify(exactly = 0) { presenceTracingRiskRepository.deleteAllMatches() }
         }
     }
@@ -198,7 +202,7 @@ class CheckInWarningMatcherTest : BaseTest() {
 
         runBlockingTest {
             createInstance().execute()
-            coVerify(exactly = 0) { presenceTracingRiskRepository.replaceAllMatches(any()) }
+            coVerify(exactly = 1) { presenceTracingRiskRepository.reportSuccessfulCalculation(emptyList()) }
             coVerify(exactly = 1) { presenceTracingRiskRepository.deleteAllMatches() }
         }
     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensionsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultExtensionsTest.kt
similarity index 79%
rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensionsTest.kt
rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultExtensionsTest.kt
index 243588ccb..f1b801552 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultExtensionsTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultExtensionsTest.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import io.kotest.matchers.longs.shouldBeInRange
 import io.kotest.matchers.shouldBe
 import io.mockk.mockk
@@ -9,16 +9,16 @@ import org.joda.time.Instant
 import org.junit.jupiter.api.Test
 import testhelpers.BaseTest
 
-class RiskLevelResultExtensionsTest : BaseTest() {
+class EwRiskLevelResultExtensionsTest : BaseTest() {
 
     private fun createRiskLevelResult(
         hasResult: Boolean,
         calculatedAt: Instant
-    ): RiskLevelResult = object : RiskLevelResult {
+    ): EwRiskLevelResult = object : EwRiskLevelResult {
         override val calculatedAt: Instant = calculatedAt
-        override val aggregatedRiskResult: AggregatedRiskResult? = if (hasResult) mockk() else null
-        override val failureReason: RiskLevelResult.FailureReason?
-            get() = if (!hasResult) RiskLevelResult.FailureReason.UNKNOWN else null
+        override val ewAggregatedRiskResult: EwAggregatedRiskResult? = if (hasResult) mockk() else null
+        override val failureReason: EwRiskLevelResult.FailureReason?
+            get() = if (!hasResult) EwRiskLevelResult.FailureReason.UNKNOWN else null
         override val exposureWindows: List<ExposureWindow>? = null
         override val matchedKeyCount: Int = 0
         override val daysWithEncounters: Int = 0
@@ -26,9 +26,9 @@ class RiskLevelResultExtensionsTest : BaseTest() {
 
     @Test
     fun `getLatestAndLastSuccessful on empty results`() {
-        val emptyResults = emptyList<RiskLevelResult>()
+        val emptyResults = emptyList<EwRiskLevelResult>()
 
-        emptyResults.tryLatestResultsWithDefaults().apply {
+        emptyResults.tryLatestEwResultsWithDefaults().apply {
             lastCalculated.apply {
                 riskState shouldBe RiskState.LOW_RISK
                 val now = Instant.now().millis
@@ -47,7 +47,7 @@ class RiskLevelResultExtensionsTest : BaseTest() {
             createRiskLevelResult(hasResult = true, calculatedAt = Instant.EPOCH.plus(1))
         )
 
-        results.tryLatestResultsWithDefaults().apply {
+        results.tryLatestEwResultsWithDefaults().apply {
             lastCalculated.calculatedAt shouldBe Instant.EPOCH.plus(1)
             lastSuccessfullyCalculated.calculatedAt shouldBe Instant.EPOCH.plus(1)
         }
@@ -61,7 +61,7 @@ class RiskLevelResultExtensionsTest : BaseTest() {
             createRiskLevelResult(hasResult = false, calculatedAt = Instant.EPOCH.plus(2))
         )
 
-        results.tryLatestResultsWithDefaults().apply {
+        results.tryLatestEwResultsWithDefaults().apply {
             lastCalculated.calculatedAt shouldBe Instant.EPOCH.plus(2)
             lastSuccessfullyCalculated.calculatedAt shouldBe Instant.EPOCH.plus(1)
         }
@@ -76,7 +76,7 @@ class RiskLevelResultExtensionsTest : BaseTest() {
             createRiskLevelResult(hasResult = false, calculatedAt = Instant.EPOCH.plus(13))
         )
 
-        results.tryLatestResultsWithDefaults().apply {
+        results.tryLatestEwResultsWithDefaults().apply {
             lastCalculated.calculatedAt shouldBe Instant.EPOCH.plus(13)
             lastSuccessfullyCalculated.calculatedAt shouldBe Instant.EPOCH
         }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultTest.kt
similarity index 55%
rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultTest.kt
rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultTest.kt
index b40143539..c5b8c6c55 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelResultTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/EwRiskLevelResultTest.kt
@@ -1,22 +1,22 @@
 package de.rki.coronawarnapp.risk
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import io.kotest.matchers.shouldBe
 import io.mockk.mockk
 import org.joda.time.Instant
 import org.junit.Test
 import testhelpers.BaseTest
 
-class RiskLevelResultTest : BaseTest() {
+class EwRiskLevelResultTest : BaseTest() {
 
     private fun createRiskLevel(
-        aggregatedRiskResult: AggregatedRiskResult?,
-        failureReason: RiskLevelResult.FailureReason?
-    ): RiskLevelResult = object : RiskLevelResult {
+        ewAggregatedRiskResult: EwAggregatedRiskResult?,
+        failureReason: EwRiskLevelResult.FailureReason?
+    ): EwRiskLevelResult = object : EwRiskLevelResult {
         override val calculatedAt: Instant = Instant.EPOCH
-        override val aggregatedRiskResult: AggregatedRiskResult? = aggregatedRiskResult
-        override val failureReason: RiskLevelResult.FailureReason? = failureReason
+        override val ewAggregatedRiskResult: EwAggregatedRiskResult? = ewAggregatedRiskResult
+        override val failureReason: EwRiskLevelResult.FailureReason? = failureReason
         override val exposureWindows: List<ExposureWindow>? = null
         override val matchedKeyCount: Int = 0
         override val daysWithEncounters: Int = 0
@@ -25,12 +25,12 @@ class RiskLevelResultTest : BaseTest() {
     @Test
     fun testUnsuccessfulRistLevels() {
         createRiskLevel(
-            aggregatedRiskResult = null,
-            failureReason = RiskLevelResult.FailureReason.UNKNOWN
+            ewAggregatedRiskResult = null,
+            failureReason = EwRiskLevelResult.FailureReason.UNKNOWN
         ).wasSuccessfullyCalculated shouldBe false
 
         createRiskLevel(
-            aggregatedRiskResult = mockk(),
+            ewAggregatedRiskResult = mockk(),
             failureReason = null
         ).wasSuccessfullyCalculated shouldBe true
     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt
index 7f496fa94..d391e5cc2 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelChangeDetectorTest.kt
@@ -9,7 +9,7 @@ import de.rki.coronawarnapp.notification.GeneralNotifications
 import de.rki.coronawarnapp.risk.RiskState.CALCULATION_FAILED
 import de.rki.coronawarnapp.risk.RiskState.INCREASED_RISK
 import de.rki.coronawarnapp.risk.RiskState.LOW_RISK
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.storage.TracingSettings
 import de.rki.coronawarnapp.submission.SubmissionSettings
@@ -66,17 +66,18 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
         every { testResultDonorSettings.riskLevelTurnedRedTime } returns mockFlowPreference(null)
         every { testResultDonorSettings.mostRecentDateWithHighOrLowRiskLevel } returns mockFlowPreference(null)
+        every { riskLevelStorage.latestCombinedEwPtRiskLevelResults } returns flowOf(listOf())
     }
 
     private fun createRiskLevel(
         riskState: RiskState,
         calculatedAt: Instant = Instant.EPOCH,
-        aggregatedRiskResult: AggregatedRiskResult? = null
-    ): RiskLevelResult = object : RiskLevelResult {
+        ewAggregatedRiskResult: EwAggregatedRiskResult? = null
+    ): EwRiskLevelResult = object : EwRiskLevelResult {
         override val riskState: RiskState = riskState
         override val calculatedAt: Instant = calculatedAt
-        override val aggregatedRiskResult: AggregatedRiskResult? = aggregatedRiskResult
-        override val failureReason: RiskLevelResult.FailureReason? = null
+        override val ewAggregatedRiskResult: EwAggregatedRiskResult? = ewAggregatedRiskResult
+        override val failureReason: EwRiskLevelResult.FailureReason? = null
         override val exposureWindows: List<ExposureWindow>? = null
         override val matchedKeyCount: Int = 0
         override val daysWithEncounters: Int = 0
@@ -98,7 +99,7 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
     @Test
     fun `nothing happens if there is only one result yet`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(listOf(createRiskLevel(LOW_RISK)))
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(listOf(createRiskLevel(LOW_RISK)))
 
         runBlockingTest {
             val instance = createInstance(scope = this)
@@ -115,7 +116,7 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
     @Test
     fun `no risklevel change, nothing should happen`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(LOW_RISK),
                 createRiskLevel(LOW_RISK)
@@ -135,9 +136,11 @@ class RiskLevelChangeDetectorTest : BaseTest() {
         }
     }
 
+    // TODO test if risk level change for combined risk triggers notification
+
     @Test
     fun `risklevel went from HIGH to LOW`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(LOW_RISK, calculatedAt = Instant.EPOCH.plus(1)),
                 createRiskLevel(INCREASED_RISK, calculatedAt = Instant.EPOCH)
@@ -151,16 +154,16 @@ class RiskLevelChangeDetectorTest : BaseTest() {
             advanceUntilIdle()
 
             coVerifySequence {
-                submissionSettings.isSubmissionSuccessful
-                foregroundState.isInForeground
                 surveys.resetSurvey(Surveys.Type.HIGH_RISK_ENCOUNTER)
             }
         }
     }
 
+    // TODO test if risk level change for combined risk triggers notification
+
     @Test
     fun `risklevel went from LOW to HIGH`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(INCREASED_RISK, calculatedAt = Instant.EPOCH.plus(1)),
                 createRiskLevel(LOW_RISK, calculatedAt = Instant.EPOCH)
@@ -174,8 +177,6 @@ class RiskLevelChangeDetectorTest : BaseTest() {
             advanceUntilIdle()
 
             coVerifySequence {
-                submissionSettings.isSubmissionSuccessful
-                foregroundState.isInForeground
                 surveys wasNot Called
             }
         }
@@ -183,7 +184,7 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
     @Test
     fun `risklevel went from LOW to HIGH but it is has already been processed`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(INCREASED_RISK, calculatedAt = Instant.EPOCH.plus(1)),
                 createRiskLevel(LOW_RISK, calculatedAt = Instant.EPOCH)
@@ -208,12 +209,12 @@ class RiskLevelChangeDetectorTest : BaseTest() {
     fun `riskLevelTurnedRedTime is only set once`() {
         testResultDonorSettings.riskLevelTurnedRedTime.update { Instant.EPOCH.plus(1) }
 
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(
                     INCREASED_RISK,
                     calculatedAt = Instant.EPOCH.plus(2),
-                    aggregatedRiskResult = mockk<AggregatedRiskResult>().apply {
+                    ewAggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply {
                         every { isIncreasedRisk() } returns true
                     }
                 ),
@@ -242,12 +243,12 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
     @Test
     fun `mostRecentDateWithHighOrLowRiskLevel is updated every time`() {
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(
                     INCREASED_RISK,
                     calculatedAt = Instant.EPOCH.plus(1),
-                    aggregatedRiskResult = mockk<AggregatedRiskResult>().apply {
+                    ewAggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply {
                         every { mostRecentDateWithHighRisk } returns Instant.EPOCH.plus(10)
                         every { isIncreasedRisk() } returns true
                     }
@@ -264,12 +265,12 @@ class RiskLevelChangeDetectorTest : BaseTest() {
 
         testResultDonorSettings.mostRecentDateWithHighOrLowRiskLevel.value shouldBe Instant.EPOCH.plus(10)
 
-        every { riskLevelStorage.latestRiskLevelResults } returns flowOf(
+        every { riskLevelStorage.latestEwRiskLevelResults } returns flowOf(
             listOf(
                 createRiskLevel(
                     INCREASED_RISK,
                     calculatedAt = Instant.EPOCH.plus(1),
-                    aggregatedRiskResult = mockk<AggregatedRiskResult>().apply {
+                    ewAggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply {
                         every { mostRecentDateWithLowRisk } returns Instant.EPOCH.plus(20)
                         every { isIncreasedRisk() } returns false
                     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt
index b89b99fcb..ffc2478a2 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/RiskLevelTaskTest.kt
@@ -12,7 +12,7 @@ import de.rki.coronawarnapp.diagnosiskeys.storage.CachedKeyInfo
 import de.rki.coronawarnapp.diagnosiskeys.storage.KeyCacheRepository
 import de.rki.coronawarnapp.nearby.ENFClient
 import de.rki.coronawarnapp.presencetracing.checkins.checkout.auto.AutoCheckOut
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.submission.SubmissionSettings
 import de.rki.coronawarnapp.task.Task
@@ -119,9 +119,9 @@ class RiskLevelTaskTest : BaseTest() {
         every { configData.isDeviceTimeCorrect } returns false
         every { configData.localOffset } returns Duration.standardHours(5)
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            failureReason = RiskLevelResult.FailureReason.INCORRECT_DEVICE_TIME
+            failureReason = EwRiskLevelResult.FailureReason.INCORRECT_DEVICE_TIME
         )
     }
 
@@ -135,9 +135,9 @@ class RiskLevelTaskTest : BaseTest() {
             }
         }
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            failureReason = RiskLevelResult.FailureReason.NO_INTERNET
+            failureReason = EwRiskLevelResult.FailureReason.NO_INTERNET
         )
     }
 
@@ -145,9 +145,9 @@ class RiskLevelTaskTest : BaseTest() {
     fun `risk calculation is skipped if tracing is disabled`() = runBlockingTest {
         every { enfClient.isTracingEnabled } returns flowOf(false)
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            failureReason = RiskLevelResult.FailureReason.TRACING_OFF
+            failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF
         )
     }
 
@@ -155,9 +155,9 @@ class RiskLevelTaskTest : BaseTest() {
     fun `risk calculation is skipped if results are not existing while in background mode`() = runBlockingTest {
         coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf()
         every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(true)
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            failureReason = RiskLevelResult.FailureReason.OUTDATED_RESULTS
+            failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS
         )
     }
 
@@ -165,9 +165,9 @@ class RiskLevelTaskTest : BaseTest() {
     fun `risk calculation is skipped if results are not existing while no background mode`() = runBlockingTest {
         coEvery { keyCacheRepository.getAllCachedKeys() } returns listOf()
         every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(false)
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            failureReason = RiskLevelResult.FailureReason.OUTDATED_RESULTS_MANUAL
+            failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS_MANUAL
         )
     }
 
@@ -184,9 +184,9 @@ class RiskLevelTaskTest : BaseTest() {
         every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(true)
         every { timeStamper.nowUTC } returns now
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = now,
-            failureReason = RiskLevelResult.FailureReason.OUTDATED_RESULTS
+            failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS
         )
     }
 
@@ -203,9 +203,9 @@ class RiskLevelTaskTest : BaseTest() {
         every { backgroundModeStatus.isAutoModeEnabled } returns flowOf(false)
         every { timeStamper.nowUTC } returns now
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = now,
-            failureReason = RiskLevelResult.FailureReason.OUTDATED_RESULTS_MANUAL
+            failureReason = EwRiskLevelResult.FailureReason.OUTDATED_RESULTS_MANUAL
         )
     }
 
@@ -224,9 +224,9 @@ class RiskLevelTaskTest : BaseTest() {
         every { submissionSettings.isAllowedToSubmitKeys } returns true
         every { submissionSettings.hasViewedTestResult.value } returns true
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = now,
-            failureReason = RiskLevelResult.FailureReason.POSITIVE_TEST_RESULT
+            failureReason = EwRiskLevelResult.FailureReason.POSITIVE_TEST_RESULT
         )
     }
 
@@ -238,7 +238,7 @@ class RiskLevelTaskTest : BaseTest() {
             }
         }
         val now = Instant.parse("2020-12-28")
-        val aggregatedRiskResult = mockk<AggregatedRiskResult>().apply {
+        val aggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply {
             every { isIncreasedRisk() } returns true
         }
 
@@ -251,10 +251,10 @@ class RiskLevelTaskTest : BaseTest() {
         every { submissionSettings.isAllowedToSubmitKeys } returns true
         every { submissionSettings.hasViewedTestResult.value } returns false
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = now,
             failureReason = null,
-            aggregatedRiskResult = aggregatedRiskResult,
+            ewAggregatedRiskResult = aggregatedRiskResult,
             listOf()
         )
     }
@@ -267,7 +267,7 @@ class RiskLevelTaskTest : BaseTest() {
             }
         }
         val now = Instant.parse("2020-12-28")
-        val aggregatedRiskResult = mockk<AggregatedRiskResult>().apply {
+        val aggregatedRiskResult = mockk<EwAggregatedRiskResult>().apply {
             every { isIncreasedRisk() } returns true
         }
 
@@ -278,10 +278,10 @@ class RiskLevelTaskTest : BaseTest() {
         every { timeStamper.nowUTC } returns now
         coEvery { analyticsExposureWindowCollector.reportRiskResultsPerWindow(any()) } just Runs
 
-        createTask().run(arguments) shouldBe RiskLevelTaskResult(
+        createTask().run(arguments) shouldBe EwRiskLevelTaskResult(
             calculatedAt = now,
             failureReason = null,
-            aggregatedRiskResult = aggregatedRiskResult,
+            ewAggregatedRiskResult = aggregatedRiskResult,
             listOf()
         )
     }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRiskTest.kt
similarity index 95%
rename from Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt
rename to Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRiskTest.kt
index f31e3b75a..cf04f40a1 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/AggregatedRiskPerDateResultTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/result/ExposureWindowDayRiskTest.kt
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test
 import testhelpers.BaseTest
 import timber.log.Timber
 
-class AggregatedRiskPerDateResultTest : BaseTest() {
+class ExposureWindowDayRiskTest : BaseTest() {
 
     @Test
     fun `day is correct`() {
@@ -34,7 +34,7 @@ class AggregatedRiskPerDateResultTest : BaseTest() {
         tomorrowAggregatedRiskPerDateResult.localDateUtc shouldBe tomorrowLocalDate
     }
 
-    private fun createAggregatedRiskPerDateResult(date: Instant) = AggregatedRiskPerDateResult(
+    private fun createAggregatedRiskPerDateResult(date: Instant) = ExposureWindowDayRisk(
         dateMillisSinceEpoch = date.millis,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         minimumDistinctEncountersWithLowRisk = 0,
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt
index 2ea28a292..099afd385 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/BaseRiskLevelStorageTest.kt
@@ -1,7 +1,7 @@
 package de.rki.coronawarnapp.risk.storage
 
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testAggregatedRiskPerDateResult
 import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindow
 import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindowDaoWrapper
@@ -66,14 +66,18 @@ class BaseRiskLevelStorageTest : BaseTest() {
 
         every { aggregatedRiskPerDateResultDao.allEntries() } returns emptyFlow()
         coEvery { aggregatedRiskPerDateResultDao.insertRisk(any()) } just Runs
+
+        // TODO proper tests
         coEvery { presenceTracingRiskRepository.traceLocationCheckInRiskStates } returns emptyFlow()
         coEvery { presenceTracingRiskRepository.presenceTracingDayRisk } returns emptyFlow()
+        coEvery { presenceTracingRiskRepository.latestAndLastSuccessful() } returns emptyFlow()
+        coEvery { presenceTracingRiskRepository.latestEntries(any()) } returns emptyFlow()
     }
 
     private fun createInstance(
         scope: CoroutineScope = TestCoroutineScope(),
         storedResultLimit: Int = 10,
-        onStoreExposureWindows: (String, RiskLevelResult) -> Unit = { id, result -> },
+        onStoreExposureWindows: (String, EwRiskLevelResult) -> Unit = { id, result -> },
         onDeletedOrphanedExposureWindows: () -> Unit = {}
     ) = object : BaseRiskLevelStorage(
         scope = scope,
@@ -82,8 +86,8 @@ class BaseRiskLevelStorageTest : BaseTest() {
     ) {
         override val storedResultLimit: Int = storedResultLimit
 
-        override suspend fun storeExposureWindows(storedResultId: String, result: RiskLevelResult) {
-            onStoreExposureWindows(storedResultId, result)
+        override suspend fun storeExposureWindows(storedResultId: String, resultEw: EwRiskLevelResult) {
+            onStoreExposureWindows(storedResultId, resultEw)
         }
 
         override suspend fun deletedOrphanedExposureWindows() {
@@ -102,7 +106,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
             allEntries shouldBe testPersistedAggregatedRiskPerDateResultFlow
             allEntries.first().map { it.toAggregatedRiskPerDateResult() } shouldBe listOf(testAggregatedRiskPerDateResult)
 
-            val aggregatedRiskPerDateResults = instance.aggregatedRiskPerDateResults.first()
+            val aggregatedRiskPerDateResults = instance.ewDayRiskStates.first()
             aggregatedRiskPerDateResults shouldNotBe listOf(testPersistedAggregatedRiskPerDateResult)
             aggregatedRiskPerDateResults shouldBe listOf(testAggregatedRiskPerDateResult)
         }
@@ -127,7 +131,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
 
         runBlockingTest {
             val instance = createInstance()
-            instance.allRiskLevelResults.first() shouldBe listOf(testRisklevelResult)
+            instance.allEwRiskLevelResults.first() shouldBe listOf(testRisklevelResult)
         }
     }
 
@@ -139,7 +143,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
         runBlockingTest {
             val instance = createInstance()
             val riskLevelResult = testRisklevelResult.copy(exposureWindows = listOf(testExposureWindow))
-            instance.allRiskLevelResults.first() shouldBe listOf(riskLevelResult)
+            instance.allEwRiskLevelResults.first() shouldBe listOf(riskLevelResult)
 
             verify {
                 riskResultTables.allEntries()
@@ -157,7 +161,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
             val instance = createInstance(scope = this)
 
             val riskLevelResult = testRisklevelResult.copy(exposureWindows = listOf(testExposureWindow))
-            instance.latestRiskLevelResults.first() shouldBe listOf(riskLevelResult)
+            instance.latestEwRiskLevelResults.first() shouldBe listOf(riskLevelResult)
 
             verify {
                 riskResultTables.latestEntries(2)
@@ -176,7 +180,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
             val instance = createInstance(scope = this)
 
             val riskLevelResult = testRisklevelResult.copy(exposureWindows = listOf(testExposureWindow))
-            instance.latestAndLastSuccessful.first() shouldBe listOf(riskLevelResult)
+            instance.latestAndLastSuccessfulEwRiskLevelResult.first() shouldBe listOf(riskLevelResult)
 
             verify {
                 riskResultTables.latestAndLastSuccessful()
@@ -204,7 +208,7 @@ class BaseRiskLevelStorageTest : BaseTest() {
 
     @Test
     fun `storeResult works`() = runBlockingTest {
-        val mockStoreWindows: (String, RiskLevelResult) -> Unit = spyk()
+        val mockStoreWindows: (String, EwRiskLevelResult) -> Unit = spyk()
         val mockDeleteOrphanedWindows: () -> Unit = spyk()
 
         val instance = createInstance(
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/CombineRiskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/CombineRiskTest.kt
index c984d0b6a..824be93cc 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/CombineRiskTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/CombineRiskTest.kt
@@ -2,7 +2,7 @@ package de.rki.coronawarnapp.risk.storage
 
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingDayRisk
 import de.rki.coronawarnapp.risk.RiskState
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.server.protocols.internal.v2.RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel
 import io.kotest.matchers.shouldBe
 import org.joda.time.LocalDate
@@ -25,25 +25,25 @@ class CombineRiskTest {
             localDateUtc = LocalDate(2021, 3, 22),
             riskState = RiskState.CALCULATION_FAILED
         )
-        val ewRisk = AggregatedRiskPerDateResult(
+        val ewRisk = ExposureWindowDayRisk(
             dateMillisSinceEpoch = Instant.parse("2021-03-22T14:00:00.000Z").toEpochMilli(),
             riskLevel = RiskLevel.HIGH,
             0,
             0
         )
-        val ewRisk2 = AggregatedRiskPerDateResult(
+        val ewRisk2 = ExposureWindowDayRisk(
             dateMillisSinceEpoch = Instant.parse("2021-03-19T14:00:00.000Z").toEpochMilli(),
             riskLevel = RiskLevel.LOW,
             0,
             0
         )
-        val ewRisk3 = AggregatedRiskPerDateResult(
+        val ewRisk3 = ExposureWindowDayRisk(
             dateMillisSinceEpoch = Instant.parse("2021-03-20T14:00:00.000Z").toEpochMilli(),
             riskLevel = RiskLevel.UNSPECIFIED,
             0,
             0
         )
-        val ewRisk4 = AggregatedRiskPerDateResult(
+        val ewRisk4 = ExposureWindowDayRisk(
             dateMillisSinceEpoch = Instant.parse("2021-03-15T14:00:00.000Z").toEpochMilli(),
             riskLevel = RiskLevel.UNSPECIFIED,
             0,
@@ -51,8 +51,8 @@ class CombineRiskTest {
         )
 
         val ptRiskList: List<PresenceTracingDayRisk> = listOf(ptRisk, ptRisk2, ptRisk3)
-        val ewRiskList: List<AggregatedRiskPerDateResult> = listOf(ewRisk, ewRisk2, ewRisk3, ewRisk4)
-        val result = combineRisk(ptRiskList, ewRiskList)
+        val exposureWindowDayRiskList: List<ExposureWindowDayRisk> = listOf(ewRisk, ewRisk2, ewRisk3, ewRisk4)
+        val result = combineRisk(ptRiskList, exposureWindowDayRiskList)
         result.size shouldBe 5
         result.find {
             it.localDate == LocalDate(2021, 3, 15)
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt
index 3301437ff..34c79dac1 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/RiskStorageTestData.kt
@@ -2,9 +2,9 @@ package de.rki.coronawarnapp.risk.storage
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import com.google.android.gms.nearby.exposurenotification.ScanInstance
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskPerDateResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
+import de.rki.coronawarnapp.risk.result.ExposureWindowDayRisk
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedAggregatedRiskPerDateResult
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao
 import de.rki.coronawarnapp.risk.storage.internal.windows.PersistedExposureWindowDao
@@ -29,7 +29,7 @@ object RiskStorageTestData {
         )
     )
 
-    val testAggregatedRiskResult = AggregatedRiskResult(
+    val testAggregatedRiskResult = EwAggregatedRiskResult(
         totalRiskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         totalMinimumDistinctEncountersWithLowRisk = 1,
         totalMinimumDistinctEncountersWithHighRisk = 2,
@@ -39,9 +39,9 @@ object RiskStorageTestData {
         numberOfDaysWithHighRisk = 6
     )
 
-    val testRisklevelResult = RiskLevelTaskResult(
+    val testRisklevelResult = EwRiskLevelTaskResult(
         calculatedAt = Instant.ofEpochMilli(9999L),
-        aggregatedRiskResult = testAggregatedRiskResult,
+        ewAggregatedRiskResult = testAggregatedRiskResult,
         exposureWindows = null
     )
 
@@ -75,7 +75,7 @@ object RiskStorageTestData {
         }.build().let { setScanInstances(listOf(it)) }
     }.build()
 
-    val testAggregatedRiskPerDateResult = AggregatedRiskPerDateResult(
+    val testAggregatedRiskPerDateResult = ExposureWindowDayRisk(
         dateMillisSinceEpoch = 9999L,
         riskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
         minimumDistinctEncountersWithLowRisk = 0,
@@ -90,8 +90,8 @@ object RiskStorageTestData {
     )
 
     val testRisklevelResultWithAggregatedRiskPerDateResult = testRisklevelResult.copy(
-        aggregatedRiskResult = testAggregatedRiskResult.copy(
-            aggregatedRiskPerDateResults = listOf(testAggregatedRiskPerDateResult)
+        ewAggregatedRiskResult = testAggregatedRiskResult.copy(
+            exposureWindowDayRisks = listOf(testAggregatedRiskPerDateResult)
         )
     )
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/internal/PersistedRiskResultDaoTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/internal/PersistedRiskResultDaoTest.kt
index 51adcacd6..52e7ac660 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/internal/PersistedRiskResultDaoTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/risk/storage/internal/PersistedRiskResultDaoTest.kt
@@ -1,6 +1,6 @@
 package de.rki.coronawarnapp.risk.storage.internal
 
-import de.rki.coronawarnapp.risk.RiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelResult
 import de.rki.coronawarnapp.risk.RiskState
 import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindow
 import de.rki.coronawarnapp.risk.storage.RiskStorageTestData.testExposureWindowDaoWrapper
@@ -34,8 +34,8 @@ class PersistedRiskResultDaoTest : BaseTest() {
             calculatedAt.millis shouldBe 931161601L
             exposureWindows shouldBe listOf(testExposureWindow)
             failureReason shouldBe null
-            aggregatedRiskResult shouldNotBe null
-            aggregatedRiskResult?.apply {
+            ewAggregatedRiskResult shouldNotBe null
+            ewAggregatedRiskResult?.apply {
                 totalRiskLevel shouldBe RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW
                 totalMinimumDistinctEncountersWithLowRisk shouldBe 89
                 totalMinimumDistinctEncountersWithHighRisk shouldBe 59
@@ -69,8 +69,8 @@ class PersistedRiskResultDaoTest : BaseTest() {
             calculatedAt.millis shouldBe 931161601L
             exposureWindows shouldBe null
             failureReason shouldBe null
-            aggregatedRiskResult shouldNotBe null
-            aggregatedRiskResult?.apply {
+            ewAggregatedRiskResult shouldNotBe null
+            ewAggregatedRiskResult?.apply {
                 totalRiskLevel shouldBe RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.LOW
                 totalMinimumDistinctEncountersWithLowRisk shouldBe 89
                 totalMinimumDistinctEncountersWithHighRisk shouldBe 59
@@ -89,14 +89,14 @@ class PersistedRiskResultDaoTest : BaseTest() {
         PersistedRiskLevelResultDao(
             id = "",
             calculatedAt = Instant.ofEpochMilli(931161601L),
-            failureReason = RiskLevelResult.FailureReason.TRACING_OFF,
+            failureReason = EwRiskLevelResult.FailureReason.TRACING_OFF,
             aggregatedRiskResult = null
         ).toRiskResult().apply {
             riskState shouldBe RiskState.CALCULATION_FAILED
             calculatedAt.millis shouldBe 931161601L
             exposureWindows shouldBe null
-            failureReason shouldBe RiskLevelResult.FailureReason.TRACING_OFF
-            aggregatedRiskResult shouldBe null
+            failureReason shouldBe EwRiskLevelResult.FailureReason.TRACING_OFF
+            ewAggregatedRiskResult shouldBe null
         }
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/IncreasedRiskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/IncreasedRiskTest.kt
index 438f59d49..6bdb0e7b9 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/IncreasedRiskTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/IncreasedRiskTest.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.tracing.states
 import android.content.Context
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.risk.RiskState
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.every
@@ -61,6 +62,6 @@ internal class IncreasedRiskTest {
         lastExposureDetectionTime = Instant.now(),
         allowManualUpdate = false,
         daysWithEncounters = 1,
-        lastEncounterAt = Instant.now()
+        lastEncounterAt = Instant.now().toLocalDateUtc()
     )
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/LowRiskTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/LowRiskTest.kt
index 3bc2c760b..c05e76379 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/LowRiskTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/states/LowRiskTest.kt
@@ -3,6 +3,7 @@ package de.rki.coronawarnapp.tracing.states
 import android.content.Context
 import de.rki.coronawarnapp.R
 import de.rki.coronawarnapp.risk.RiskState
+import de.rki.coronawarnapp.util.TimeAndDateExtensions.toLocalDateUtc
 import io.kotest.matchers.shouldBe
 import io.mockk.MockKAnnotations
 import io.mockk.every
@@ -44,7 +45,7 @@ internal class LowRiskTest {
     @Test
     fun `Active tracing row should be gone in low risk card on the home screen when there are encounters`() {
         defaultRisk
-            .copy(daysWithEncounters = 1, lastEncounterAt = Instant.now())
+            .copy(daysWithEncounters = 1, lastEncounterAt = Instant.now().toLocalDateUtc())
             .isGoneOnContentLowView(context) shouldBe true
     }
 
@@ -58,7 +59,7 @@ internal class LowRiskTest {
     @Test
     fun `Active tracing row should be shown in low risk detail screen when there are encounters`() {
         defaultRisk
-            .copy(daysWithEncounters = 1, lastEncounterAt = Instant.now(), isInDetailsMode = true)
+            .copy(daysWithEncounters = 1, lastEncounterAt = Instant.now().toLocalDateUtc(), isInDetailsMode = true)
             .isGoneOnContentLowView(context) shouldBe false
     }
 }
diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProviderTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProviderTest.kt
index 7ca361ae4..27175e36d 100644
--- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProviderTest.kt
+++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/tracing/ui/details/TracingDetailsItemProviderTest.kt
@@ -5,9 +5,12 @@ import android.content.res.Resources
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import de.rki.coronawarnapp.datadonation.survey.Surveys
 import de.rki.coronawarnapp.installTime.InstallTimeProvider
+import de.rki.coronawarnapp.presencetracing.risk.PtRiskLevelResult
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
 import de.rki.coronawarnapp.risk.ProtoRiskLevel
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.RiskState
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
+import de.rki.coronawarnapp.risk.storage.CombinedEwPtRiskLevelResult
 import de.rki.coronawarnapp.risk.storage.RiskLevelStorage
 import de.rki.coronawarnapp.tracing.GeneralTracingStatus
 import de.rki.coronawarnapp.tracing.ui.details.items.additionalinfos.AdditionalInfoLowRiskBox
@@ -36,7 +39,7 @@ class TracingDetailsItemProviderTest : BaseTest() {
 
     @MockK(relaxed = true) lateinit var context: Context
     @MockK(relaxed = true) lateinit var resources: Resources
-    @MockK(relaxed = true) lateinit var aggregatedRiskResult: AggregatedRiskResult
+    @MockK(relaxed = true) lateinit var ewAggregatedRiskResult: EwAggregatedRiskResult
 
     @MockK lateinit var tracingStatus: GeneralTracingStatus
     @MockK lateinit var riskLevelStorage: RiskLevelStorage
@@ -64,25 +67,36 @@ class TracingDetailsItemProviderTest : BaseTest() {
         availableSurveys: List<Surveys.Type> = emptyList()
     ) {
         every { tracingStatus.generalStatus } returns flowOf(status)
-        every { aggregatedRiskResult.totalRiskLevel } returns riskLevel
+        every { ewAggregatedRiskResult.totalRiskLevel } returns riskLevel
         every { installTimeProvider.daysSinceInstallation } returns daysSinceInstallation
         every { surveys.availableSurveys } returns flowOf(availableSurveys)
 
         if (riskLevel == ProtoRiskLevel.LOW) {
-            every { aggregatedRiskResult.isLowRisk() } returns true
+            every { ewAggregatedRiskResult.isLowRisk() } returns true
         } else if (riskLevel == ProtoRiskLevel.HIGH) {
-            every { aggregatedRiskResult.isIncreasedRisk() } returns true
+            every { ewAggregatedRiskResult.isIncreasedRisk() } returns true
         }
 
         val exposureWindow: ExposureWindow = mockk()
 
-        val riskLevelResult = RiskLevelTaskResult(
+        val ewRiskLevelTaskResult = EwRiskLevelTaskResult(
             calculatedAt = Instant.EPOCH,
-            aggregatedRiskResult = aggregatedRiskResult,
+            ewAggregatedRiskResult = ewAggregatedRiskResult,
             exposureWindows = listOf(exposureWindow)
         )
-        every { riskLevelResult.matchedKeyCount } returns matchedKeyCount
-        every { riskLevelStorage.latestAndLastSuccessful } returns flowOf(listOf(riskLevelResult))
+
+        val ptRiskLevelResult = PtRiskLevelResult(
+            calculatedAt = Instant.EPOCH,
+            riskState = RiskState.CALCULATION_FAILED
+        )
+        val combined = CombinedEwPtRiskLevelResult(
+            ewRiskLevelResult = ewRiskLevelTaskResult,
+            ptRiskLevelResult = ptRiskLevelResult
+        )
+        every { ewRiskLevelTaskResult.matchedKeyCount } returns matchedKeyCount
+        every { riskLevelStorage.latestAndLastSuccessfulEwRiskLevelResult } returns flowOf(listOf(ewRiskLevelTaskResult))
+        // TODO tests
+        every { riskLevelStorage.latestAndLastSuccessfulCombinedEwPtRiskLevelResult } returns flowOf(listOf(combined))
     }
 
     @Test
diff --git a/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
index c8ff0a812..8c676d6ab 100644
--- a/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
+++ b/Corona-Warn-App/src/testDevice/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
@@ -2,8 +2,8 @@ package de.rki.coronawarnapp.test.risk.storage
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.DefaultRiskLevelStorage
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao
@@ -48,9 +48,9 @@ class DefaultRiskLevelStorageTest : BaseTest() {
         ),
         failureReason = null
     )
-    private val testRisklevelResult = RiskLevelTaskResult(
+    private val testRisklevelResult = EwRiskLevelTaskResult(
         calculatedAt = Instant.ofEpochMilli(9999L),
-        aggregatedRiskResult = AggregatedRiskResult(
+        ewAggregatedRiskResult = EwAggregatedRiskResult(
             totalRiskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
             totalMinimumDistinctEncountersWithLowRisk = 1,
             totalMinimumDistinctEncountersWithHighRisk = 2,
@@ -88,6 +88,8 @@ class DefaultRiskLevelStorageTest : BaseTest() {
 
         every { presenceTracingRiskRepository.traceLocationCheckInRiskStates } returns emptyFlow()
         every { presenceTracingRiskRepository.presenceTracingDayRisk } returns emptyFlow()
+        every { presenceTracingRiskRepository.latestAndLastSuccessful() } returns emptyFlow()
+        every { presenceTracingRiskRepository.latestEntries(any()) } returns emptyFlow()
     }
 
     private fun createInstance(
diff --git a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
index 17810289b..24478cb80 100644
--- a/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
+++ b/Corona-Warn-App/src/testDeviceForTesters/java/de/rki/coronawarnapp/test/risk/storage/DefaultRiskLevelStorageTest.kt
@@ -2,8 +2,8 @@ package de.rki.coronawarnapp.test.risk.storage
 
 import com.google.android.gms.nearby.exposurenotification.ExposureWindow
 import de.rki.coronawarnapp.presencetracing.risk.PresenceTracingRiskRepository
-import de.rki.coronawarnapp.risk.RiskLevelTaskResult
-import de.rki.coronawarnapp.risk.result.AggregatedRiskResult
+import de.rki.coronawarnapp.risk.EwRiskLevelTaskResult
+import de.rki.coronawarnapp.risk.result.EwAggregatedRiskResult
 import de.rki.coronawarnapp.risk.storage.DefaultRiskLevelStorage
 import de.rki.coronawarnapp.risk.storage.internal.RiskResultDatabase
 import de.rki.coronawarnapp.risk.storage.internal.riskresults.PersistedRiskLevelResultDao
@@ -47,9 +47,9 @@ class DefaultRiskLevelStorageTest : BaseTestInstrumentation() {
         ),
         failureReason = null
     )
-    private val testRisklevelResult = RiskLevelTaskResult(
+    private val testRisklevelResult = EwRiskLevelTaskResult(
         calculatedAt = Instant.ofEpochMilli(9999L),
-        aggregatedRiskResult = AggregatedRiskResult(
+        ewAggregatedRiskResult = EwAggregatedRiskResult(
             totalRiskLevel = RiskCalculationParametersOuterClass.NormalizedTimeToRiskLevelMapping.RiskLevel.HIGH,
             totalMinimumDistinctEncountersWithLowRisk = 1,
             totalMinimumDistinctEncountersWithHighRisk = 2,
@@ -87,6 +87,8 @@ class DefaultRiskLevelStorageTest : BaseTestInstrumentation() {
 
         every { presenceTracingRiskRepository.traceLocationCheckInRiskStates } returns emptyFlow()
         every { presenceTracingRiskRepository.presenceTracingDayRisk } returns emptyFlow()
+        every { presenceTracingRiskRepository.latestAndLastSuccessful() } returns emptyFlow()
+        every { presenceTracingRiskRepository.latestEntries(any()) } returns emptyFlow()
     }
 
     private fun createInstance() = DefaultRiskLevelStorage(
-- 
GitLab