From 209a1f04fd1474965486fa0d748aed0b2ba6a71c Mon Sep 17 00:00:00 2001 From: Michelle Tang Date: Mon, 10 Aug 2026 11:31:21 -0400 Subject: [PATCH] Fix KT usernames after device transfer. --- .../securesms/database/RecipientTable.kt | 4 +- .../securesms/database/SignalDatabase.kt | 2 + .../securesms/jobs/JobManagerFactories.java | 2 + .../migrations/ApplicationMigrations.java | 7 ++- .../KeyTransparencyFailureMigrationJob.kt | 46 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/migrations/KeyTransparencyFailureMigrationJob.kt diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt index fe343a84d3..1380cd8034 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientTable.kt @@ -4244,8 +4244,8 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da val updated = writableDatabase .update(TABLE_NAME) .values(KEY_TRANSPARENCY_DATA to null) - .where("$ACI_COLUMN = ?", Recipient.self().requireAci().toString()) - .run() + .where("$ACI_COLUMN = ? AND $KEY_TRANSPARENCY_DATA IS NOT NULL", Recipient.self().requireAci().toString()) + .run() > 0 Log.i(TAG, "Clearing self key transparency data $updated") } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt index a32ae0ae25..221d7aed96 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SignalDatabase.kt @@ -10,6 +10,7 @@ import org.signal.core.util.crypto.AttachmentSecret import org.signal.core.util.logging.Log import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.crypto.DatabaseSecret +import org.thoughtcrime.securesms.database.SignalDatabase.Companion.runInTransaction import org.thoughtcrime.securesms.database.helpers.SignalDatabaseMigrations import org.thoughtcrime.securesms.database.model.AvatarPickerDatabase import java.io.File @@ -299,6 +300,7 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data instance!!.reactionTable.deleteAbandonedReactions() instance!!.searchTable.fullyResetTables(useTransaction = false) instance!!.recipientTable.clearFileWallpapersPostBackupRestore() + instance!!.recipientTable.clearSelfKeyTransparencyData() instance!!.rawWritableDatabase.execSQL("DROP TABLE IF EXISTS key_value") instance!!.rawWritableDatabase.execSQL("DROP TABLE IF EXISTS megaphone") instance!!.rawWritableDatabase.execSQL("DROP TABLE IF EXISTS job_spec") 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 b1145c8ba3..31af08a5f3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -76,6 +76,7 @@ import org.thoughtcrime.securesms.migrations.FixChangeNumberErrorMigrationJob; import org.thoughtcrime.securesms.migrations.GooglePlayBillingPurchaseTokenMigrationJob; import org.thoughtcrime.securesms.migrations.GroupDeletedBackfillMigrationJob; import org.thoughtcrime.securesms.migrations.IdentityTableCleanupMigrationJob; +import org.thoughtcrime.securesms.migrations.KeyTransparencyFailureMigrationJob; import org.thoughtcrime.securesms.migrations.KeyTransparencyUsernameMigrationJob; import org.thoughtcrime.securesms.migrations.LegacyMigrationJob; import org.thoughtcrime.securesms.migrations.LocalArchiveReconciliationMigrationJob; @@ -347,6 +348,7 @@ public final class JobManagerFactories { put(GooglePlayBillingPurchaseTokenMigrationJob.KEY, new GooglePlayBillingPurchaseTokenMigrationJob.Factory()); put(GroupDeletedBackfillMigrationJob.KEY, new GroupDeletedBackfillMigrationJob.Factory()); put(IdentityTableCleanupMigrationJob.KEY, new IdentityTableCleanupMigrationJob.Factory()); + put(KeyTransparencyFailureMigrationJob.KEY, new KeyTransparencyFailureMigrationJob.Factory()); put(KeyTransparencyUsernameMigrationJob.KEY, new KeyTransparencyUsernameMigrationJob.Factory()); put(LegacyMigrationJob.KEY, new LegacyMigrationJob.Factory()); put(LocalArchiveReconciliationMigrationJob.KEY, new LocalArchiveReconciliationMigrationJob.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 81fb032568..56458cd727 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/ApplicationMigrations.java @@ -211,9 +211,10 @@ public class ApplicationMigrations { // static final int GROUP_DELETED_AT_BACKFILL = 167; static final int STICKER_PACK_STORAGE_SYNC = 168; static final int GROUP_DELETED_AT_BACKFILL = 169; + static final int KT_RESET_FAILURE = 170; } - public static final int CURRENT_VERSION = 169; + public static final int CURRENT_VERSION = 170; /** * This *must* be called after the {@link JobManager} has been instantiated, but *before* the call @@ -980,6 +981,10 @@ public class ApplicationMigrations { jobs.put(Version.GROUP_DELETED_AT_BACKFILL, new GroupDeletedBackfillMigrationJob()); } + if (lastSeenVersion < Version.KT_RESET_FAILURE) { + jobs.put(Version.KT_RESET_FAILURE, new KeyTransparencyFailureMigrationJob()); + } + return jobs; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/migrations/KeyTransparencyFailureMigrationJob.kt b/app/src/main/java/org/thoughtcrime/securesms/migrations/KeyTransparencyFailureMigrationJob.kt new file mode 100644 index 0000000000..8f849204af --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/migrations/KeyTransparencyFailureMigrationJob.kt @@ -0,0 +1,46 @@ +package org.thoughtcrime.securesms.migrations + +import org.signal.core.util.logging.Log +import org.signal.core.util.logging.Log.tag +import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobs.CheckKeyTransparencyJob +import org.thoughtcrime.securesms.keyvalue.SignalStore + +/** + * Previously had a bug in KT that affected those with usernames who did a device transfer. + * Check if there is a KT failure, and reset it so that KT will start again. + */ +internal class KeyTransparencyFailureMigrationJob private constructor(parameters: Parameters) : MigrationJob(parameters) { + + companion object { + + const val KEY = "KeyTransparencyFailureMigrationJob" + + private val TAG: String = tag(KeyTransparencyFailureMigrationJob::class.java) + } + + internal constructor() : this(Parameters.Builder().build()) + + override fun isUiBlocking(): Boolean = false + + override fun getFactoryKey(): String = KEY + + override fun performMigration() { + if (SignalStore.misc.hasKeyTransparencyFailure) { + Log.i(TAG, "Has KT failure, resetting and enqueuing again") + SignalStore.account.distinguishedHead = null + SignalStore.misc.nextKeyTransparencyTime = 0 + SignalDatabase.recipients.clearAllKeyTransparencyData() + CheckKeyTransparencyJob.enqueueIfNecessary(addDelay = false, force = true) + } + } + + override fun shouldRetry(e: Exception): Boolean = false + + class Factory : Job.Factory { + override fun create(parameters: Parameters, serializedData: ByteArray?): KeyTransparencyFailureMigrationJob { + return KeyTransparencyFailureMigrationJob(parameters) + } + } +}