diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsFragment.kt index f9ae41e6bd..d6fb7adcef 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsFragment.kt @@ -103,13 +103,13 @@ class ExpireTimerSettingsFragment : DSLSettingsFragment( val values: Array = resources.getIntArray(R.array.ExpireTimerSettingsFragment__values).toTypedArray() var hasCustomValue = true - labels.zip(values).forEach { (label, value) -> + labels.zip(values).forEach { (label, seconds) -> radioPref( title = DSLSettingsText.from(label), - isChecked = state.currentTimer == value, - onClick = { viewModel.select(value) } + isChecked = state.currentTimer == seconds, + onClick = { viewModel.select(seconds) } ) - hasCustomValue = hasCustomValue && state.currentTimer != value + hasCustomValue = hasCustomValue && state.currentTimer != seconds } radioPref( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt index a4b017143d..526aeef933 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsRepository.kt @@ -8,10 +8,12 @@ import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.groups.GroupChangeException import org.thoughtcrime.securesms.groups.GroupManager +import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.recipients.RecipientId import org.thoughtcrime.securesms.sms.MessageSender +import org.thoughtcrime.securesms.storage.StorageSyncHelper import java.io.IOException private val TAG: String = Log.tag(ExpireTimerSettingsRepository::class.java) @@ -44,6 +46,15 @@ class ExpireTimerSettingsRepository(val context: Context) { } } + fun setUniversalExpireTimerSeconds(newExpirationTime: Int, onDone: () -> Unit) { + SignalExecutors.BOUNDED.execute { + SignalStore.settings().universalExpireTimer = newExpirationTime + SignalDatabase.recipients.markNeedsSync(Recipient.self().id) + StorageSyncHelper.scheduleSyncForDataChange() + onDone.invoke() + } + } + @WorkerThread private fun getThreadId(recipientId: RecipientId): Long { val threadDatabase: ThreadDatabase = SignalDatabase.threads diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsViewModel.kt index bd5ba4b549..3d4a706182 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/privacy/expire/ExpireTimerSettingsViewModel.kt @@ -45,8 +45,9 @@ class ExpireTimerSettingsViewModel(val config: Config, private val repository: E } else if (config.forResultMode) { store.update { it.copy(saveState = ProcessState.Success(userSetTimer)) } } else { - SignalStore.settings().universalExpireTimer = userSetTimer - store.update { it.copy(saveState = ProcessState.Success(userSetTimer)) } + repository.setUniversalExpireTimerSeconds(userSetTimer) { + store.update { it.copy(saveState = ProcessState.Success(userSetTimer)) } + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java index 861a84e8ab..eecf16f1ca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java @@ -4,6 +4,7 @@ import android.content.Context; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.IdentityKeyPair; diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java index 16efb4e816..dc26a9293c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceConfigurationUpdateJob.java @@ -9,6 +9,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.NotPushRegisteredException; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -94,6 +95,11 @@ public class MultiDeviceConfigurationUpdateJob extends BaseJob { return; } + if (SignalStore.account().isLinkedDevice()) { + Log.i(TAG, "Not primary device, aborting..."); + return; + } + SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); messageSender.sendSyncMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled), Optional.of(unidentifiedDeliveryIndicatorsEnabled), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt index bd035fdb3b..be6b3b86d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactSyncJob.kt @@ -50,7 +50,7 @@ class MultiDeviceContactSyncJob(parameters: Parameters, private val attachmentPo } if (SignalStore.account().isPrimaryDevice) { - Log.i(TAG, "Sync jobs are for linked devices only") + Log.i(TAG, "Not linked device, aborting...") return } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java index 69b925f390..06acc0e104 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java @@ -126,6 +126,11 @@ public class MultiDeviceContactUpdateJob extends BaseJob { return; } + if (SignalStore.account().isLinkedDevice()) { + Log.i(TAG, "Not primary device, aborting..."); + return; + } + if (recipientId == null) generateFullContactUpdate(); else generateSingleContactUpdate(recipientId); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java index fb4150b21a..cf6ae12288 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceGroupUpdateJob.java @@ -14,6 +14,7 @@ import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.net.NotPushRegisteredException; import org.thoughtcrime.securesms.profiles.AvatarHelper; import org.thoughtcrime.securesms.providers.BlobProvider; @@ -82,6 +83,11 @@ public class MultiDeviceGroupUpdateJob extends BaseJob { return; } + if (SignalStore.account().isLinkedDevice()) { + Log.i(TAG, "Not primary device, aborting..."); + return; + } + ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe(); InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0]); Uri uri = BlobProvider.getInstance() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java index 51520908fb..6cd66e3f51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceKeysUpdateJob.java @@ -65,6 +65,11 @@ public class MultiDeviceKeysUpdateJob extends BaseJob { return; } + if (SignalStore.account().isLinkedDevice()) { + Log.i(TAG, "Not primary device, aborting..."); + return; + } + SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender(); StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 8a4238eb59..df467997de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -99,7 +99,7 @@ public class RefreshOwnProfileJob extends BaseJob { return; } - if (!SignalStore.registrationValues().hasUploadedProfile()) { + if (!SignalStore.registrationValues().hasUploadedProfile() && SignalStore.account().isPrimaryDevice()) { Log.i(TAG, "Registered but haven't uploaded profile yet."); return; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java index a4e7fd3573..6e19d1e0fe 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageForcePushJob.java @@ -71,6 +71,11 @@ public class StorageForcePushJob extends BaseJob { @Override protected void onRun() throws IOException, RetryLaterException { + if (SignalStore.account().isLinkedDevice()) { + Log.i(TAG, "Only the primary device can force push"); + return; + } + StorageKey storageServiceKey = SignalStore.storageService().getOrCreateStorageKey(); SignalServiceAccountManager accountManager = ApplicationDependencies.getSignalServiceAccountManager(); RecipientDatabase recipientDatabase = SignalDatabase.recipients(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java index 61f83bd114..22f477e2d9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/StorageSyncJob.java @@ -9,6 +9,7 @@ import com.annimon.stream.Stream; import net.zetetic.database.sqlcipher.SQLiteDatabase; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.database.RecipientDatabase; import org.thoughtcrime.securesms.database.model.RecipientRecord; import org.thoughtcrime.securesms.database.SignalDatabase; @@ -37,6 +38,9 @@ import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.SignalServiceAccountManager; +import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage; +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.storage.SignalAccountRecord; import org.whispersystems.signalservice.api.storage.SignalContactRecord; @@ -47,6 +51,7 @@ import org.whispersystems.signalservice.api.storage.SignalStorageManifest; import org.whispersystems.signalservice.api.storage.SignalStorageRecord; import org.whispersystems.signalservice.api.storage.StorageId; import org.whispersystems.signalservice.api.storage.StorageKey; +import org.whispersystems.signalservice.internal.push.SignalServiceProtos; import org.whispersystems.signalservice.internal.storage.protos.ManifestRecord; import java.io.IOException; @@ -160,7 +165,7 @@ public class StorageSyncJob extends BaseJob { } @Override - protected void onRun() throws IOException, RetryLaterException { + protected void onRun() throws IOException, RetryLaterException, UntrustedIdentityException { if (!SignalStore.kbsValues().hasPin() && !SignalStore.kbsValues().hasOptedOut()) { Log.i(TAG, "Doesn't have a PIN. Skipping."); return; @@ -185,12 +190,18 @@ public class StorageSyncJob extends BaseJob { SignalStore.storageService().onSyncCompleted(); } catch (InvalidKeyException e) { - Log.w(TAG, "Failed to decrypt remote storage! Force-pushing and syncing the storage key to linked devices.", e); + if (SignalStore.account().isPrimaryDevice()) { + Log.w(TAG, "Failed to decrypt remote storage! Force-pushing and syncing the storage key to linked devices.", e); - ApplicationDependencies.getJobManager().startChain(new MultiDeviceKeysUpdateJob()) - .then(new StorageForcePushJob()) - .then(new MultiDeviceStorageSyncRequestJob()) - .enqueue(); + ApplicationDependencies.getJobManager().startChain(new MultiDeviceKeysUpdateJob()) + .then(new StorageForcePushJob()) + .then(new MultiDeviceStorageSyncRequestJob()) + .enqueue(); + } else { + Log.w(TAG, "Failed to decrypt remote storage! Requesting new keys from primary.", e); + SignalStore.storageService().clearStorageKeyFromPrimary(); + ApplicationDependencies.getSignalServiceMessageSender().sendSyncMessage(SignalServiceSyncMessage.forRequest(RequestMessage.forType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)), UnidentifiedAccessUtil.getAccessForSync(context)); + } } } @@ -233,7 +244,7 @@ public class StorageSyncJob extends BaseJob { List localStorageIdsBeforeMerge = getAllLocalStorageIds(context, self); IdDifferenceResult idDifference = StorageSyncHelper.findIdDifference(remoteManifest.getStorageIds(), localStorageIdsBeforeMerge); - if (idDifference.hasTypeMismatches()) { + if (idDifference.hasTypeMismatches() && SignalStore.account().isPrimaryDevice()) { Log.w(TAG, "[Remote Sync] Found type mismatches in the ID sets! Scheduling a force push after this sync completes."); needsForcePush = true; } @@ -361,7 +372,7 @@ public class StorageSyncJob extends BaseJob { Log.i(TAG, "No remote writes needed. Still at version: " + remoteManifest.getVersion()); } - if (needsForcePush) { + if (needsForcePush && SignalStore.account().isPrimaryDevice()) { Log.w(TAG, "Scheduling a force push."); ApplicationDependencies.getJobManager().add(new StorageForcePushJob()); } 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 2e1bc8941f..e39bdf4cce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java +++ b/app/src/main/java/org/thoughtcrime/securesms/keyvalue/SettingsValues.java @@ -10,6 +10,7 @@ import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import org.signal.core.util.concurrent.SignalExecutors; +import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.mms.SentMediaQuality; @@ -18,7 +19,6 @@ import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.util.SingleLiveEvent; import org.thoughtcrime.securesms.util.TextSecurePreferences; -import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.webrtc.CallBandwidthMode; import java.util.Arrays; @@ -377,10 +377,6 @@ public final class SettingsValues extends SignalStoreValues { public void setUniversalExpireTimer(int seconds) { putInteger(UNIVERSAL_EXPIRE_TIMER, seconds); - SignalExecutors.BOUNDED.execute(() -> { - SignalDatabase.recipients().markNeedsSync(Recipient.self().getId()); - StorageSyncHelper.scheduleSyncForDataChange(); - }); } public int getUniversalExpireTimer() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java index e01f248b6b..9e49e69319 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/AccountRecordProcessor.java @@ -6,6 +6,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.signal.core.util.logging.Log; +import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.subscription.Subscriber; import org.whispersystems.libsignal.util.guava.Optional; @@ -105,8 +106,8 @@ public class AccountRecordProcessor extends DefaultStorageRecordProcessor defaultReactions = remote.getDefaultReactions().size() > 0 ? remote.getDefaultReactions() : local.getDefaultReactions(); boolean displayBadgesOnProfile = remote.isDisplayBadgesOnProfile(); boolean subscriptionManuallyCancelled = remote.isSubscriptionManuallyCancelled(); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceDataStore.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceDataStore.java index 55983b667a..0730b929ba 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceDataStore.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceDataStore.java @@ -10,7 +10,7 @@ import java.io.Closeable; */ public interface SignalServiceDataStore extends SignalProtocolStore, SignalServiceSessionStore, SignalServiceSenderKeyStore { /** - * @return True if the active account has linked devices, otherwise false. + * @return True if the active account has or is a linked device, otherwise false. */ boolean isMultiDevice();