From 9a8e0dd64f2c26f230ff3396851a436bd6c45e2c Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Fri, 17 Jul 2026 14:56:46 -0400 Subject: [PATCH] Fix linked device changing discoverability bug. --- .../securesms/jobs/RefreshAttributesJob.java | 4 +++- .../securesms/storage/StorageSyncHelper.kt | 6 ++++++ .../signalservice/api/account/AccountApi.kt | 13 +++++++++++++ .../push/PhoneNumberDiscoverabilityRequest.kt | 11 +++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PhoneNumberDiscoverabilityRequest.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java index f8ef77e832..69b6af9113 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshAttributesJob.java @@ -108,8 +108,10 @@ public class RefreshAttributesJob extends BaseJob { if (SignalStore.account().isPrimaryDevice()) { setPrimaryDeviceAttributes(svrValues, capabilities); } else { - Log.i(TAG, "Linked device, refreshing device capabilities only. Capabilities: " + capabilities); + boolean phoneNumberDiscoverable = SignalStore.phoneNumberPrivacy().getPhoneNumberDiscoverabilityMode() == PhoneNumberDiscoverabilityMode.DISCOVERABLE; + Log.i(TAG, "Linked device, refreshing device capabilities and phone number discoverability. Capabilities: " + capabilities + ", discoverable: " + phoneNumberDiscoverable); RequestResultUtil.successOrThrow(SignalNetwork.account().setCapabilities(capabilities)); + RequestResultUtil.successOrThrow(SignalNetwork.account().setPhoneNumberDiscoverability(phoneNumberDiscoverable)); } hasRefreshedThisAppCycle = true; diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt index 7d483f0291..4783696f08 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/StorageSyncHelper.kt @@ -22,6 +22,7 @@ import org.thoughtcrime.securesms.database.model.KeyTransparencyStore import org.thoughtcrime.securesms.database.model.RecipientRecord import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.dependencies.KeyTransparencyApi +import org.thoughtcrime.securesms.jobs.RefreshAttributesJob import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob import org.thoughtcrime.securesms.jobs.StorageSyncJob import org.thoughtcrime.securesms.keyvalue.AccountValues @@ -263,6 +264,11 @@ object StorageSyncHelper { SignalStore.story.userHasSeenGroupStoryEducationSheet = update.new.proto.hasSeenGroupStoryEducationSheet SignalStore.uiHints.setHasCompletedUsernameOnboarding(update.new.proto.hasCompletedUsernameOnboarding) + if (update.new.proto.unlistedPhoneNumber != update.old.proto.unlistedPhoneNumber && SignalStore.account.isPrimaryDevice) { + Log.i(TAG, "Phone number discoverability changed via storage service. Refreshing attributes to push the change to the server.") + AppDependencies.jobManager.add(RefreshAttributesJob()) + } + if (SignalStore.settings.automaticVerificationEnabled && update.new.proto.automaticKeyVerificationDisabled) { SignalDatabase.recipients.clearAllKeyTransparencyData() } diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt index 6befea4b8b..f26e374341 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/account/AccountApi.kt @@ -22,6 +22,7 @@ import org.whispersystems.signalservice.api.websocket.SignalWebSocket import org.whispersystems.signalservice.internal.push.ConfirmUsernameRequest import org.whispersystems.signalservice.internal.push.ConfirmUsernameResponse import org.whispersystems.signalservice.internal.push.GcmRegistrationId +import org.whispersystems.signalservice.internal.push.PhoneNumberDiscoverabilityRequest import org.whispersystems.signalservice.internal.push.PushServiceSocket import org.whispersystems.signalservice.internal.push.ReserveUsernameRequest import org.whispersystems.signalservice.internal.push.ReserveUsernameResponse @@ -90,6 +91,18 @@ class AccountApi(private val authWebSocket: SignalWebSocket.AuthenticatedWebSock return authWebSocket.fromWebSocketRequest(request, Unit::class) } + /** + * Set whether this account is discoverable by phone number. Unlike [setAccountAttributes], this + * dedicated endpoint can be called from a linked device. + * + * PUT /v2/accounts/phone_number_discoverability + * - 204: Success + */ + fun setPhoneNumberDiscoverability(discoverable: Boolean): RequestResult { + val request = WebSocketRequestMessage.put("/v2/accounts/phone_number_discoverability", PhoneNumberDiscoverabilityRequest(discoverable)) + return authWebSocket.fromWebSocketRequest(request, Unit::class) + } + /** * PUT /v1/accounts/registration_lock * - 204: Success diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PhoneNumberDiscoverabilityRequest.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PhoneNumberDiscoverabilityRequest.kt new file mode 100644 index 0000000000..772db79bfb --- /dev/null +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PhoneNumberDiscoverabilityRequest.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2026 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ +package org.whispersystems.signalservice.internal.push + +import com.fasterxml.jackson.annotation.JsonProperty + +data class PhoneNumberDiscoverabilityRequest( + @JsonProperty val discoverableByPhoneNumber: Boolean +)