From faa19acf81919353e83c90e8ed5e86722e8778dd Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Wed, 17 Mar 2021 12:52:25 -0400 Subject: [PATCH] Include additional settings in backup. --- .../securesms/AppInitialization.java | 1 + .../securesms/backup/FullBackupExporter.java | 53 ++++++++++++++++++- .../securesms/backup/FullBackupImporter.java | 32 +++++++++++ .../securesms/keyvalue/CertificateValues.java | 8 +++ .../securesms/keyvalue/EmojiValues.java | 8 +++ .../securesms/keyvalue/InternalValues.java | 10 ++++ .../securesms/keyvalue/KbsValues.java | 7 +++ .../securesms/keyvalue/KeyValueDataSet.java | 3 +- .../securesms/keyvalue/KeyValueReader.java | 1 + .../securesms/keyvalue/KeyValueStore.java | 7 +++ .../keyvalue/MiscellaneousValues.java | 8 +++ .../securesms/keyvalue/OnboardingValues.java | 8 +++ .../keyvalue/PhoneNumberPrivacyValues.java | 6 +++ .../securesms/keyvalue/PinValues.java | 8 +++ .../securesms/keyvalue/ProxyValues.java | 8 +++ .../keyvalue/RegistrationValues.java | 8 +++ .../keyvalue/RemoteConfigValues.java | 8 +++ .../securesms/keyvalue/SettingsValues.java | 21 ++++++-- .../securesms/keyvalue/SignalStore.java | 25 +++++++++ .../securesms/keyvalue/SignalStoreValues.java | 4 ++ .../keyvalue/StorageServiceValues.java | 8 +++ .../securesms/keyvalue/TooltipValues.java | 8 +++ .../securesms/keyvalue/UiHints.java | 8 +++ .../securesms/keyvalue/WallpaperValues.java | 8 +++ .../securesms/util/TextSecurePreferences.java | 7 +++ app/src/main/proto/Backups.proto | 11 ++++ 26 files changed, 279 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java b/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java index 42fb8d3758..39fb8ab916 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java +++ b/app/src/main/java/org/thoughtcrime/securesms/AppInitialization.java @@ -54,6 +54,7 @@ public final class AppInitialization { ApplicationDependencies.getMegaphoneRepository().onFirstEverAppLaunch(); SignalStore.onFirstEverAppLaunch(); SignalStore.onboarding().clearAll(); + TextSecurePreferences.onPostBackupRestore(context); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.ZOZO.getPackId(), BlessedPacks.ZOZO.getPackKey(), false)); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.BANDIT.getPackId(), BlessedPacks.BANDIT.getPackKey(), false)); ApplicationDependencies.getJobManager().add(StickerPackDownloadJob.forInstall(BlessedPacks.DAY_BY_DAY.getPackId(), BlessedPacks.DAY_BY_DAY.getPackKey(), false)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index addc3bcd79..379252a083 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil; import org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream; import org.thoughtcrime.securesms.database.AttachmentDatabase; import org.thoughtcrime.securesms.database.GroupReceiptDatabase; +import org.thoughtcrime.securesms.database.KeyValueDatabase; import org.thoughtcrime.securesms.database.MmsDatabase; import org.thoughtcrime.securesms.database.MmsSmsColumns; import org.thoughtcrime.securesms.database.OneTimePreKeyDatabase; @@ -33,6 +34,9 @@ import org.thoughtcrime.securesms.database.SessionDatabase; import org.thoughtcrime.securesms.database.SignedPreKeyDatabase; import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.util.SetUtil; import org.thoughtcrime.securesms.util.Stopwatch; @@ -64,7 +68,6 @@ import javax.crypto.spec.SecretKeySpec; public class FullBackupExporter extends FullBackupBase { - @SuppressWarnings("unused") private static final String TAG = FullBackupExporter.class.getSimpleName(); private static final Set BLACKLISTED_TABLES = SetUtil.newHashSet( @@ -165,6 +168,10 @@ public class FullBackupExporter extends FullBackupBase { stopwatch.split("prefs"); + count = exportKeyValues(outputStream, SignalStore.getKeysToIncludeInBackup(), count, cancellationSignal); + + stopwatch.split("key_values"); + for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) { throwIfCanceled(cancellationSignal); if (avatar != null) { @@ -351,6 +358,46 @@ public class FullBackupExporter extends FullBackupBase { return result; } + private static int exportKeyValues(@NonNull BackupFrameOutputStream outputStream, + @NonNull List keysToIncludeInBackup, + int count, + BackupCancellationSignal cancellationSignal) throws IOException + { + KeyValueDataSet dataSet = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()) + .getDataSet(); + + for (String key : keysToIncludeInBackup) { + throwIfCanceled(cancellationSignal); + if (!dataSet.containsKey(key)) { + continue; + } + BackupProtos.KeyValue.Builder builder = BackupProtos.KeyValue.newBuilder() + .setKey(key); + + Class type = dataSet.getType(key); + if (type == byte[].class) { + builder.setBlobValue(ByteString.copyFrom(dataSet.getBlob(key, null))); + } else if (type == Boolean.class) { + builder.setBooleanValue(dataSet.getBoolean(key, false)); + } else if (type == Float.class) { + builder.setFloatValue(dataSet.getFloat(key, 0)); + } else if (type == Integer.class) { + builder.setIntegerValue(dataSet.getInteger(key, 0)); + } else if (type == Long.class) { + builder.setLongValue(dataSet.getLong(key, 0)); + } else if (type == String.class) { + builder.setStringValue(dataSet.getString(key, null)); + } else { + throw new AssertionError("Unknown type: " + type); + } + + EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count)); + outputStream.write(builder.build()); + } + + return count; + } + private static boolean isNonExpiringMmsMessage(@NonNull Cursor cursor) { return cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0 && cursor.getInt(cursor.getColumnIndexOrThrow(MmsDatabase.VIEW_ONCE)) <= 0; @@ -422,6 +469,10 @@ public class FullBackupExporter extends FullBackupBase { write(outputStream, BackupProtos.BackupFrame.newBuilder().setPreference(preference).build()); } + public void write(BackupProtos.KeyValue keyValue) throws IOException { + write(outputStream, BackupProtos.BackupFrame.newBuilder().setKeyValue(keyValue).build()); + } + public void write(BackupProtos.SqlStatement statement) throws IOException { write(outputStream, BackupProtos.BackupFrame.newBuilder().setStatement(statement).build()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java index b913e1d690..5b274495ef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.java @@ -26,8 +26,11 @@ import org.thoughtcrime.securesms.backup.BackupProtos.Sticker; import org.thoughtcrime.securesms.crypto.AttachmentSecret; import org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream; import org.thoughtcrime.securesms.database.AttachmentDatabase; +import org.thoughtcrime.securesms.database.KeyValueDatabase; import org.thoughtcrime.securesms.database.SearchDatabase; import org.thoughtcrime.securesms.database.StickerDatabase; +import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.BackupUtil; @@ -45,6 +48,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -78,10 +82,12 @@ public class FullBackupImporter extends FullBackupBase { { int count = 0; + SQLiteDatabase keyValueDatabase = KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()).getSqlCipherDatabase(); try { BackupRecordInputStream inputStream = new BackupRecordInputStream(is, passphrase); db.beginTransaction(); + keyValueDatabase.beginTransaction(); dropAllTables(db); @@ -97,12 +103,15 @@ public class FullBackupImporter extends FullBackupBase { else if (frame.hasAttachment()) processAttachment(context, attachmentSecret, db, frame.getAttachment(), inputStream); else if (frame.hasSticker()) processSticker(context, attachmentSecret, db, frame.getSticker(), inputStream); else if (frame.hasAvatar()) processAvatar(context, db, frame.getAvatar(), inputStream); + else if (frame.hasKeyValue()) processKeyValue(frame.getKeyValue()); else count--; } db.setTransactionSuccessful(); + keyValueDatabase.setTransactionSuccessful(); } finally { db.endTransaction(); + keyValueDatabase.endTransaction(); } EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, count)); @@ -213,6 +222,29 @@ public class FullBackupImporter extends FullBackupBase { } } + private static void processKeyValue(BackupProtos.KeyValue keyValue) { + KeyValueDataSet dataSet = new KeyValueDataSet(); + + if (keyValue.hasBlobValue()) { + dataSet.putBlob(keyValue.getKey(), keyValue.getBlobValue().toByteArray()); + } else if (keyValue.hasBooleanValue()) { + dataSet.putBoolean(keyValue.getKey(), keyValue.getBooleanValue()); + } else if (keyValue.hasFloatValue()) { + dataSet.putFloat(keyValue.getKey(), keyValue.getFloatValue()); + } else if (keyValue.hasIntegerValue()) { + dataSet.putInteger(keyValue.getKey(), keyValue.getIntegerValue()); + } else if (keyValue.hasLongValue()) { + dataSet.putLong(keyValue.getKey(), keyValue.getLongValue()); + } else if (keyValue.hasStringValue()) { + dataSet.putString(keyValue.getKey(), keyValue.getStringValue()); + } else { + Log.i(TAG, "Unknown KeyValue backup value, skipping"); + return; + } + + KeyValueDatabase.getInstance(ApplicationDependencies.getApplication()).writeDataSet(dataSet, Collections.emptyList()); + } + @SuppressLint("ApplySharedPref") private static void processPreference(@NonNull Context context, SharedPreference preference) { SharedPreferences preferences = context.getSharedPreferences(preference.getFile(), 0); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/CertificateValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/CertificateValues.java index c3ec5bbde9..c448e5b8de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/CertificateValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/CertificateValues.java @@ -4,6 +4,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; +import java.util.Collections; +import java.util.List; + public final class CertificateValues extends SignalStoreValues { private static final String UD_CERTIFICATE_UUID_AND_E164 = "certificate.uuidAndE164"; @@ -17,6 +20,11 @@ public final class CertificateValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + @WorkerThread public void setUnidentifiedAccessCertificate(@NonNull CertificateType certificateType, @Nullable byte[] certificate) diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/EmojiValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/EmojiValues.java index ac1cddf077..0ed55b01a1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/EmojiValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/EmojiValues.java @@ -4,6 +4,9 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.components.emoji.EmojiUtil; +import java.util.Collections; +import java.util.List; + public class EmojiValues extends SignalStoreValues { private static final String PREFIX = "emojiPref__"; @@ -17,6 +20,11 @@ public class EmojiValues extends SignalStoreValues { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public void setPreferredVariation(@NonNull String emoji) { String canonical = EmojiUtil.getCanonicalRepresentation(emoji); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java index 71753244e4..7e312c1af7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/InternalValues.java @@ -1,7 +1,12 @@ package org.thoughtcrime.securesms.keyvalue; +import androidx.annotation.NonNull; + import org.thoughtcrime.securesms.util.FeatureFlags; +import java.util.Collections; +import java.util.List; + public final class InternalValues extends SignalStoreValues { public static final String GV2_DO_NOT_CREATE_GV2 = "internal.gv2.do_not_create_gv2"; @@ -21,6 +26,11 @@ public final class InternalValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + /** * Do not attempt to create GV2 groups, i.e. will force creation of GV1 or MMS groups. */ diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java index 8dd450f2c6..a6018237af 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KbsValues.java @@ -11,6 +11,8 @@ import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse import java.io.IOException; import java.security.SecureRandom; +import java.util.Collections; +import java.util.List; public final class KbsValues extends SignalStoreValues { @@ -30,6 +32,11 @@ public final class KbsValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + /** * Deliberately does not clear the {@link #MASTER_KEY}. * diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java index 0f61048c7b..d18ec98b60 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueDataSet.java @@ -106,7 +106,8 @@ public class KeyValueDataSet implements KeyValueReader { } } - boolean containsKey(@NonNull String key) { + @Override + public boolean containsKey(@NonNull String key) { return values.containsKey(key); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueReader.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueReader.java index 95a2c2393b..0195429609 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueReader.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueReader.java @@ -9,4 +9,5 @@ interface KeyValueReader { int getInteger(@NonNull String key, int defaultValue); long getLong(@NonNull String key, long defaultValue); String getString(@NonNull String key, String defaultValue); + boolean containsKey(@NonNull String key); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java index 1706173d08..9750cec159 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/KeyValueStore.java @@ -87,6 +87,13 @@ public final class KeyValueStore implements KeyValueReader { return dataSet.getString(key, defaultValue); } + @AnyThread + @Override + public synchronized boolean containsKey(@NonNull String key) { + initializeIfNecessary(); + return dataSet.containsKey(key); + } + /** * @return A writer that allows writing and removing multiple entries in a single atomic * transaction. diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java index 2c37520e54..bae602f36c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/MiscellaneousValues.java @@ -2,6 +2,9 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; +import java.util.Collections; +import java.util.List; + public final class MiscellaneousValues extends SignalStoreValues { private static final String LAST_PREKEY_REFRESH_TIME = "last_prekey_refresh_time"; @@ -21,6 +24,11 @@ public final class MiscellaneousValues extends SignalStoreValues { putLong(MESSAGE_REQUEST_ENABLE_TIME, 0); } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public long getLastPrekeyRefreshTime() { return getLong(LAST_PREKEY_REFRESH_TIME, 0); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java index 3f04276d24..46fc71338e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/OnboardingValues.java @@ -7,6 +7,9 @@ import androidx.annotation.NonNull; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.util.Util; +import java.util.Collections; +import java.util.List; + public final class OnboardingValues extends SignalStoreValues { private static final String SHOW_NEW_GROUP = "onboarding.new_group"; @@ -24,6 +27,11 @@ public final class OnboardingValues extends SignalStoreValues { putBoolean(SHOW_SMS, true); } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public void clearAll() { setShowNewGroup(false); setShowInviteFriends(false); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues.java index 4d013f7fbe..440fadb857 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PhoneNumberPrivacyValues.java @@ -7,6 +7,7 @@ import org.thoughtcrime.securesms.util.FeatureFlags; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.List; public final class PhoneNumberPrivacyValues extends SignalStoreValues { @@ -29,6 +30,11 @@ public final class PhoneNumberPrivacyValues extends SignalStoreValues { // .apply(); } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Arrays.asList(SHARING_MODE, LISTING_MODE); + } + public @NonNull PhoneNumberSharingMode getPhoneNumberSharingMode() { if (!FeatureFlags.phoneNumberPrivacy()) return PhoneNumberSharingMode.EVERYONE; return PhoneNumberSharingMode.values()[getInteger(SHARING_MODE, PhoneNumberSharingMode.EVERYONE.ordinal())]; diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java index 042dd42ae9..22475ee3c0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/PinValues.java @@ -9,6 +9,9 @@ import org.thoughtcrime.securesms.lock.SignalPinReminders; import org.thoughtcrime.securesms.lock.v2.PinKeyboardType; import org.thoughtcrime.securesms.util.TextSecurePreferences; +import java.util.Collections; +import java.util.List; + /** * Specifically handles just the UI/UX state around PINs. For actual keys, see {@link KbsValues}. */ @@ -30,6 +33,11 @@ public final class PinValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public void onEntrySuccess(@NonNull String pin) { long nextInterval = SignalPinReminders.getNextInterval(getCurrentInterval()); Log.i(TAG, "onEntrySuccess() nextInterval: " + nextInterval); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ProxyValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ProxyValues.java index 160e31aec0..a1f3067401 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ProxyValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/ProxyValues.java @@ -6,6 +6,9 @@ import androidx.annotation.Nullable; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.internal.configuration.SignalProxy; +import java.util.Arrays; +import java.util.List; + public final class ProxyValues extends SignalStoreValues { private static final String KEY_PROXY_ENABLED = "proxy.enabled"; @@ -20,6 +23,11 @@ public final class ProxyValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Arrays.asList(KEY_PROXY_ENABLED, KEY_HOST, KEY_PORT); + } + public void enableProxy(@NonNull SignalProxy proxy) { if (Util.isEmpty(proxy.getHost())) { throw new IllegalArgumentException("Empty host!"); diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.java index 6028046235..4d8bba0159 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RegistrationValues.java @@ -3,6 +3,9 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.CheckResult; import androidx.annotation.NonNull; +import java.util.Collections; +import java.util.List; + public final class RegistrationValues extends SignalStoreValues { private static final String REGISTRATION_COMPLETE = "registration.complete"; @@ -19,6 +22,11 @@ public final class RegistrationValues extends SignalStoreValues { .commit(); } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public synchronized void clearRegistrationComplete() { onFirstEverAppLaunch(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RemoteConfigValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RemoteConfigValues.java index a6a8b6a27f..f9fdc5bc1f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RemoteConfigValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/RemoteConfigValues.java @@ -4,6 +4,9 @@ import androidx.annotation.NonNull; import org.signal.core.util.logging.Log; +import java.util.Collections; +import java.util.List; + public final class RemoteConfigValues extends SignalStoreValues { private static final String TAG = Log.tag(RemoteConfigValues.class); @@ -20,6 +23,11 @@ public final class RemoteConfigValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public String getCurrentConfig() { return getString(CURRENT_CONFIG, null); } 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 d3664df33c..71040dfbfc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -8,6 +8,9 @@ import androidx.annotation.Nullable; import org.thoughtcrime.securesms.webrtc.CallBandwidthMode; +import java.util.Arrays; +import java.util.List; + public final class SettingsValues extends SignalStoreValues { public static final String LINK_PREVIEWS = "settings.link_previews"; @@ -29,9 +32,21 @@ public final class SettingsValues extends SignalStoreValues { @Override void onFirstEverAppLaunch() { - getStore().beginWrite() - .putBoolean(LINK_PREVIEWS, true) - .apply(); + if (!getStore().containsKey(LINK_PREVIEWS)) { + getStore().beginWrite() + .putBoolean(LINK_PREVIEWS, true) + .apply(); + } + } + + @Override + @NonNull List getKeysToIncludeInBackup() { + return Arrays.asList(LINK_PREVIEWS, + KEEP_MESSAGES_DURATION, + PREFER_SYSTEM_CONTACT_PHOTOS, + CALL_BANDWIDTH_MODE, + THREAD_TRIM_LENGTH, + THREAD_TRIM_ENABLED); } public boolean isLinkPreviewsEnabled() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java index 8db6b32255..8239174ad4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStore.java @@ -7,6 +7,9 @@ import androidx.preference.PreferenceDataStore; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.util.SignalUncaughtExceptionHandler; +import java.util.ArrayList; +import java.util.List; + /** * Simple, encrypted key-value store. */ @@ -62,6 +65,7 @@ public final class SignalStore { tooltips().onFirstEverAppLaunch(); misc().onFirstEverAppLaunch(); internalValues().onFirstEverAppLaunch(); + emojiValues().onFirstEverAppLaunch(); settings().onFirstEverAppLaunch(); certificateValues().onFirstEverAppLaunch(); phoneNumberPrivacy().onFirstEverAppLaunch(); @@ -70,6 +74,27 @@ public final class SignalStore { proxy().onFirstEverAppLaunch(); } + public static List getKeysToIncludeInBackup() { + List keys = new ArrayList<>(); + keys.addAll(kbsValues().getKeysToIncludeInBackup()); + keys.addAll(registrationValues().getKeysToIncludeInBackup()); + keys.addAll(pinValues().getKeysToIncludeInBackup()); + keys.addAll(remoteConfigValues().getKeysToIncludeInBackup()); + keys.addAll(storageServiceValues().getKeysToIncludeInBackup()); + keys.addAll(uiHints().getKeysToIncludeInBackup()); + keys.addAll(tooltips().getKeysToIncludeInBackup()); + keys.addAll(misc().getKeysToIncludeInBackup()); + keys.addAll(internalValues().getKeysToIncludeInBackup()); + keys.addAll(emojiValues().getKeysToIncludeInBackup()); + keys.addAll(settings().getKeysToIncludeInBackup()); + keys.addAll(certificateValues().getKeysToIncludeInBackup()); + keys.addAll(phoneNumberPrivacy().getKeysToIncludeInBackup()); + keys.addAll(onboarding().getKeysToIncludeInBackup()); + keys.addAll(wallpaper().getKeysToIncludeInBackup()); + keys.addAll(proxy().getKeysToIncludeInBackup()); + return keys; + } + /** * Forces the store to re-fetch all of it's data from the database. * Should only be used for testing! diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValues.java index 02557064ce..f7f2e1be1e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SignalStoreValues.java @@ -2,6 +2,8 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; +import java.util.List; + abstract class SignalStoreValues { private final KeyValueStore store; @@ -16,6 +18,8 @@ abstract class SignalStoreValues { abstract void onFirstEverAppLaunch(); + abstract @NonNull List getKeysToIncludeInBackup(); + String getString(String key, String defaultValue) { return store.getString(key, defaultValue); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java index b67937d2f2..0848461e31 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/StorageServiceValues.java @@ -4,6 +4,9 @@ import androidx.annotation.NonNull; import org.whispersystems.signalservice.api.storage.StorageKey; +import java.util.Collections; +import java.util.List; + public class StorageServiceValues extends SignalStoreValues { private static final String LAST_SYNC_TIME = "storage.last_sync_time"; @@ -17,6 +20,11 @@ public class StorageServiceValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public synchronized StorageKey getOrCreateStorageKey() { return SignalStore.kbsValues().getOrCreateMasterKey().deriveStorageServiceKey(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java index 33a2fc6b4c..6db83250f5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/TooltipValues.java @@ -2,6 +2,9 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; +import java.util.Collections; +import java.util.List; + public class TooltipValues extends SignalStoreValues { private static final int GROUP_CALLING_MAX_TOOLTIP_DISPLAY_COUNT = 3; @@ -19,6 +22,11 @@ public class TooltipValues extends SignalStoreValues { public void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public boolean hasSeenBlurHudIconTooltip() { return getBoolean(BLUR_HUD_ICON, false); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java index f476934516..7976b84af6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/UiHints.java @@ -2,6 +2,9 @@ package org.thoughtcrime.securesms.keyvalue; import androidx.annotation.NonNull; +import java.util.Collections; +import java.util.List; + public class UiHints extends SignalStoreValues { private static final String HAS_SEEN_GROUP_SETTINGS_MENU_TOAST = "uihints.has_seen_group_settings_menu_toast"; @@ -16,6 +19,11 @@ public class UiHints extends SignalStoreValues { markHasSeenGroupSettingsMenuToast(); } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public void markHasSeenGroupSettingsMenuToast() { putBoolean(HAS_SEEN_GROUP_SETTINGS_MENU_TOAST, true); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java index 20764f7fe9..2fc79a1509 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/WallpaperValues.java @@ -15,6 +15,9 @@ import org.thoughtcrime.securesms.wallpaper.ChatWallpaper; import org.thoughtcrime.securesms.wallpaper.ChatWallpaperFactory; import org.thoughtcrime.securesms.wallpaper.WallpaperStorage; +import java.util.Collections; +import java.util.List; + public final class WallpaperValues extends SignalStoreValues { private static final String TAG = Log.tag(WallpaperValues.class); @@ -29,6 +32,11 @@ public final class WallpaperValues extends SignalStoreValues { void onFirstEverAppLaunch() { } + @Override + @NonNull List getKeysToIncludeInBackup() { + return Collections.emptyList(); + } + public void setWallpaper(@NonNull Context context, @Nullable ChatWallpaper wallpaper) { Wallpaper currentWallpaper = getCurrentWallpaper(); Uri currentUri = null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java index 386bf833b4..5ffc16af10 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -20,6 +20,7 @@ import org.thoughtcrime.securesms.backup.BackupProtos; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; import org.thoughtcrime.securesms.keyvalue.SettingsValues; import org.thoughtcrime.securesms.lock.RegistrationLockReminders; +import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.preferences.widgets.NotificationPrivacyPreference; import org.whispersystems.libsignal.util.Medium; import org.whispersystems.signalservice.api.util.UuidUtil; @@ -280,6 +281,12 @@ public class TextSecurePreferences { return backupProtos; } + public static void onPostBackupRestore(@NonNull Context context) { + if (NotificationChannels.supported() && PreferenceManager.getDefaultSharedPreferences(context).contains(VIBRATE_PREF)) { + NotificationChannels.updateMessageVibrate(context, isNotificationVibrateEnabled(context)); + } + } + public static boolean isScreenLockEnabled(@NonNull Context context) { return getBooleanPreference(context, SCREEN_LOCK, false); } diff --git a/app/src/main/proto/Backups.proto b/app/src/main/proto/Backups.proto index 3b44d5d1fd..41acb55d52 100644 --- a/app/src/main/proto/Backups.proto +++ b/app/src/main/proto/Backups.proto @@ -59,6 +59,16 @@ message Header { optional bytes salt = 2; } +message KeyValue { + optional string key = 1; + optional bytes blobValue = 2; + optional bool booleanValue = 3; + optional float floatValue = 4; + optional int32 integerValue = 5; + optional int64 longValue = 6; + optional string stringValue = 7; +} + message BackupFrame { optional Header header = 1; optional SqlStatement statement = 2; @@ -68,4 +78,5 @@ message BackupFrame { optional bool end = 6; optional Avatar avatar = 7; optional Sticker sticker = 8; + optional KeyValue keyValue = 9; } \ No newline at end of file