diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt index 9728b5ccf0..06898bf6e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/subscription/subscribe/SubscribeViewModel.kt @@ -21,6 +21,7 @@ import org.thoughtcrime.securesms.components.settings.app.subscription.DonationE import org.thoughtcrime.securesms.components.settings.app.subscription.DonationExceptions import org.thoughtcrime.securesms.components.settings.app.subscription.DonationPaymentRepository import org.thoughtcrime.securesms.components.settings.app.subscription.SubscriptionsRepository +import org.thoughtcrime.securesms.jobs.MultiDeviceSubscriptionSyncRequestJob import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.subscription.LevelUpdate import org.thoughtcrime.securesms.subscription.Subscriber @@ -169,6 +170,7 @@ class SubscribeViewModel( SignalStore.donationsValues().setLastEndOfPeriod(0L) SignalStore.donationsValues().clearLevelOperations() SignalStore.donationsValues().shouldCancelSubscriptionBeforeNextSubscribeAttempt = false + MultiDeviceSubscriptionSyncRequestJob.enqueue() } } else { Completable.complete() @@ -185,6 +187,7 @@ class SubscribeViewModel( SignalStore.donationsValues().clearLevelOperations() SignalStore.donationsValues().markUserManuallyCancelled() refreshActiveSubscription() + MultiDeviceSubscriptionSyncRequestJob.enqueue() store.update { it.copy(stage = SubscribeState.Stage.READY) } }, onError = { throwable -> 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 4d1582902c..d93f2e04ba 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -111,6 +111,7 @@ public final class JobManagerFactories { put(MultiDeviceStickerPackOperationJob.KEY, new MultiDeviceStickerPackOperationJob.Factory()); put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory()); put(MultiDeviceStorageSyncRequestJob.KEY, new MultiDeviceStorageSyncRequestJob.Factory()); + put(MultiDeviceSubscriptionSyncRequestJob.KEY, new MultiDeviceSubscriptionSyncRequestJob.Factory()); put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory()); put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory()); put(MultiDeviceViewedUpdateJob.KEY, new MultiDeviceViewedUpdateJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceSubscriptionSyncRequestJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceSubscriptionSyncRequestJob.kt new file mode 100644 index 0000000000..c081dcba38 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/MultiDeviceSubscriptionSyncRequestJob.kt @@ -0,0 +1,76 @@ +package org.thoughtcrime.securesms.jobs + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil +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.net.NotPushRegisteredException +import org.thoughtcrime.securesms.recipients.Recipient +import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage +import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException +import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException + +/** + * Sends a sync message to linked devices to notify them to refresh subscription status. + */ +class MultiDeviceSubscriptionSyncRequestJob private constructor(parameters: Parameters) : BaseJob(parameters) { + + companion object { + const val KEY = "MultiDeviceSubscriptionSyncRequestJob" + + private val TAG = Log.tag(MultiDeviceSubscriptionSyncRequestJob::class.java) + + @JvmStatic + fun enqueue() { + val job = MultiDeviceSubscriptionSyncRequestJob( + Parameters.Builder() + .setQueue("MultiDeviceSubscriptionSyncRequestJob") + .setMaxInstancesForFactory(2) + .addConstraint(NetworkConstraint.KEY) + .setMaxAttempts(10) + .build() + ) + + ApplicationDependencies.getJobManager().add(job) + } + } + + override fun serialize(): Data = Data.EMPTY + + override fun getFactoryKey(): String = KEY + + override fun onFailure() { + Log.w(TAG, "Did not succeed!") + } + + override fun onRun() { + if (!Recipient.self().isRegistered) { + throw NotPushRegisteredException() + } + + if (!TextSecurePreferences.isMultiDevice(context)) { + Log.i(TAG, "Not multi device, aborting...") + return + } + + val messageSender = ApplicationDependencies.getSignalServiceMessageSender() + + messageSender.sendSyncMessage( + SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.SUBSCRIPTION_STATUS), + UnidentifiedAccessUtil.getAccessForSync(context) + ) + } + + override fun onShouldRetry(e: Exception): Boolean { + return e is PushNetworkException && e !is ServerRejectedException + } + + class Factory : Job.Factory { + override fun create(parameters: Parameters, data: Data): MultiDeviceSubscriptionSyncRequestJob { + return MultiDeviceSubscriptionSyncRequestJob(parameters) + } + } +} 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 620922ba5d..8a4238eb59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -218,6 +218,7 @@ public class RefreshOwnProfileJob extends BaseJob { if (!SignalStore.donationsValues().isUserManuallyCancelled()) { Log.d(TAG, "Detected an unexpected subscription expiry.", true); + MultiDeviceSubscriptionSyncRequestJob.enqueue(); SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true); } } else if (!remoteHasBoostBadges && localHasBoostBadges) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java index b2715cb763..b7c6353ad7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionReceiptRequestResponseJob.java @@ -104,6 +104,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { public void onFailure() { DonorBadgeNotifications.RedemptionFailed.INSTANCE.show(context); SignalStore.donationsValues().markSubscriptionRedemptionFailed(); + MultiDeviceSubscriptionSyncRequestJob.enqueue(); } @Override @@ -128,6 +129,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { } else { Log.i(TAG, "Recording end of period from active subscription.", true); SignalStore.donationsValues().setLastEndOfPeriod(subscription.getEndOfCurrentPeriod()); + MultiDeviceSubscriptionSyncRequestJob.enqueue(); } if (requestContext == null) { @@ -240,6 +242,7 @@ public class SubscriptionReceiptRequestResponseJob extends BaseJob { private void onPaymentFailure() { SignalStore.donationsValues().setShouldCancelSubscriptionBeforeNextSubscribeAttempt(true); setOutputData(new Data.Builder().putBoolean(DonationReceiptRedemptionJob.INPUT_PAYMENT_FAILURE, true).build()); + MultiDeviceSubscriptionSyncRequestJob.enqueue(); } /** diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java index aa6593c8f9..e3bc86fd11 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageContentProcessor.java @@ -967,6 +967,9 @@ public final class MessageContentProcessor { case STORAGE_MANIFEST: StorageSyncHelper.scheduleSyncForDataChange(); break; + case SUBSCRIPTION_STATUS: + warn(TAG, "Dropping subscription status fetch message."); + break; default: warn(TAG, "Received a fetch message for an unknown type."); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 09753ef621..32b8b30984 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -1214,6 +1214,9 @@ public class SignalServiceMessageSender { case STORAGE_MANIFEST: fetchMessage.setType(SyncMessage.FetchLatest.Type.STORAGE_MANIFEST); break; + case SUBSCRIPTION_STATUS: + fetchMessage.setType(SyncMessage.FetchLatest.Type.SUBSCRIPTION_STATUS); + break; default: Log.w(TAG, "Unknown fetch type!"); break; diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java index 4bb633b1b4..9b00072a39 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/SignalServiceContent.java @@ -741,8 +741,9 @@ public final class SignalServiceContent { if (content.hasFetchLatest() && content.getFetchLatest().hasType()) { switch (content.getFetchLatest().getType()) { - case LOCAL_PROFILE: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE); - case STORAGE_MANIFEST: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.STORAGE_MANIFEST); + case LOCAL_PROFILE: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE); + case STORAGE_MANIFEST: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.STORAGE_MANIFEST); + case SUBSCRIPTION_STATUS: return SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.SUBSCRIPTION_STATUS); } } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java index 1c96e18074..b6759c59ea 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/multidevice/SignalServiceSyncMessage.java @@ -434,6 +434,7 @@ public class SignalServiceSyncMessage { public enum FetchType { LOCAL_PROFILE, - STORAGE_MANIFEST + STORAGE_MANIFEST, + SUBSCRIPTION_STATUS } } diff --git a/libsignal/service/src/main/proto/SignalService.proto b/libsignal/service/src/main/proto/SignalService.proto index 715c752a83..a02d54a229 100644 --- a/libsignal/service/src/main/proto/SignalService.proto +++ b/libsignal/service/src/main/proto/SignalService.proto @@ -447,9 +447,10 @@ message SyncMessage { message FetchLatest { enum Type { - UNKNOWN = 0; - LOCAL_PROFILE = 1; - STORAGE_MANIFEST = 2; + UNKNOWN = 0; + LOCAL_PROFILE = 1; + STORAGE_MANIFEST = 2; + SUBSCRIPTION_STATUS = 3; } optional Type type = 1;