diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 55275b21ec..b8bf2922f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.migrations.AttachmentCleanupMigrationJob; import org.thoughtcrime.securesms.migrations.AttributesMigrationJob; import org.thoughtcrime.securesms.migrations.AvatarIdRemovalMigrationJob; import org.thoughtcrime.securesms.migrations.AvatarMigrationJob; +import org.thoughtcrime.securesms.migrations.BackupJitterMigrationJob; import org.thoughtcrime.securesms.migrations.BackupNotificationMigrationJob; import org.thoughtcrime.securesms.migrations.BlobStorageLocationMigrationJob; import org.thoughtcrime.securesms.migrations.CachedAttachmentsMigrationJob; @@ -100,7 +101,7 @@ public final class JobManagerFactories { put(CheckServiceReachabilityJob.KEY, new CheckServiceReachabilityJob.Factory()); put(CleanPreKeysJob.KEY, new CleanPreKeysJob.Factory()); put(ClearFallbackKbsEnclaveJob.KEY, new ClearFallbackKbsEnclaveJob.Factory()); - put(ConversationShortcutRankingUpdateJob.KEY, new ConversationShortcutRankingUpdateJob.Factory()); + put(ConversationShortcutRankingUpdateJob.KEY, new ConversationShortcutRankingUpdateJob.Factory()); put(ConversationShortcutUpdateJob.KEY, new ConversationShortcutUpdateJob.Factory()); put(CreateReleaseChannelJob.KEY, new CreateReleaseChannelJob.Factory()); put(DirectoryRefreshJob.KEY, new DirectoryRefreshJob.Factory()); @@ -220,6 +221,7 @@ public final class JobManagerFactories { put(AttributesMigrationJob.KEY, new AttributesMigrationJob.Factory()); put(AvatarIdRemovalMigrationJob.KEY, new AvatarIdRemovalMigrationJob.Factory()); put(AvatarMigrationJob.KEY, new AvatarMigrationJob.Factory()); + put(BackupJitterMigrationJob.KEY, new BackupJitterMigrationJob.Factory()); put(BackupNotificationMigrationJob.KEY, new BackupNotificationMigrationJob.Factory()); put(BlobStorageLocationMigrationJob.KEY, new BlobStorageLocationMigrationJob.Factory()); put(CachedAttachmentsMigrationJob.KEY, new CachedAttachmentsMigrationJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java index 3d96790808..13c9e7d06f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -24,6 +24,7 @@ import org.thoughtcrime.securesms.webrtc.CallBandwidthMode; import java.util.Arrays; import java.util.List; +import java.util.Random; @SuppressWarnings("deprecation") public final class SettingsValues extends SignalStoreValues { @@ -72,6 +73,9 @@ public final class SettingsValues extends SignalStoreValues { private static final String KEEP_MUTED_CHATS_ARCHIVED = "settings.keepMutedChatsArchived"; private static final String USE_COMPACT_NAVIGATION_BAR = "settings.useCompactNavigationBar"; + public static final int BACKUP_DEFAULT_HOUR = 2; + public static final int BACKUP_DEFAULT_MINUTE = 0; + private final SingleLiveEvent onConfigurationSettingChanged = new SingleLiveEvent<>(); SettingsValues(@NonNull KeyValueStore store) { @@ -80,10 +84,15 @@ public final class SettingsValues extends SignalStoreValues { @Override void onFirstEverAppLaunch() { - if (!getStore().containsKey(LINK_PREVIEWS)) { - getStore().beginWrite() - .putBoolean(LINK_PREVIEWS, true) - .apply(); + final KeyValueStore store = getStore(); + if (!store.containsKey(LINK_PREVIEWS)) { + store.beginWrite() + .putBoolean(LINK_PREVIEWS, true) + .apply(); + } + if (!store.containsKey(BACKUPS_SCHEDULE_HOUR)) { + // Initialize backup time to a 5min interval between 1-5am + setBackupSchedule(new Random().nextInt(5) + 1, new Random().nextInt(12) * 5); } } @@ -267,11 +276,11 @@ public final class SettingsValues extends SignalStoreValues { } public int getBackupHour() { - return getInteger(BACKUPS_SCHEDULE_HOUR, 2); + return getInteger(BACKUPS_SCHEDULE_HOUR, BACKUP_DEFAULT_HOUR); } public int getBackupMinute() { - return getInteger(BACKUPS_SCHEDULE_MINUTE, 0); + return getInteger(BACKUPS_SCHEDULE_MINUTE, BACKUP_DEFAULT_MINUTE); } public void setBackupSchedule(int hour, int minute) { @@ -449,7 +458,7 @@ public final class SettingsValues extends SignalStoreValues { } public void setKeepMutedChatsArchived(boolean enabled) { - putBoolean(KEEP_MUTED_CHATS_ARCHIVED, enabled); + putBoolean(KEEP_MUTED_CHATS_ARCHIVED, enabled); } public boolean shouldKeepMutedChatsArchived() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index dbc74b629a..5732799960 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -12,7 +12,6 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.jobmanager.JobManager; -import org.thoughtcrime.securesms.jobs.RefreshAttributesJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.stickers.BlessedPacks; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -128,9 +127,10 @@ public class ApplicationMigrations { static final int REBUILD_MESSAGE_FTS_INDEX_4 = 83; static final int INDEX_DATABASE_MIGRATION = 84; static final int ACCOUNT_CONSISTENCY_CHECK = 85; + static final int BACKUP_JITTER = 86; } - public static final int CURRENT_VERSION = 85; + public static final int CURRENT_VERSION = 86; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -572,6 +572,10 @@ public class ApplicationMigrations { jobs.put(Version.ACCOUNT_CONSISTENCY_CHECK, new AccountConsistencyMigrationJob()); } + if (lastSeenVersion < Version.BACKUP_JITTER) { + jobs.put(Version.BACKUP_JITTER, new BackupJitterMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/BackupJitterMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/BackupJitterMigrationJob.kt new file mode 100644 index 0000000000..fba71ce2e8 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/BackupJitterMigrationJob.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.keyvalue.SettingsValues +import org.thoughtcrime.securesms.keyvalue.SignalStore +import java.util.Random + +internal class BackupJitterMigrationJob(parameters: Parameters = Parameters.Builder().build()) : MigrationJob(parameters) { + companion object { + const val KEY = "BackupJitterMigrationJob" + val TAG = Log.tag(BackupJitterMigrationJob::class.java) + } + + override fun getFactoryKey(): String = KEY + + override fun isUiBlocking(): Boolean = false + + override fun performMigration() { + val hour = SignalStore.settings().backupHour + val minute = SignalStore.settings().backupMinute + if (hour == SettingsValues.BACKUP_DEFAULT_HOUR && minute == SettingsValues.BACKUP_DEFAULT_MINUTE) { + val rand = Random() + val newHour = rand.nextInt(3) + 1 // between 1AM - 3AM + val newMinute = rand.nextInt(12) * 5 // 5 minute intervals up to +55 minutes + SignalStore.settings().setBackupSchedule(newHour, newMinute) + } + } + + override fun shouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): BackupJitterMigrationJob { + return BackupJitterMigrationJob(parameters) + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java index 849cf678f9..26b2ffadba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java +++ b/app/src/main/java/org/thoughtcrime/securesms/service/LocalBackupListener.java @@ -6,14 +6,19 @@ import android.content.Context; import androidx.annotation.NonNull; import org.thoughtcrime.securesms.jobs.LocalBackupJob; +import org.thoughtcrime.securesms.keyvalue.SettingsValues; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.util.JavaTimeExtensionsKt; import org.thoughtcrime.securesms.util.TextSecurePreferences; import java.time.LocalDateTime; +import java.util.Random; +import java.util.concurrent.TimeUnit; public class LocalBackupListener extends PersistentAlarmManagerListener { + private static final int BACKUP_JITTER_WINDOW_SECONDS = Math.toIntExact(TimeUnit.MINUTES.toSeconds(10)); + @Override protected boolean shouldScheduleExact() { return true; @@ -44,6 +49,11 @@ public class LocalBackupListener extends PersistentAlarmManagerListener { int hour = SignalStore.settings().getBackupHour(); int minute = SignalStore.settings().getBackupMinute(); LocalDateTime next = now.withHour(hour).withMinute(minute).withSecond(0); + + int jitter = (new Random().nextInt(BACKUP_JITTER_WINDOW_SECONDS)) - (BACKUP_JITTER_WINDOW_SECONDS / 2); + + next.plusSeconds(jitter); + if (now.isAfter(next)) { next = next.plusDays(1); }