diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
index 6010d2f3303dfaf3ba613ebb78a2a6fc931eb68b..3ce3ac2efdf276050c9c9ae1d5413d7300382ec1 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/LocalData.kt
@@ -143,6 +143,23 @@ object LocalData {
         )
     }
 
+    /**
+     * Sets the total amount of time the tracing was not active
+     * from the EncryptedSharedPrefs
+     *
+     * @param value timestamp in ms
+     */
+    fun totalNonActiveTracing(value: Long?) {
+        // TODO need this for nullable ref, shout not be goto for nullable storage
+        getSharedPreferenceInstance().edit(true) {
+            putLong(
+                CoronaWarnApplication.getAppContext()
+                    .getString(R.string.preference_total_non_active_tracing),
+                value ?: 0L
+            )
+        }
+    }
+
     /**
      * Gets the total amount of time the tracing was not active
      * from the EncryptedSharedPrefs
@@ -158,22 +175,65 @@ object LocalData {
     }
 
     /**
-     * Sets the total amount of time the tracing was not active
+
+     * Gets the timestamp when the Background Polling Began initially
+     * from the EncryptedSharedPrefs
+     *
+     * @return timestamp in ms
+     */
+    fun initialPollingForTestResultTimeStamp(): Long {
+        return getSharedPreferenceInstance().getLong(
+            CoronaWarnApplication.getAppContext()
+                .getString(R.string.preference_polling_test_result_started),
+            0L
+        )
+    }
+
+    /**
+     * Sets the timestamp when the Background Polling Began initially
      * from the EncryptedSharedPrefs
      *
      * @param value timestamp in ms
      */
