From f172f5d47ef8fb16d51465208331ef40f5d84db0 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 14 Apr 2025 19:59:32 -0400 Subject: [PATCH 1/3] Convert change number back to REST. --- .../app/changenumber/ChangeNumberRepository.kt | 2 +- .../api/registration/RegistrationApi.kt | 12 ++++++++++++ .../internal/push/PushServiceSocket.java | 11 +++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt index da8feb0380..2048288178 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/changenumber/ChangeNumberRepository.kt @@ -267,7 +267,7 @@ class ChangeNumberRepository( SignalStore.misc.setPendingChangeNumberMetadata(metadata) withContext(Dispatchers.IO) { - result = SignalNetwork.account.changeNumber(request) + result = accountManager.registrationApi.changeNumber(request) } val possibleError = result.getCause() as? MismatchedDevicesException diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt index eafb34e22b..26b42f9566 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/registration/RegistrationApi.kt @@ -7,6 +7,7 @@ package org.whispersystems.signalservice.api.registration import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.account.AccountAttributes +import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest import org.whispersystems.signalservice.api.account.PreKeyCollection import org.whispersystems.signalservice.api.provisioning.RestoreMethod import org.whispersystems.signalservice.internal.push.BackupV2AuthCheckResponse @@ -133,4 +134,15 @@ class RegistrationApi( pushServiceSocket.setRestoreMethodChosen(token, RestoreMethodBody(method = method)) } } + + /** + * Changes the phone number that an account is associated with. + * + * `PUT /v2/accounts/number` + */ + fun changeNumber(requestBody: ChangePhoneNumberRequest): NetworkResult { + return NetworkResult.fromFetch { + pushServiceSocket.changeNumber(requestBody) + } + } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java index 8f649baeb2..b7a38407ba 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/internal/push/PushServiceSocket.java @@ -29,6 +29,7 @@ import org.signal.storageservice.protos.groups.GroupResponse; import org.signal.storageservice.protos.groups.Member; import org.whispersystems.signalservice.api.NetworkResult; import org.whispersystems.signalservice.api.account.AccountAttributes; +import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest; import org.whispersystems.signalservice.api.account.PreKeyCollection; import org.whispersystems.signalservice.api.crypto.SealedSenderAccess; import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString; @@ -368,6 +369,16 @@ public class PushServiceSocket { return JsonUtil.fromJson(response, VerifyAccountResponse.class); } + + public VerifyAccountResponse changeNumber(@Nonnull ChangePhoneNumberRequest changePhoneNumberRequest) + throws IOException + { + String requestBody = JsonUtil.toJson(changePhoneNumberRequest); + String responseBody = makeServiceRequest("/v2/accounts/number", "PUT", requestBody); + + return JsonUtil.fromJson(responseBody, VerifyAccountResponse.class); + } + public void setRestoreMethodChosen(@Nonnull String token, @Nonnull RestoreMethodBody request) throws IOException { String body = JsonUtil.toJson(request); makeServiceRequest(String.format(Locale.US, SET_RESTORE_METHOD_PATH, urlEncode(token)), "PUT", body, NO_HEADERS, UNOPINIONATED_HANDLER, SealedSenderAccess.NONE); From 6a7bf791a2c5133608e9332b16cd394bb276b9ae Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 15 Apr 2025 16:59:44 -0400 Subject: [PATCH 2/3] Fix potential bad state with change numbers. --- .../securesms/jobs/JobManagerFactories.java | 2 + .../migrations/ApplicationMigrations.java | 8 +- .../FixChangeNumberErrorMigrationJob.kt | 73 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/migrations/FixChangeNumberErrorMigrationJob.kt 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 85e6d85671..b0ca49e584 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.migrations.DuplicateE164MigrationJob; import org.thoughtcrime.securesms.migrations.E164FormattingMigrationJob; import org.thoughtcrime.securesms.migrations.EmojiDownloadMigrationJob; import org.thoughtcrime.securesms.migrations.EmojiSearchIndexCheckMigrationJob; +import org.thoughtcrime.securesms.migrations.FixChangeNumberErrorMigrationJob; import org.thoughtcrime.securesms.migrations.GooglePlayBillingPurchaseTokenMigrationJob; import org.thoughtcrime.securesms.migrations.IdentityTableCleanupMigrationJob; import org.thoughtcrime.securesms.migrations.LegacyMigrationJob; @@ -298,6 +299,7 @@ public final class JobManagerFactories { put(E164FormattingMigrationJob.KEY, new E164FormattingMigrationJob.Factory()); put(EmojiDownloadMigrationJob.KEY, new EmojiDownloadMigrationJob.Factory()); put(EmojiSearchIndexCheckMigrationJob.KEY, new EmojiSearchIndexCheckMigrationJob.Factory()); + put(FixChangeNumberErrorMigrationJob.KEY, new FixChangeNumberErrorMigrationJob.Factory()); put(GooglePlayBillingPurchaseTokenMigrationJob.KEY, new GooglePlayBillingPurchaseTokenMigrationJob.Factory()); put(IdentityTableCleanupMigrationJob.KEY, new IdentityTableCleanupMigrationJob.Factory()); put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java index b42a570250..71f686c5bf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -176,9 +176,11 @@ public class ApplicationMigrations { static final int AVATAR_COLOR_MIGRATION_JOB = 132; static final int DUPLICATE_E164_FIX_2 = 133; static final int E164_FORMATTING = 134; + // Need to skip because a 135 exists in 7.40, which went to beta users before this + static final int FIX_CHANGE_NUMBER_ERROR = 136; } - public static final int CURRENT_VERSION = 134; + public static final int CURRENT_VERSION = 136; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -813,6 +815,10 @@ public class ApplicationMigrations { jobs.put(Version.E164_FORMATTING, new E164FormattingMigrationJob()); } + if (lastSeenVersion < Version.FIX_CHANGE_NUMBER_ERROR) { + jobs.put(Version.FIX_CHANGE_NUMBER_ERROR, new FixChangeNumberErrorMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/FixChangeNumberErrorMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/FixChangeNumberErrorMigrationJob.kt new file mode 100644 index 0000000000..15036eeaee --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/FixChangeNumberErrorMigrationJob.kt @@ -0,0 +1,73 @@ +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberRepository +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.keyvalue.SignalStore +import org.thoughtcrime.securesms.net.SignalNetwork +import org.whispersystems.signalservice.api.NetworkResult +import org.whispersystems.signalservice.api.push.ServiceId +import org.whispersystems.signalservice.internal.push.WhoAmIResponse +import java.io.IOException + +/** + * There was a server error during change number where a number was changed but gave back a 409 response. + * We need devices to re-fetch their E164+PNI's, save them, and then get prekeys. + */ +internal class FixChangeNumberErrorMigrationJob( + parameters: Parameters = Parameters.Builder().build() +) : MigrationJob(parameters) { + companion object { + const val KEY = "FixChangeNumberErrorMigrationJob" + private val TAG = Log.tag(FixChangeNumberErrorMigrationJob::class) + } + + override fun getFactoryKey(): String = KEY + + override fun isUiBlocking(): Boolean = false + + override fun performMigration() { + if (!SignalStore.account.isRegistered) { + Log.i(TAG, "Not registered, skipping.") + return + } + + val pendingChangeNumberMetadata = SignalStore.misc.pendingChangeNumberMetadata + + if (pendingChangeNumberMetadata == null) { + Log.i(TAG, "No pending change number metadata, skipping.") + return + } + + if (pendingChangeNumberMetadata.previousPni != SignalStore.account.pni?.toByteString()) { + Log.i(TAG, "Pending change number operation isn't for our current PNI, skipping.") + return + } + + when (val result = SignalNetwork.account.whoAmI()) { + is NetworkResult.Success -> { + val pni = result.result.pni?.let { ServiceId.PNI.parseOrNull(it) } ?: return + + if (result.result.number != SignalStore.account.e164 || pni != SignalStore.account.pni) { + Log.w(TAG, "Detected a number or PNI mismatch! Fixing...") + ChangeNumberRepository().changeLocalNumber(result.result.number, pni) + Log.w(TAG, "Done!") + } else { + Log.i(TAG, "No number or PNI mismatch detected.") + return + } + } + is NetworkResult.ApplicationError -> throw result.throwable + is NetworkResult.NetworkError -> throw result.exception + is NetworkResult.StatusCodeError -> throw result.exception + } + } + + override fun shouldRetry(e: Exception): Boolean = e is IOException + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): FixChangeNumberErrorMigrationJob { + return FixChangeNumberErrorMigrationJob(parameters) + } + } +} From f13c894df73f007f8ec86ce94c53f229c758b28e Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Tue, 15 Apr 2025 17:03:06 -0400 Subject: [PATCH 3/3] Bump version to 7.39.5 --- app/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 88f31fc26b..49170d8ef3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -22,8 +22,8 @@ plugins { apply(from = "static-ips.gradle.kts") val canonicalVersionCode = 1535 -val canonicalVersionName = "7.39.4" -val currentHotfixVersion = 0 +val canonicalVersionName = "7.39.5" +val currentHotfixVersion = 1 val maxHotfixVersions = 100 val keystores: Map = mapOf("debug" to loadKeystoreProperties("keystore.debug.properties"))