diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 59f196b372..84973bc56c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -54,6 +54,7 @@ import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob; import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob; import org.thoughtcrime.securesms.jobs.RefreshPreKeysJob; import org.thoughtcrime.securesms.jobs.RetrieveProfileJob; +import org.thoughtcrime.securesms.jobs.SubscriptionKeepAliveJob; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger; import org.thoughtcrime.securesms.logging.PersistentLogger; @@ -61,7 +62,6 @@ import org.thoughtcrime.securesms.messageprocessingalarm.MessageProcessReceiver; import org.thoughtcrime.securesms.migrations.ApplicationMigrations; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.providers.BlobProvider; -import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.ratelimit.RateLimitUtil; import org.thoughtcrime.securesms.registration.RegistrationUtil; import org.thoughtcrime.securesms.ringrtc.RingRtcLogger; @@ -70,7 +70,6 @@ import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.LocalBackupListener; import org.thoughtcrime.securesms.service.RotateSenderCertificateListener; import org.thoughtcrime.securesms.service.RotateSignedPreKeyListener; -import org.thoughtcrime.securesms.service.SubscriberIdKeepAliveListener; import org.thoughtcrime.securesms.service.UpdateApkRefreshListener; import org.thoughtcrime.securesms.storage.StorageSyncHelper; import org.thoughtcrime.securesms.util.AppForegroundObserver; @@ -193,6 +192,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr ApplicationDependencies.getFrameRateTracker().start(); ApplicationDependencies.getMegaphoneRepository().onAppForegrounded(); ApplicationDependencies.getDeadlockDetector().start(); + SubscriptionKeepAliveJob.launchSubscriberIdKeepAliveJobIfNecessary(); SignalExecutors.BOUNDED.execute(() -> { FeatureFlags.refreshIfNecessary(); @@ -334,7 +334,6 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr LocalBackupListener.schedule(this); RotateSenderCertificateListener.schedule(this); MessageProcessReceiver.startOrUpdateAlarm(this); - SubscriberIdKeepAliveListener.schedule(this); if (BuildConfig.PLAY_STORE_DISABLED) { UpdateApkRefreshListener.schedule(this); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java index d76e93f4bb..9c5a05c1dc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/SubscriptionKeepAliveJob.java @@ -29,11 +29,22 @@ public class SubscriptionKeepAliveJob extends BaseJob { private static final String TAG = Log.tag(SubscriptionKeepAliveJob.class); private static final long JOB_TIMEOUT = TimeUnit.DAYS.toMillis(3); - public SubscriptionKeepAliveJob() { + public static void launchSubscriberIdKeepAliveJobIfNecessary() { + long nextLaunchTime = SignalStore.donationsValues().getLastKeepAliveLaunchTime() + TimeUnit.DAYS.toMillis(3); + long now = System.currentTimeMillis(); + + if (nextLaunchTime <= now) { + ApplicationDependencies.getJobManager().add(new SubscriptionKeepAliveJob()); + SignalStore.donationsValues().setLastKeepAliveLaunchTime(now); + } + } + + private SubscriptionKeepAliveJob() { this(new Parameters.Builder() .setQueue(KEY) .addConstraint(NetworkConstraint.KEY) .setMaxInstancesForQueue(1) + .setMaxAttempts(Parameters.UNLIMITED) .setLifespan(JOB_TIMEOUT) .build()); } @@ -68,22 +79,15 @@ public class SubscriptionKeepAliveJob extends BaseJob { .putSubscription(subscriber.getSubscriberId()) .blockingGet(); - if (!response.getResult().isPresent()) { - if (response.getStatus() == 403) { - Log.w(TAG, "Response code 403, possibly corrupted subscription id."); - // TODO [alex] - Probably need some UX around this, or some kind of protocol. - } - - throw new IOException("Failed to ping subscription service."); - } + verifyResponse(response); + Log.i(TAG, "Successful call to PUT subscription ID"); ServiceResponse activeSubscriptionResponse = ApplicationDependencies.getDonationsService() .getSubscription(subscriber.getSubscriberId()) .blockingGet(); - if (!response.getResult().isPresent()) { - throw new IOException("Failed to perform active subscription check"); - } + verifyResponse(activeSubscriptionResponse); + Log.i(TAG, "Successful call to GET active subscription"); ActiveSubscription activeSubscription = activeSubscriptionResponse.getResult().get(); if (activeSubscription.getActiveSubscription() == null || !activeSubscription.getActiveSubscription().isActive()) { @@ -97,9 +101,29 @@ public class SubscriptionKeepAliveJob extends BaseJob { } } + private void verifyResponse(@NonNull ServiceResponse response) throws Exception { + if (response.getExecutionError().isPresent()) { + Log.w(TAG, "Failed with an execution error. Scheduling retry.", response.getExecutionError().get(), true); + throw new RetryableException(); + } else if (response.getApplicationError().isPresent()) { + switch (response.getStatus()) { + case 403: + case 404: + Log.w(TAG, "Invalid or malformed subscriber id. Status: " + response.getStatus(), response.getApplicationError().get(), true); + throw new IOException(); + default: + Log.w(TAG, "An unknown server error occurred: " + response.getStatus(), response.getApplicationError().get(), true); + throw new RetryableException(); + } + } + } + @Override protected boolean onShouldRetry(@NonNull Exception e) { - return true; + return e instanceof RetryableException; + } + + private static class RetryableException extends Exception { } public static class Factory implements Job.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/SubscriberIdKeepAliveListener.java b/app/src/main/java/org/thoughtcrime/securesms/service/SubscriberIdKeepAliveListener.java deleted file mode 100644 index 365032ef06..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/service/SubscriberIdKeepAliveListener.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.thoughtcrime.securesms.service; - -import android.content.Context; -import android.content.Intent; - -import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; -import org.thoughtcrime.securesms.jobs.SubscriptionKeepAliveJob; -import org.thoughtcrime.securesms.keyvalue.SignalStore; - -import java.util.concurrent.TimeUnit; - -/** - * Manages the scheduling of jobs for keeping a subscription id alive. - */ -public class SubscriberIdKeepAliveListener extends PersistentAlarmManagerListener { - - private static final long INTERVAL = TimeUnit.DAYS.toMillis(3); - - @Override - protected long getNextScheduledExecutionTime(Context context) { - return SignalStore.donationsValues().getLastKeepAliveLaunchTime() + INTERVAL; - } - - @Override - protected long onAlarm(Context context, long scheduledTime) { - if (SignalStore.donationsValues().getSubscriber() != null) { - ApplicationDependencies.getJobManager().add(new SubscriptionKeepAliveJob()); - } - - long now = System.currentTimeMillis(); - SignalStore.donationsValues().setLastKeepAliveLaunchTime(now); - - return now + INTERVAL; - } - - public static void schedule(Context context) { - new SubscriberIdKeepAliveListener().onReceive(context, new Intent()); - } -}