-    fun totalNonActiveTracing(value: Long?) {
-        // TODO need this for nullable ref, shout not be goto for nullable storage
+    fun initialPollingForTestResultTimeStamp(value: Long) =
         getSharedPreferenceInstance().edit(true) {
             putLong(
                 CoronaWarnApplication.getAppContext()
-                    .getString(R.string.preference_total_non_active_tracing),
-                value ?: 0L
+                    .getString(R.string.preference_polling_test_result_started),
+                value
             )
         }
+
+    /**
+
+     * Gets the flag if notification is executed on Status Change
+     * from the EncryptedSharedPrefs
+     *
+     * @return boolean
+     */
+    fun isTestResultNotificationSent(): Boolean {
+        return getSharedPreferenceInstance().getBoolean(
+            CoronaWarnApplication.getAppContext()
+                .getString(R.string.preference_test_result_notification),
+            false
+        )
     }
 
+    /**
+     * Sets the flag if notification is executed on Status Change
+     * from the EncryptedSharedPrefs
+     *
+     * @param value boolean
+     */
+    fun isTestResultNotificationSent(value: Boolean) =
+        getSharedPreferenceInstance().edit(true) {
+            putBoolean(
+                CoronaWarnApplication.getAppContext()
+                    .getString(R.string.preference_test_result_notification),
+                value
+            )
+        }
+
     /****************************************************
      * RISK LEVEL
      ****************************************************/
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt
index f6ab3dad85f97a4aad5977020b87057c54410e80..424167ff581921dc6d702187f1dc75072e3e60f9 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SubmissionRepository.kt
@@ -5,6 +5,7 @@ import de.rki.coronawarnapp.exception.NoRegistrationTokenSetException
 import de.rki.coronawarnapp.service.submission.SubmissionService
 import de.rki.coronawarnapp.util.DeviceUIState
 import de.rki.coronawarnapp.util.formatter.TestResult
+import de.rki.coronawarnapp.worker.BackgroundWorkScheduler
 import java.util.Date
 
 object SubmissionRepository {
@@ -45,6 +46,9 @@ object SubmissionRepository {
                 val currentTime = System.currentTimeMillis()
                 LocalData.inititalTestResultReceivedTimestamp(currentTime)
                 testResultReceivedDate.value = Date(currentTime)
+                if (testResult == TestResult.PENDING) {
+                    BackgroundWorkScheduler.startWorkScheduler()
+                }
             } else {
                 testResultReceivedDate.value = Date(initialTestResultReceivedTimestamp)
             }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt
index 6206d2a139526fc87a79dafe8353e4951c540b13..717435f2587c8950a872c782a602ee59163d2b47 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/TimeAndDateExtensions.kt
@@ -51,4 +51,17 @@ object TimeAndDateExtensions {
         TimeUnit.MILLISECONDS.toMinutes(this) % TimeUnit.HOURS.toMinutes(1),
         TimeUnit.MILLISECONDS.toSeconds(this) % TimeUnit.MINUTES.toSeconds(1)
     )
+
+    /**
+     * Calculates the difference between two timestamps in Days Units
+     *
+     * @return Long
+     *
+     * @see TimeUnit
+     */
+    fun calculateDays(firstDate: Long, secondDate: Long): Long {
+        val millionSeconds = secondDate - firstDate
+        var days = TimeUnit.MILLISECONDS.toDays(millionSeconds)
+        return days
+    }
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt
index a36174aeac4bc1859222d0ef21d03b368550d8c5..30ad926c412aed40bc31db4e29927bd5f7c7d17e 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundConstants.kt
@@ -19,6 +19,11 @@ object BackgroundConstants {
      */
     const val DIAGNOSIS_KEY_PERIODIC_WORKER_TAG = "DIAGNOSIS_KEY_PERIODIC_WORKER"
 
+    /**
+     * Tag for background polling tp check test result periodic work
+     */
+    const val DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER_TAG = "DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER"
+
     /**
      * Unique name for diagnosis key retrieval one time work
      */
@@ -29,6 +34,11 @@ object BackgroundConstants {
      */
     const val DIAGNOSIS_KEY_PERIODIC_WORK_NAME = "DiagnosisKeyBackgroundPeriodicWork"
 
+    /**
+     * Unique name for diagnosis test result retrieval periodic work
+     */
+    const val DIAGNOSIS_TEST_RESULT_PERIODIC_WORK_NAME = "DiagnosisTestResultBackgroundPeriodicWork"
+
     /**
      * Total minutes in one day
      */
@@ -46,12 +56,25 @@ object BackgroundConstants {
      */
     const val GOOGLE_API_MAX_CALLS_PER_DAY = 20
 
+    /**
+     * Total tries count for diagnosis key retrieval per day
+     * Internal requirement
+     */
+    const val DIAGNOSIS_TEST_RESULT_RETRIEVAL_TRIES_PER_DAY = 12
+
     /**
      * Kind initial delay in minutes for periodic work for accessibility reason
      *
      * @see TimeUnit.MINUTES
      */
-    const val DIAGNOSIS_KEY_PERIODIC_KIND_DELAY = 1L
+    const val KIND_DELAY = 1L
+
+    /**
+     * Kind initial delay in minutes for periodic work for accessibility reason
+     *
+     * @see TimeUnit.SECONDS
+     */
+    const val DIAGNOSIS_TEST_RESULT_PERIODIC_INITIAL_DELAY = 10L
 
     /**
      * Minimum initial delay in minutes for diagnosis key retrieval one time work
@@ -89,4 +112,11 @@ object BackgroundConstants {
      * Retries before work would set as FAILED
      */
     const val WORKER_RETRY_COUNT_THRESHOLD = 3
+
+    /**
+     * The maximum validity in days for keeping Background polling active
+     *
+     * @see TimeUnit.DAYS
+     */
+    const val POLLING_VALIDITY_MAX_DAYS = 21
 }
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkHelper.kt
new file mode 100644
index 0000000000000000000000000000000000000000..b981162c45bf3a0b1d3c4a09fa6c217e07af4ffb
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkHelper.kt
@@ -0,0 +1,73 @@
+package de.rki.coronawarnapp.worker
+
+import androidx.work.Constraints
+import androidx.work.NetworkType
+
+/**
+ * Singleton class for background work helper functions
+ * The helper uses externalised constants for readability.
+ *
+ * @see BackgroundConstants
+ * @see BackgroundWorkScheduler
+ */
+object BackgroundWorkHelper {
+
+    /**
+     * Calculate the time for diagnosis key retrieval periodic work
+     *
+     * @return Long
+     *
+     * @see BackgroundConstants.MINUTES_IN_DAY
+     * @see getDiagnosisKeyRetrievalMaximumCalls
+     */
+    fun getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(): Long =
+        (BackgroundConstants.MINUTES_IN_DAY / getDiagnosisKeyRetrievalMaximumCalls()).toLong()
+
+    /**
+     * Calculate the time for diagnosis key retrieval periodic work
+     *
+     * @return Long
+     *
+     * @see BackgroundConstants.MINUTES_IN_DAY
+     */
+    fun getDiagnosisTestResultRetrievalPeriodicWorkTimeInterval(): Long =
+        (BackgroundConstants.MINUTES_IN_DAY /
+                BackgroundConstants.DIAGNOSIS_TEST_RESULT_RETRIEVAL_TRIES_PER_DAY).toLong()
+
+    /**
+     * Get maximum calls count to Google API
+     *
+     * @return Long
+     *
+     * @see BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY
+     * @see BackgroundConstants.GOOGLE_API_MAX_CALLS_PER_DAY
+     */
+    fun getDiagnosisKeyRetrievalMaximumCalls() =
+        BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY
+            .coerceAtMost(BackgroundConstants.GOOGLE_API_MAX_CALLS_PER_DAY)
+
+    /**
+     * Constraints for diagnosis key periodic work
+     * Do not execute background work if battery on low level.
+     *
+     * @return Constraints
+     */
+    fun getConstraintsForDiagnosisKeyPeriodicBackgroundWork() =
+        Constraints.Builder().setRequiresBatteryNotLow(true).build()
+
+    /**
+     * Constraints for diagnosis key one time work
+     * Requires battery not low and any network connection
+     * Mobile data usage is handled on OS level in application settings
+     *
+     * @return Constraints
+     *
+     * @see NetworkType.CONNECTED
+     */
+    fun getConstraintsForDiagnosisKeyOneTimeBackgroundWork() =
+        Constraints
+            .Builder()
+            .setRequiresBatteryNotLow(true)
+            .setRequiredNetworkType(NetworkType.CONNECTED)
+            .build()
+}
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkScheduler.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkScheduler.kt
index c4e946d982ce78986df0135c77e62fa5ad8d0f47..bb7bf742c5600350bcf0f29ae0aa5a878db8fe93 100644
--- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkScheduler.kt
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/BackgroundWorkScheduler.kt
@@ -2,10 +2,8 @@ package de.rki.coronawarnapp.worker
 
 import android.util.Log
 import androidx.work.BackoffPolicy
-import androidx.work.Constraints
 import androidx.work.ExistingPeriodicWorkPolicy
 import androidx.work.ExistingWorkPolicy
-import androidx.work.NetworkType
 import androidx.work.OneTimeWorkRequestBuilder
 import androidx.work.Operation
 import androidx.work.PeriodicWorkRequestBuilder
@@ -13,6 +11,7 @@ import androidx.work.WorkInfo
 import androidx.work.WorkManager
 import de.rki.coronawarnapp.BuildConfig
 import de.rki.coronawarnapp.CoronaWarnApplication
+import de.rki.coronawarnapp.storage.LocalData
 import org.joda.time.DateTime
 import org.joda.time.DateTimeZone
 import org.joda.time.Instant
@@ -21,9 +20,10 @@ import java.util.concurrent.TimeUnit
 
 /**
  * Singleton class for background work handling
- * The helper uses externalised constants for readability.
+ * The helper uses externalised constants and helper for readability.
  *
  * @see BackgroundConstants
+ * @see BackgroundWorkHelper
  */
 object BackgroundWorkScheduler {
 
@@ -36,10 +36,12 @@ object BackgroundWorkScheduler {
      *
      * @see BackgroundConstants.DIAGNOSIS_KEY_ONE_TIME_WORKER_TAG
      * @see BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORKER_TAG
+     * @see BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER_TAG
      */
     enum class WorkTag(val tag: String) {
         DIAGNOSIS_KEY_RETRIEVAL_ONE_TIME_WORKER(BackgroundConstants.DIAGNOSIS_KEY_ONE_TIME_WORKER_TAG),
-        DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER(BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORKER_TAG)
+        DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER(BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORKER_TAG),
+        DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER(BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER_TAG)
     }
 
     /**
@@ -49,35 +51,14 @@ object BackgroundWorkScheduler {
      *
      * @see BackgroundConstants.DIAGNOSIS_KEY_ONE_TIME_WORK_NAME
      * @see BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORK_NAME
+     * @see BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_WORK_NAME
      */
     enum class WorkType(val uniqueName: String) {
         DIAGNOSIS_KEY_BACKGROUND_ONE_TIME_WORK(BackgroundConstants.DIAGNOSIS_KEY_ONE_TIME_WORK_NAME),
-        DIAGNOSIS_KEY_BACKGROUND_PERIODIC_WORK(BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORK_NAME)
+        DIAGNOSIS_KEY_BACKGROUND_PERIODIC_WORK(BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_WORK_NAME),
+        DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER(BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_WORK_NAME)
     }
 
-    /**
-     * Calculate the time for diagnosis key retrieval periodic work
-     *
-     * @return Long
-     *
-     * @see BackgroundConstants.MINUTES_IN_DAY
-     * @see getDiagnosisKeyRetrievalMaximumCalls
-     */
-    private fun getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(): Long =
-        (BackgroundConstants.MINUTES_IN_DAY / getDiagnosisKeyRetrievalMaximumCalls()).toLong()
-
-    /**
-     * Get maximum calls count to Google API
-     *
-     * @return Long
-     *
-     * @see BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY
-     * @see BackgroundConstants.GOOGLE_API_MAX_CALLS_PER_DAY
-     */
-    private fun getDiagnosisKeyRetrievalMaximumCalls() =
-        BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY
-            .coerceAtMost(BackgroundConstants.GOOGLE_API_MAX_CALLS_PER_DAY)
-
     /**
      * Work manager instance
      */
@@ -86,7 +67,9 @@ object BackgroundWorkScheduler {
     /**
      * Start work scheduler
      * Checks if periodic worker was already scheduled. If not - reschedule it again.
+     * For [WorkTag.DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER] also checks if User is Registered
      *
+     * @see LocalData.registrationToken
      * @see isWorkActive
      */
     fun startWorkScheduler() {
@@ -95,9 +78,27 @@ object BackgroundWorkScheduler {
             WorkTag.DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER.tag,
             isPeriodicWorkActive
         )
-        if (!isPeriodicWorkActive) WorkType.DIAGNOSIS_KEY_BACKGROUND_PERIODIC_WORK.start()
+        if (!isPeriodicWorkActive) {
+            WorkType.DIAGNOSIS_KEY_BACKGROUND_PERIODIC_WORK.start()
+        }
+        if (!isWorkActive(WorkTag.DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER.tag) &&
+            LocalData.registrationToken() != null && !LocalData.isTestResultNotificationSent()
+        ) {
+            WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.start()
+            LocalData.initialPollingForTestResultTimeStamp(System.currentTimeMillis())
+        }
     }
 
+    /**
+     * Stop work by unique name
+     *
+     * @return Operation
+     *
+     * @see WorkType
+     */
+    fun WorkType.stop(): Operation =
+        workManager.cancelUniqueWork(this.uniqueName)
+
     /**
      * Checks if defined work is active
      * Non-active means worker was Cancelled, Failed or have not been enqueued at all
@@ -167,6 +168,7 @@ object BackgroundWorkScheduler {
     private fun WorkType.start(): Operation = when (this) {
         WorkType.DIAGNOSIS_KEY_BACKGROUND_PERIODIC_WORK -> enqueueDiagnosisKeyBackgroundPeriodicWork()
         WorkType.DIAGNOSIS_KEY_BACKGROUND_ONE_TIME_WORK -> enqueueDiagnosisKeyBackgroundOneTimeWork()
+        WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER -> enqueueDiagnosisTestResultBackgroundPeriodicWork()
     }
 
     /**
@@ -197,6 +199,22 @@ object BackgroundWorkScheduler {
         buildDiagnosisKeyRetrievalOneTimeWork()
     ).also { it.logOperationSchedule(WorkType.DIAGNOSIS_KEY_BACKGROUND_ONE_TIME_WORK) }
 
+    /**
+     * Enqueue diagnosis Test Result periodic
+     * Show a Notification when new Test Results are in.
+     * Replace with new if older work exists.
+     *
+     * @return Operation
+     *
+     * @see WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER
+     */
+    private fun enqueueDiagnosisTestResultBackgroundPeriodicWork() =
+        workManager.enqueueUniquePeriodicWork(
+            WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.uniqueName,
+            ExistingPeriodicWorkPolicy.REPLACE,
+            buildDiagnosisTestResultRetrievalPeriodicWork()
+        ).also { it.logOperationSchedule(WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER) }
+
     /**
      * Build diagnosis key periodic work request
      * Set "kind delay" for accessibility reason.
@@ -205,22 +223,22 @@ object BackgroundWorkScheduler {
      * @return PeriodicWorkRequest
      *
      * @see WorkTag.DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER
-     * @see BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_KIND_DELAY
+     * @see BackgroundConstants.KIND_DELAY
      * @see BackoffPolicy.LINEAR
      */
     private fun buildDiagnosisKeyRetrievalPeriodicWork() =
         PeriodicWorkRequestBuilder<DiagnosisKeyRetrievalPeriodicWorker>(
-            getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(), TimeUnit.MINUTES
+            BackgroundWorkHelper.getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(), TimeUnit.MINUTES
         )
             .addTag(WorkTag.DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER.tag)
-            .setConstraints(getConstraintsForDiagnosisKeyPeriodicBackgroundWork())
+            .setConstraints(BackgroundWorkHelper.getConstraintsForDiagnosisKeyPeriodicBackgroundWork())
             .setInitialDelay(
-                BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_KIND_DELAY,
+                BackgroundConstants.KIND_DELAY,
                 TimeUnit.MINUTES
             )
             .setBackoffCriteria(
                 BackoffPolicy.LINEAR,
-                BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_KIND_DELAY,
+                BackgroundConstants.KIND_DELAY,
                 TimeUnit.MINUTES
             )
             .build()
@@ -234,13 +252,13 @@ object BackgroundWorkScheduler {
      *
      * @see WorkTag.DIAGNOSIS_KEY_RETRIEVAL_ONE_TIME_WORKER
      * @see buildDiagnosisKeyRetrievalOneTimeWork
-     * @see BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_KIND_DELAY
+     * @see BackgroundConstants.KIND_DELAY
      * @see BackoffPolicy.LINEAR
      */
     private fun buildDiagnosisKeyRetrievalOneTimeWork() =
         OneTimeWorkRequestBuilder<DiagnosisKeyRetrievalOneTimeWorker>()
             .addTag(WorkTag.DIAGNOSIS_KEY_RETRIEVAL_ONE_TIME_WORKER.tag)
-            .setConstraints(getConstraintsForDiagnosisKeyOneTimeBackgroundWork())
+            .setConstraints(BackgroundWorkHelper.getConstraintsForDiagnosisKeyOneTimeBackgroundWork())
             .setInitialDelay(
                 DiagnosisKeyRetrievalTimeCalculator.generateDiagnosisKeyRetrievalOneTimeWorkRandomDuration(
                     DateTime(
@@ -251,50 +269,59 @@ object BackgroundWorkScheduler {
             )
             .setBackoffCriteria(
                 BackoffPolicy.LINEAR,
-                BackgroundConstants.DIAGNOSIS_KEY_PERIODIC_KIND_DELAY,
+                BackgroundConstants.KIND_DELAY,
                 TimeUnit.MINUTES
             )
             .build()
 
     /**
-     * Constraints for diagnosis key periodic work
-     * Do not execute background work if battery on low level.
-     *
-     * @return Constraints
-     */
-    private fun getConstraintsForDiagnosisKeyPeriodicBackgroundWork() =
-        Constraints.Builder().setRequiresBatteryNotLow(true).build()
-
-    /**
-     * Constraints for diagnosis key one time work
-     * Requires battery not low and any network connection
-     * Mobile data usage is handled on OS level in application settings
+     * Build diagnosis Test Result periodic work request
+     * Set "kind delay" for accessibility reason.
      *
-     * @return Constraints
+     * @return PeriodicWorkRequest
      *
-     * @see NetworkType.CONNECTED
+     * @see WorkTag.DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER
+     * @see BackgroundConstants.KIND_DELAY
      */
-    private fun getConstraintsForDiagnosisKeyOneTimeBackgroundWork() =
-        Constraints
-            .Builder()
-            .setRequiresBatteryNotLow(true)
-            .setRequiredNetworkType(NetworkType.CONNECTED)
+    private fun buildDiagnosisTestResultRetrievalPeriodicWork() =
+        PeriodicWorkRequestBuilder<DiagnosisTestResultRetrievalPeriodicWorker>(
+            BackgroundWorkHelper.getDiagnosisTestResultRetrievalPeriodicWorkTimeInterval(), TimeUnit.MINUTES
+        )
+            .addTag(WorkTag.DIAGNOSIS_TEST_RESULT_RETRIEVAL_PERIODIC_WORKER.tag)
+            .setConstraints(BackgroundWorkHelper.getConstraintsForDiagnosisKeyOneTimeBackgroundWork())
+            .setInitialDelay(
+                BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_INITIAL_DELAY,
+                TimeUnit.SECONDS
+            ).setBackoffCriteria(
+                BackoffPolicy.LINEAR,
+                BackgroundConstants.KIND_DELAY,
+                TimeUnit.MINUTES
+            )
             .build()
 
     /**
      * Log operation schedule
      */
-    private fun Operation.logOperationSchedule(workType: WorkType) = this.result.addListener({
-        if (BuildConfig.DEBUG) Log.d(TAG, "${workType.uniqueName} completed.")
-    }, { it.run() }).also { if (BuildConfig.DEBUG) Log.d(TAG, "${workType.uniqueName} scheduled.") }
+    private fun Operation.logOperationSchedule(workType: WorkType) =
+        this.result.addListener({
+            if (BuildConfig.DEBUG) Log.d(
+                TAG,
+                "${workType.uniqueName} completed."
+            )
+        }, { it.run() })
+            .also { if (BuildConfig.DEBUG) Log.d(TAG, "${workType.uniqueName} scheduled.") }
 
     /**
      * Log operation cancellation
      */
-    private fun Operation.logOperationCancelByTag(workTag: WorkTag) = this.result.addListener({
-        if (BuildConfig.DEBUG) Log.d(TAG, "All work with tag ${workTag.tag} canceled.")
-    }, { it.run() })
-        .also { if (BuildConfig.DEBUG) Log.d(TAG, "Canceling all work with tag ${workTag.tag}") }
+    private fun Operation.logOperationCancelByTag(workTag: WorkTag) =
+        this.result.addListener({
+            if (BuildConfig.DEBUG) Log.d(
+                TAG,
+                "All work with tag ${workTag.tag} canceled."
+            )
+        }, { it.run() })
+            .also { if (BuildConfig.DEBUG) Log.d(TAG, "Canceling all work with tag ${workTag.tag}") }
 
     /**
      * Log work active status
diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt
new file mode 100644
index 0000000000000000000000000000000000000000..e1b40d62d61a12a1ec14fc93b308622f2adb4a5b
--- /dev/null
+++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisTestResultRetrievalPeriodicWorker.kt
@@ -0,0 +1,116 @@
+package de.rki.coronawarnapp.worker
+
+import android.content.Context
+import android.util.Log
+import androidx.core.app.NotificationCompat
+import androidx.work.CoroutineWorker
+import androidx.work.WorkerParameters
+import de.rki.coronawarnapp.BuildConfig
+import de.rki.coronawarnapp.CoronaWarnApplication
+import de.rki.coronawarnapp.R
+import de.rki.coronawarnapp.notification.NotificationHelper
+import de.rki.coronawarnapp.service.submission.SubmissionService
+import de.rki.coronawarnapp.storage.LocalData
+import de.rki.coronawarnapp.util.TimeAndDateExtensions
+import de.rki.coronawarnapp.util.formatter.TestResult
+import de.rki.coronawarnapp.worker.BackgroundWorkScheduler.stop
+
+/**
+ * Diagnosis Test Result Periodic retrieavl
+ *
+ * @see BackgroundWorkScheduler
+ */
+class DiagnosisTestResultRetrievalPeriodicWorker(
+    val context: Context,
+    workerParams: WorkerParameters
+) :
+    CoroutineWorker(context, workerParams) {
+
+    companion object {
+        private val TAG: String? = DiagnosisTestResultRetrievalPeriodicWorker::class.simpleName
+    }
+
+    /**
+     * Work execution
+     *
+     * If background job is running for less than 21 days, testResult is checked.
+     * If the job is running for more than 21 days, the job will be stopped
+     *
+     * @return Result
+     *
+     * @see LocalData.isTestResultNotificationSent
+     * @see LocalData.initialPollingForTestResultTimeStamp
+     */
+    override suspend fun doWork(): Result {
+
+        if (BuildConfig.DEBUG) Log.d(
+            TAG,
+            "Background job started. Run attempt: $runAttemptCount"
+        )
+
+        if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) {
+            if (BuildConfig.DEBUG) Log.d(
+                TAG,
+                "Background job failed after $runAttemptCount attempts. Rescheduling"
+            )
+            BackgroundWorkScheduler.scheduleDiagnosisKeyPeriodicWork()
+            return Result.failure()
+        }
+        var result = Result.success()
+        try {
+            if (TimeAndDateExtensions.calculateDays(
+                    LocalData.initialPollingForTestResultTimeStamp(),
+                    System.currentTimeMillis()
+                ) < BackgroundConstants.POLLING_VALIDITY_MAX_DAYS
+            ) {
+                val testResult = SubmissionService.asyncRequestTestResult()
+                initiateNotification(testResult)
+            } else {
+                stopWorker()
+            }
+        } catch (e: Exception) {
+            result = Result.retry()
+        }
+        return result
+    }
+
+    /**
+     * Notification Initiation
+     *
+     * If the returned Test Result is Negative, Positive or Invalid
+     * The Background polling  will be stopped
+     * and a notification is shown, but only if the App is not in foreground
+     *
+     * @see LocalData.isTestResultNotificationSent
+     * @see LocalData.initialPollingForTestResultTimeStamp
+     * @see TestResult
+     */
+    private fun initiateNotification(testResult: TestResult) {
+        if (testResult == TestResult.NEGATIVE || testResult == TestResult.POSITIVE ||
+            testResult == TestResult.INVALID
+        ) {
+            if (!CoronaWarnApplication.isAppInForeground) {
+                NotificationHelper.sendNotification(
+                    CoronaWarnApplication.getAppContext()
+                        .getString(R.string.notification_name), CoronaWarnApplication.getAppContext()
+                        .getString(R.string.notification_body),
+                    NotificationCompat.PRIORITY_HIGH
+                )
+            }
+            LocalData.isTestResultNotificationSent(true)
+            stopWorker()
+        }
+    }
+
+    /**
+     * Stops the Background Polling worker
+     *
+     * @see LocalData.initialPollingForTestResultTimeStamp
+     * @see BackgroundWorkScheduler.stop
+
+     */
+    private fun stopWorker() {
+        LocalData.initialPollingForTestResultTimeStamp(0L)
+        BackgroundWorkScheduler.WorkType.DIAGNOSIS_TEST_RESULT_PERIODIC_WORKER.stop()
+    }
+}
diff --git a/Corona-Warn-App/src/main/res/values/strings.xml b/Corona-Warn-App/src/main/res/values/strings.xml
index 62f103e4287bca2673f8932498ba4d21764c9132..3e6c8c43fbfd2b58c619dbcd14f17894a731ffa7 100644
--- a/Corona-Warn-App/src/main/res/values/strings.xml
+++ b/Corona-Warn-App/src/main/res/values/strings.xml
@@ -126,6 +126,14 @@
         <xliff:g id="preference">preference_last_three_hours_from_server</xliff:g>
     </string>
 
+    <string name="preference_polling_test_result_started">
+        <xliff:g id="preference">preference_polling_test_result_started</xliff:g>
+    </string>
+
+    <string name="preference_test_result_notification">
+        <xliff:g id="preference">preference_test_result_notification</xliff:g>
+    </string>
+
     <!-- ####################################
                      Generics
     ###################################### -->