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 30ad926c412aed40bc31db4e29927bd5f7c7d17e..df62ad1ccc6a2565db759f2fd22daffad489a267 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 @@ -48,7 +48,7 @@ object BackgroundConstants { * Total tries count for diagnosis key retrieval per day * Internal requirement */ - const val DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY = 1 + const val DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY = 12 /** * Maximum tries count for diagnosis key retrieval per day @@ -77,46 +77,21 @@ object BackgroundConstants { const val DIAGNOSIS_TEST_RESULT_PERIODIC_INITIAL_DELAY = 10L /** - * Minimum initial delay in minutes for diagnosis key retrieval one time work - * - * @see DiagnosisKeyRetrievalTimeCalculator.getPossibleSchedulingTimes - * @see TimeUnit.MINUTES - */ - const val DIAGNOSIS_KEY_RETRIEVAL_MIN_DELAY = 0 - - /** - * Maximum initial delay in minutes for diagnosis key retrieval one time work - * - * @see DiagnosisKeyRetrievalTimeCalculator.getPossibleSchedulingTimes - * @see TimeUnit.MINUTES + * Retries before work would set as FAILED */ - const val DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY = 59 + const val WORKER_RETRY_COUNT_THRESHOLD = 2 /** - * Time schedule start in minutes of day - * 07:00 = 420 minutes passed midnight + * The maximum validity in days for keeping Background polling active * - * @see TimeUnit.MINUTES + * @see TimeUnit.DAYS */ - const val TIME_RANGE_MIN = 420 + const val POLLING_VALIDITY_MAX_DAYS = 21 /** - * Time schedule stop in minutes of day - * 23:59 = 1439 minutes passed midnight + * Backoff initial delay * * @see TimeUnit.MINUTES */ - const val TIME_RANGE_MAX = 1439 - - /** - * 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 + const val BACKOFF_INITIAL_DELAY = 8L } 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 index b981162c45bf3a0b1d3c4a09fa6c217e07af4ffb..593d1c2eef7e6ce4a723543d7cb3abf01d873249 100644 --- 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 @@ -46,15 +46,6 @@ object BackgroundWorkHelper { 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 @@ -67,7 +58,6 @@ object BackgroundWorkHelper { 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 aaaa0d92c8c76b09dd1054fd89ec432a8e2af26b..0cc08f34721b7661a48b9fd529b5a81e2555e893 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 @@ -11,9 +11,6 @@ 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 import timber.log.Timber import java.util.concurrent.ExecutionException import java.util.concurrent.TimeUnit @@ -27,8 +24,6 @@ import java.util.concurrent.TimeUnit */ object BackgroundWorkScheduler { - private val TAG: String? = BackgroundWorkScheduler::class.simpleName - /** * Enum class for work tags * @@ -224,6 +219,7 @@ object BackgroundWorkScheduler { * * @see WorkTag.DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER * @see BackgroundConstants.KIND_DELAY + * @see BackgroundConstants.BACKOFF_INITIAL_DELAY * @see BackoffPolicy.LINEAR */ private fun buildDiagnosisKeyRetrievalPeriodicWork() = @@ -231,14 +227,13 @@ object BackgroundWorkScheduler { BackgroundWorkHelper.getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(), TimeUnit.MINUTES ) .addTag(WorkTag.DIAGNOSIS_KEY_RETRIEVAL_PERIODIC_WORKER.tag) - .setConstraints(BackgroundWorkHelper.getConstraintsForDiagnosisKeyPeriodicBackgroundWork()) .setInitialDelay( BackgroundConstants.KIND_DELAY, TimeUnit.MINUTES ) .setBackoffCriteria( - BackoffPolicy.LINEAR, - BackgroundConstants.KIND_DELAY, + BackoffPolicy.EXPONENTIAL, + BackgroundConstants.BACKOFF_INITIAL_DELAY, TimeUnit.MINUTES ) .build() @@ -252,7 +247,7 @@ object BackgroundWorkScheduler { * * @see WorkTag.DIAGNOSIS_KEY_RETRIEVAL_ONE_TIME_WORKER * @see buildDiagnosisKeyRetrievalOneTimeWork - * @see BackgroundConstants.KIND_DELAY + * @see BackgroundConstants.BACKOFF_INITIAL_DELAY * @see BackoffPolicy.LINEAR */ private fun buildDiagnosisKeyRetrievalOneTimeWork() = @@ -260,16 +255,12 @@ object BackgroundWorkScheduler { .addTag(WorkTag.DIAGNOSIS_KEY_RETRIEVAL_ONE_TIME_WORKER.tag) .setConstraints(BackgroundWorkHelper.getConstraintsForDiagnosisKeyOneTimeBackgroundWork()) .setInitialDelay( - DiagnosisKeyRetrievalTimeCalculator.generateDiagnosisKeyRetrievalOneTimeWorkRandomDuration( - DateTime( - Instant.now(), - DateTimeZone.getDefault() - ) - ), TimeUnit.MINUTES + BackgroundConstants.KIND_DELAY, + TimeUnit.MINUTES ) .setBackoffCriteria( - BackoffPolicy.LINEAR, - BackgroundConstants.KIND_DELAY, + BackoffPolicy.EXPONENTIAL, + BackgroundConstants.BACKOFF_INITIAL_DELAY, TimeUnit.MINUTES ) .build() diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt index 2e418ca126a0d5b97fd6a1b3e6a2cc3c77a7f248..e5688bfa51a997b44dc8763b44559132dcea1310 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalOneTimeWorker.kt @@ -31,12 +31,8 @@ class DiagnosisKeyRetrievalOneTimeWorker(val context: Context, workerParams: Wor * @see RetrieveDiagnosisKeysTransaction */ override suspend fun doWork(): Result { - Timber.d("Background job started. Run attempt: $runAttemptCount") + Timber.d("Background job started. Run attempt: $runAttemptCount ") - if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { - Timber.d("Background job failed after $runAttemptCount attempts. Rescheduling") - return Result.failure() - } var result = Result.success() try { val currentDate = DateTime(Instant.now(), DateTimeZone.getDefault()) @@ -50,7 +46,11 @@ class DiagnosisKeyRetrievalOneTimeWorker(val context: Context, workerParams: Wor RetrieveDiagnosisKeysTransaction.start() } } catch (e: Exception) { - result = Result.retry() + if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { + return Result.failure() + } else { + result = Result.retry() + } } return result } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt index e69deb0c120fec858b8a9f3bdf14ed0560795085..5722a2c48787711f891919c6663b3cae582a52a3 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalPeriodicWorker.kt @@ -30,16 +30,15 @@ class DiagnosisKeyRetrievalPeriodicWorker(val context: Context, workerParams: Wo override suspend fun doWork(): Result { Timber.d("Background job started. Run attempt: $runAttemptCount") - if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { - Timber.d("Background job failed after $runAttemptCount attempts. Rescheduling") - BackgroundWorkScheduler.scheduleDiagnosisKeyPeriodicWork() - return Result.failure() - } var result = Result.success() try { BackgroundWorkScheduler.scheduleDiagnosisKeyOneTimeWork() } catch (e: Exception) { - result = Result.retry() + if (runAttemptCount > BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD) { + return Result.failure() + } else { + result = Result.retry() + } } return result } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculator.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculator.kt deleted file mode 100644 index ce163686aa75448244a3bdccceff432f82082ea0..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculator.kt +++ /dev/null @@ -1,92 +0,0 @@ -package de.rki.coronawarnapp.worker - -import org.joda.time.DateTime -import org.joda.time.Duration -import kotlin.random.Random - -object DiagnosisKeyRetrievalTimeCalculator { - - /** - * Generates a random duration for scheduling a job - * - * TODO check how to simplify and check edge cases - * - * @param currentTime - * - * @return random duration to schedule job - * - * @see getPossibleSchedulingTimes - */ - fun generateDiagnosisKeyRetrievalOneTimeWorkRandomDuration(currentTime: DateTime): Long { - val rangeForDelay = getPossibleSchedulingTimes(currentTime) - - val minMinutesOfDelay = convertTimestampToMinutesFromNow(rangeForDelay.first, currentTime) - val maxMinutesOfDelay = convertTimestampToMinutesFromNow(rangeForDelay.second, currentTime) - - if (minMinutesOfDelay == maxMinutesOfDelay) { - return minMinutesOfDelay.toLong() - } - return Random.nextInt(minMinutesOfDelay, maxMinutesOfDelay).toLong() - } - - /** - * Get the earliest and latest possible scheduling time for a job based on BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MIN_DELAY and BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY - * The delay should be generated within BackgroundConstants.TIME_RANGE_MIN and BackgroundConstants.TIME_RANGE_MAX - * - * Cases: - * - * Case 1: if planned scheduling min and max are within the window of BackgroundConstants.TIME_RANGE_MIN and BackgroundConstants.TIME_RANGE_MAX - * earliest possible scheduling would be planned scheduling min - * latest possible scheduling would be planned scheduling max - * - * Case 2: if planned scheduling min is before BackgroundConstants.TIME_RANGE_MIN - * earliest possible scheduling would be BackgroundConstants.TIME_RANGE_MIN - * latest possible scheduling would be BackgroundConstants.TIME_RANGE_MIN plus BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY - * - * Case 3: if planned scheduling min is after BackgroundConstants.TIME_RANGE_MAX - * earliest possible scheduling would be planned scheduling min - * latest possible scheduling would BackgroundConstants.TIME_RANGE_MAX - * - * @param currentTime - * - * @return (earliestSchedulingTime, latestSchedulingTime) - * - */ - fun getPossibleSchedulingTimes(currentTime: DateTime): Pair<DateTime, DateTime> { - val timeMin = currentTime.plusMinutes(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MIN_DELAY) - val timeMax = timeMin.plusMinutes(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY) - - val todayMidnight = DateTime().withTimeAtStartOfDay() - val earliestAllowedScheduleTime = - todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MIN) - val latestAllowedScheduleTime = - todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MAX) - - var earliestSchedulingTime = timeMin - var latestSchedulingTime = timeMax - - if (timeMin.isAfter(earliestAllowedScheduleTime) && timeMax.isBefore( - latestAllowedScheduleTime - ) - ) { - earliestSchedulingTime = timeMin - latestSchedulingTime = timeMax - } else if (timeMin.isBefore(earliestAllowedScheduleTime)) { - earliestSchedulingTime = earliestAllowedScheduleTime - latestSchedulingTime = - earliestAllowedScheduleTime.plusMinutes(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY) - } else if (timeMin.isAfter(earliestAllowedScheduleTime) && timeMax.isAfter( - latestAllowedScheduleTime - ) - ) { - earliestSchedulingTime = timeMin - latestSchedulingTime = latestAllowedScheduleTime - } - return Pair(earliestSchedulingTime, latestSchedulingTime) - } - - private fun convertTimestampToMinutesFromNow(input: DateTime, timeNow: DateTime): Int { - val differenceInMilis = Duration(input.millis - timeNow.millis) - return differenceInMilis.standardMinutes.toInt() - } -} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..161df1e1a35400cdd21eea306492d1dd04fa0e6d --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundConstantsTest.kt @@ -0,0 +1,20 @@ +package de.rki.coronawarnapp.worker + +import org.junit.Assert +import org.junit.Test + +class BackgroundConstantsTest { + + @Test + fun allBackgroundConstants() { + Assert.assertEquals(BackgroundConstants.MINUTES_IN_DAY, 1440) + Assert.assertEquals(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_TRIES_PER_DAY, 12) + Assert.assertEquals(BackgroundConstants.GOOGLE_API_MAX_CALLS_PER_DAY, 20) + Assert.assertEquals(BackgroundConstants.DIAGNOSIS_TEST_RESULT_RETRIEVAL_TRIES_PER_DAY, 12) + Assert.assertEquals(BackgroundConstants.KIND_DELAY, 1L) + Assert.assertEquals(BackgroundConstants.DIAGNOSIS_TEST_RESULT_PERIODIC_INITIAL_DELAY, 10L) + Assert.assertEquals(BackgroundConstants.WORKER_RETRY_COUNT_THRESHOLD, 2) + Assert.assertEquals(BackgroundConstants.POLLING_VALIDITY_MAX_DAYS, 21) + Assert.assertEquals(BackgroundConstants.BACKOFF_INITIAL_DELAY, 8L) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundWorkHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundWorkHelperTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..e6756e38d9b81cbb95752ac4d6e3528f7cc95beb --- /dev/null +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/BackgroundWorkHelperTest.kt @@ -0,0 +1,28 @@ +package de.rki.coronawarnapp.worker + +import androidx.work.NetworkType +import org.junit.Assert +import org.junit.Test + +class BackgroundWorkHelperTest { + @Test + fun getDiagnosisKeyRetrievalPeriodicWorkTimeInterval() { + Assert.assertEquals(BackgroundWorkHelper.getDiagnosisKeyRetrievalPeriodicWorkTimeInterval(), 120) + } + + @Test + fun getDiagnosisTestResultRetrievalPeriodicWorkTimeInterval() { + Assert.assertEquals(BackgroundWorkHelper.getDiagnosisTestResultRetrievalPeriodicWorkTimeInterval(), 120) + } + + @Test + fun getDiagnosisKeyRetrievalMaximumCalls() { + Assert.assertEquals(BackgroundWorkHelper.getDiagnosisKeyRetrievalMaximumCalls(), 12) + } + + @Test + fun getConstraintsForDiagnosisKeyOneTimeBackgroundWork() { + val constraints = BackgroundWorkHelper.getConstraintsForDiagnosisKeyOneTimeBackgroundWork() + Assert.assertEquals(constraints.requiredNetworkType, NetworkType.CONNECTED) + } +} diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculatorTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculatorTest.kt deleted file mode 100644 index 2dee153578b47486623ff5368aeb760abed8e2cf..0000000000000000000000000000000000000000 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/worker/DiagnosisKeyRetrievalTimeCalculatorTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -package de.rki.coronawarnapp.worker - -import org.hamcrest.CoreMatchers.`is` -import org.hamcrest.MatcherAssert.assertThat -import org.hamcrest.Matchers.greaterThanOrEqualTo -import org.hamcrest.Matchers.lessThanOrEqualTo -import org.hamcrest.core.IsEqual.equalTo -import org.joda.time.DateTime -import org.junit.Test - -class DiagnosisKeyRetrievalTimeCalculatorTest { - - @Test - fun getDatetimeRangeForDelayInWindow() { - val timeNow = DateTime() - .withHourOfDay(8) - .withMinuteOfHour(0) - .withSecondOfMinute(0) - - val timeNowPlusMaxDelay = - timeNow.plusMinutes(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY) - - val result = DiagnosisKeyRetrievalTimeCalculator.getPossibleSchedulingTimes(timeNow) - - val todayMidnight = DateTime().withTimeAtStartOfDay() - val windowMin = todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MIN) - val windowMax = todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MAX) - - assertThat(result.first, `is`(equalTo(timeNow))) - assertThat(result.second, `is`(equalTo(timeNowPlusMaxDelay))) - - assertThat(result.first, `is`(greaterThanOrEqualTo(windowMin))) - assertThat(result.second, `is`(lessThanOrEqualTo(windowMax))) - } - - @Test - fun getDatetimeRangeForDelayInWindowStartTimeBeforeWindow() { - - val timeNow = DateTime() - .withHourOfDay(5) - .withMinuteOfHour(20) - .withSecondOfMinute(0) - val result = DiagnosisKeyRetrievalTimeCalculator.getPossibleSchedulingTimes(timeNow) - - val todayMidnight = DateTime().withTimeAtStartOfDay() - val windowMin = todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MIN) - val windowMinPlusTimeRangeMax = - windowMin.plusMinutes(BackgroundConstants.DIAGNOSIS_KEY_RETRIEVAL_MAX_DELAY) - - assertThat(result.first, `is`(equalTo(windowMin))) - assertThat(result.second, `is`(lessThanOrEqualTo(windowMinPlusTimeRangeMax))) - } - - @Test - fun getDatetimeRangeForDelayInWindowEndimeAfterWindow() { - val timeNow = DateTime() - .withHourOfDay(23) - .withMinuteOfHour(20) - .withSecondOfMinute(0) - val result = DiagnosisKeyRetrievalTimeCalculator.getPossibleSchedulingTimes(timeNow) - - val todayMidnight = DateTime().withTimeAtStartOfDay() - val windowMax = todayMidnight.plusMinutes(BackgroundConstants.TIME_RANGE_MAX) - - assertThat(result.first, `is`(equalTo(timeNow))) - assertThat(result.second, `is`(lessThanOrEqualTo(windowMax))) - } -}