diff --git a/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java index b7f41607b0..af6ba683c3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java @@ -59,6 +59,7 @@ import java.io.IOException; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -121,7 +122,7 @@ public class NewConversationActivity extends ContactSelectionActivity if (!resolved.isRegistered() || !resolved.hasServiceId()) { Log.i(TAG, "[onContactSelected] Not registered or no UUID. Doing a directory refresh."); try { - ContactDiscovery.refresh(this, resolved, false); + ContactDiscovery.refresh(this, resolved, false, TimeUnit.SECONDS.toMillis(10)); resolved = Recipient.resolved(resolved.getId()); } catch (IOException e) { Log.w(TAG, "[onContactSelected] Failed to refresh directory for new contact."); diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/new/NewCallActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/new/NewCallActivity.kt index eb45c32a9e..90e2f87504 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/new/NewCallActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/new/NewCallActivity.kt @@ -25,6 +25,7 @@ import org.thoughtcrime.securesms.util.views.SimpleProgressDialog import java.io.IOException import java.util.Optional import java.util.function.Consumer +import kotlin.time.Duration.Companion.seconds class NewCallActivity : ContactSelectionActivity(), ContactSelectionListFragment.NewCallCallback { @@ -49,7 +50,7 @@ class NewCallActivity : ContactSelectionActivity(), ContactSelectionListFragment if (!resolved.isRegistered || !resolved.hasServiceId()) { Log.i(TAG, "[onContactSelected] Not registered or no UUID. Doing a directory refresh.") resolved = try { - refresh(this, resolved, false) + refresh(this, resolved, false, 10.seconds.inWholeMilliseconds) Recipient.resolved(resolved.id) } catch (e: IOException) { Log.w(TAG, "[onContactSelected] Failed to refresh directory for new contact.") diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt index 579323a08a..b08e5935c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscovery.kt @@ -91,14 +91,15 @@ object ContactDiscovery { } @JvmStatic + @JvmOverloads @Throws(IOException::class) @WorkerThread - fun refresh(context: Context, recipient: Recipient, notifyOfNewUsers: Boolean): RecipientTable.RegisteredState { + fun refresh(context: Context, recipient: Recipient, notifyOfNewUsers: Boolean, timeoutMs: Long? = null): RecipientTable.RegisteredState { val result: RefreshResult = refreshRecipients( context = context, descriptor = "refresh-single", refresh = { - ContactDiscoveryRefreshV2.refresh(context, listOf(recipient), useCompat = !FeatureFlags.phoneNumberPrivacy(), ignoreResults = false) + ContactDiscoveryRefreshV2.refresh(context, listOf(recipient), useCompat = !FeatureFlags.phoneNumberPrivacy(), ignoreResults = false, timeoutMs = timeoutMs) }, removeSystemContactLinksIfMissing = false, notifyOfNewUsers = notifyOfNewUsers diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt index 1a16eacdcb..936f49cda5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/sync/ContactDiscoveryRefreshV2.kt @@ -49,7 +49,7 @@ object ContactDiscoveryRefreshV2 { @WorkerThread @Synchronized @JvmStatic - fun refreshAll(context: Context, useCompat: Boolean, ignoreResults: Boolean): ContactDiscovery.RefreshResult { + fun refreshAll(context: Context, useCompat: Boolean, ignoreResults: Boolean, timeoutMs: Long? = null): ContactDiscovery.RefreshResult { val recipientE164s: Set = SignalDatabase.recipients.getAllE164s().sanitize() val systemE164s: Set = SystemContactsRepository.getAllDisplayNumbers(context).toE164s(context).sanitize() @@ -59,7 +59,8 @@ object ContactDiscoveryRefreshV2 { inputPreviousE164s = SignalDatabase.cds.getAllE164s(), isPartialRefresh = false, useCompat = useCompat, - ignoreResults = ignoreResults + ignoreResults = ignoreResults, + timeoutMs = timeoutMs ) } @@ -67,14 +68,14 @@ object ContactDiscoveryRefreshV2 { @WorkerThread @Synchronized @JvmStatic - fun refresh(context: Context, inputRecipients: List, useCompat: Boolean, ignoreResults: Boolean): ContactDiscovery.RefreshResult { + fun refresh(context: Context, inputRecipients: List, useCompat: Boolean, ignoreResults: Boolean, timeoutMs: Long? = null): ContactDiscovery.RefreshResult { val recipients: List = inputRecipients.map { it.resolve() } val inputE164s: Set = recipients.mapNotNull { it.e164.orElse(null) }.toSet().sanitize() return if (inputE164s.size > MAXIMUM_ONE_OFF_REQUEST_SIZE) { Log.i(TAG, "List of specific recipients to refresh is too large! (Size: ${recipients.size}). Doing a full refresh instead.") - val fullResult: ContactDiscovery.RefreshResult = refreshAll(context, useCompat = useCompat, ignoreResults = ignoreResults) + val fullResult: ContactDiscovery.RefreshResult = refreshAll(context, useCompat = useCompat, ignoreResults = ignoreResults, timeoutMs = timeoutMs) val inputIds: Set = recipients.map { it.id }.toSet() ContactDiscovery.RefreshResult( @@ -88,7 +89,8 @@ object ContactDiscoveryRefreshV2 { inputPreviousE164s = emptySet(), isPartialRefresh = true, useCompat = useCompat, - ignoreResults = ignoreResults + ignoreResults = ignoreResults, + timeoutMs = timeoutMs ) } } @@ -100,7 +102,8 @@ object ContactDiscoveryRefreshV2 { inputPreviousE164s: Set, isPartialRefresh: Boolean, useCompat: Boolean, - ignoreResults: Boolean + ignoreResults: Boolean, + timeoutMs: Long? = null ): ContactDiscovery.RefreshResult { val tag = "refreshInternal-${if (useCompat) "compat" else "v2"}" val stopwatch = Stopwatch(tag) @@ -134,7 +137,8 @@ object ContactDiscoveryRefreshV2 { SignalDatabase.recipients.getAllServiceIdProfileKeyPairs(), useCompat, Optional.ofNullable(token), - BuildConfig.CDSI_MRENCLAVE + BuildConfig.CDSI_MRENCLAVE, + timeoutMs ) { tokenToSave -> stopwatch.split("network-pre-token") if (!isPartialRefresh) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java index edb94a7ebd..a5fcd56c9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java @@ -723,7 +723,7 @@ public class ConversationParentFragment extends Fragment case ADD_CONTACT: SimpleTask.run(() -> { try { - ContactDiscovery.refresh(requireContext(), recipient.get(), false); + ContactDiscovery.refresh(requireContext(), recipient.get(), false, TimeUnit.SECONDS.toMillis(10)); } catch (IOException e) { Log.w(TAG, "Failed to refresh user after adding to contacts."); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java index db116e41e7..bf0aa73618 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/CreateGroupActivity.java @@ -33,6 +33,7 @@ import java.io.IOException; import java.util.List; import java.util.Optional; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -160,7 +161,7 @@ public class CreateGroupActivity extends ContactSelectionActivity { for (Recipient recipient : registeredChecks) { try { - ContactDiscovery.refresh(this, recipient, false); + ContactDiscovery.refresh(this, recipient, false, TimeUnit.SECONDS.toMillis(10)); } catch (IOException e) { Log.w(TAG, "Failed to refresh registered status for " + recipient.getId(), e); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java index 14553491c7..b5f3f3e503 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -49,6 +49,7 @@ import org.whispersystems.signalservice.api.push.ServiceId; import java.io.IOException; import java.util.Optional; +import java.util.concurrent.TimeUnit; public class CommunicationActions { @@ -303,7 +304,7 @@ public class CommunicationActions { if (!recipient.isRegistered() || !recipient.hasServiceId()) { try { - ContactDiscovery.refresh(activity, recipient, false); + ContactDiscovery.refresh(activity, recipient, false, TimeUnit.SECONDS.toMillis(10)); recipient = Recipient.resolved(recipient.getId()); } catch (IOException e) { Log.w(TAG, "[handlePotentialSignalMeUrl] Failed to refresh directory for new contact."); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java index 0e4f33d9a6..37576b8d9d 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceAccountManager.java @@ -390,6 +390,7 @@ public class SignalServiceAccountManager { boolean requireAcis, Optional token, String mrEnclave, + Long timeoutMs, Consumer tokenSaver) throws IOException { @@ -400,11 +401,20 @@ public class SignalServiceAccountManager { ServiceResponse serviceResponse; try { - serviceResponse = single.blockingGet(); + if (timeoutMs == null) { + serviceResponse = single + .blockingGet(); + } else { + serviceResponse = single + .timeout(timeoutMs, TimeUnit.MILLISECONDS) + .blockingGet(); + } } catch (RuntimeException e) { Throwable cause = e.getCause(); if (cause instanceof InterruptedException) { throw new IOException("Interrupted", cause); + } else if (cause instanceof TimeoutException) { + throw new IOException("Timed out"); } else { throw e; }