From fc9d3e11e8d5a11b6de4ac2bd5b63139d061b529 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Tue, 28 Apr 2026 17:19:44 -0400 Subject: [PATCH] Only retrieve remote announcements during specific time window. --- .../securesms/database/JobDatabase.kt | 2 +- .../jobs/RetrieveRemoteAnnouncementsJob.kt | 38 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt index 1c1d859170..ca17c20440 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/JobDatabase.kt @@ -485,7 +485,7 @@ class JobDatabase( /** Should only be used for debugging! */ fun debugResetBackoffInterval() { - writableDatabase.update(Jobs.TABLE_NAME, contentValuesOf(Jobs.NEXT_BACKOFF_INTERVAL to 0), null, null) + writableDatabase.update(Jobs.TABLE_NAME, contentValuesOf(Jobs.NEXT_BACKOFF_INTERVAL to 0, Jobs.INITIAL_DELAY to 0), null, null) } private fun JobSpec.toContentValues(): ContentValues { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt index d47cf8333c..557ffd8743 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveRemoteAnnouncementsJob.kt @@ -35,8 +35,12 @@ import org.whispersystems.signalservice.internal.ServiceResponse import java.io.IOException import java.lang.Integer.max import java.security.MessageDigest +import java.time.LocalTime +import java.time.ZonedDateTime import java.util.Locale import java.util.concurrent.TimeUnit +import kotlin.time.Duration +import kotlin.time.Duration.Companion.milliseconds /** * Retrieves and processes release channel messages. @@ -52,6 +56,9 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool private val TAG = Log.tag(RetrieveRemoteAnnouncementsJob::class.java) private val RETRIEVE_FREQUENCY = TimeUnit.DAYS.toMillis(3) + private val WINDOW_START = LocalTime.of(9, 0) + private val WINDOW_END = LocalTime.of(21, 0) + @JvmStatic @JvmOverloads fun enqueue(force: Boolean = false) { @@ -65,13 +72,18 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool return } + val windowDelay = calculateTimeToNextWindow() + if (windowDelay > Duration.ZERO) { + Log.i(TAG, "Outside window, delaying ${windowDelay.inWholeMinutes} minutes") + } + val job = RetrieveRemoteAnnouncementsJob( force, Parameters.Builder() .setQueue("RetrieveReleaseChannelJob") - .setMaxInstancesForFactory(1) .setMaxAttempts(3) .addConstraint(NetworkConstraint.KEY) + .setInitialDelay(windowDelay.inWholeMilliseconds) .build() ) @@ -80,6 +92,23 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool .then(job) .enqueue() } + + private fun calculateTimeToNextWindow(): Duration { + val now = ZonedDateTime.now() + val time = now.toLocalTime() + + if (time.isAfter(WINDOW_START) && time.isBefore(WINDOW_END)) { + return Duration.ZERO + } + + val next9am = if (time.isBefore(WINDOW_START)) { + now.with(WINDOW_START) + } else { + now.plusDays(1).with(WINDOW_START) + } + + return (next9am.toInstant().toEpochMilli() - System.currentTimeMillis()).milliseconds + } } override fun serialize(): ByteArray? = JsonJobData.Builder().putBoolean(KEY_FORCE, force).serialize() @@ -106,6 +135,13 @@ class RetrieveRemoteAnnouncementsJob private constructor(private val force: Bool return } + val delay = calculateTimeToNextWindow() + if (delay > Duration.ZERO) { + Log.i(TAG, "Outside window, re-enqueuing") + enqueue(force = force) + return + } + val manifestMd5: ByteArray? = S3.getObjectMD5(MANIFEST) if (manifestMd5 == null) {