From 9762899272deb9b92ac0dc87a26acce35b3866f1 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 17 Apr 2024 15:04:22 -0400 Subject: [PATCH] Remove old thread remappings. --- .../securesms/backup/FullBackupExporter.java | 5 ++- .../database/RemappedRecordTables.kt | 34 +++++++++++++++++-- .../securesms/database/RemappedRecords.java | 23 ++++--------- .../securesms/database/ThreadTable.kt | 22 ++++++++---- 4 files changed, 58 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java index c62a0fae85..1b3d40bf59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.java @@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.database.MessageTable; import org.thoughtcrime.securesms.database.OneTimePreKeyTable; import org.thoughtcrime.securesms.database.PendingRetryReceiptTable; import org.thoughtcrime.securesms.database.ReactionTable; +import org.thoughtcrime.securesms.database.RemappedRecordTables; import org.thoughtcrime.securesms.database.SearchTable; import org.thoughtcrime.securesms.database.SenderKeyTable; import org.thoughtcrime.securesms.database.SenderKeySharedTable; @@ -92,7 +93,9 @@ public class FullBackupExporter extends FullBackupBase { SenderKeyTable.TABLE_NAME, SenderKeySharedTable.TABLE_NAME, PendingRetryReceiptTable.TABLE_NAME, - AvatarPickerDatabase.TABLE_NAME + AvatarPickerDatabase.TABLE_NAME, + RemappedRecordTables.Recipients.TABLE_NAME, + RemappedRecordTables.Threads.TABLE_NAME ); public static BackupEvent export(@NonNull Context context, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt index 3859c9761b..1eb4bc806c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecordTables.kt @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database import android.content.Context import android.database.Cursor import androidx.core.content.contentValuesOf +import org.signal.core.util.delete import org.signal.core.util.logging.Log import org.signal.core.util.readToList import org.signal.core.util.requireLong @@ -30,7 +31,7 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe const val NEW_ID = "new_id" } - private object Recipients { + object Recipients { const val TABLE_NAME = "remapped_recipients" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -41,7 +42,7 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe """ } - private object Threads { + object Threads { const val TABLE_NAME = "remapped_threads" const val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -56,6 +57,9 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe val recipientMap: MutableMap = HashMap() readableDatabase.withinTransaction { db -> + trimInvalidRecipientEntries(db) + trimInvalidThreadEntries(db) + val mappings = getAllMappings(db, Recipients.TABLE_NAME) for (mapping in mappings) { val oldId = RecipientId.from(mapping.oldId) @@ -102,6 +106,32 @@ class RemappedRecordTables internal constructor(context: Context?, databaseHelpe .run() } + fun deleteThreadMapping(oldId: Long) { + writableDatabase.delete(Threads.TABLE_NAME) + .where("$OLD_ID = ?", oldId) + .run() + } + + private fun trimInvalidRecipientEntries(db: SQLiteDatabase) { + val count = db.delete(Recipients.TABLE_NAME) + .where("$OLD_ID IN (SELECT $ID FROM ${RecipientTable.TABLE_NAME})") + .run() + + if (count > 0) { + Log.w(TAG, "Trimmed $count invalid recipient entries.", true) + } + } + + private fun trimInvalidThreadEntries(db: SQLiteDatabase) { + val count = db.delete(Threads.TABLE_NAME) + .where("$OLD_ID IN (SELECT $ID FROM ${ThreadTable.TABLE_NAME})") + .run() + + if (count > 0) { + Log.w(TAG, "Trimmed $count invalid thread entries.", true) + } + } + private fun getAllMappings(db: SQLiteDatabase, table: String): List { return db.select() .from(table) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java index cbd5737c82..fb97049171 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RemappedRecords.java @@ -49,27 +49,18 @@ class RemappedRecords { return Optional.ofNullable(threadMap.get(oldId)); } + void deleteThread(long oldId) { + ensureInTransaction(); + ensureThreadMapIsPopulated(); + threadMap.remove(oldId); + SignalDatabase.remappedRecords().deleteThreadMapping(oldId); + } + boolean areAnyRemapped(@NonNull Collection recipientIds) { ensureRecipientMapIsPopulated(); return recipientIds.stream().anyMatch(id -> recipientMap.containsKey(id)); } - @NonNull Set remap(@NonNull Collection recipientIds) { - ensureRecipientMapIsPopulated(); - - Set remapped = new LinkedHashSet<>(); - - for (RecipientId original : recipientIds) { - if (recipientMap.containsKey(original)) { - remapped.add(recipientMap.get(original)); - } else { - remapped.add(original); - } - } - - return remapped; - } - @NonNull String buildRemapDescription(@NonNull Collection recipientIds) { StringBuilder builder = new StringBuilder(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt index a304546eec..05b458c023 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -1141,15 +1141,23 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa fun getOrCreateValidThreadId(recipient: Recipient, candidateId: Long, distributionType: Int): Long { return if (candidateId != -1L) { - val remapped = RemappedRecords.getInstance().getThread(candidateId) - if (remapped.isPresent) { - Log.i(TAG, "Using remapped threadId: " + candidateId + " -> " + remapped.get()) - remapped.get() + if (areThreadIdAndRecipientAssociated(candidateId, recipient)) { + candidateId } else { - if (areThreadIdAndRecipientAssociated(candidateId, recipient)) { - candidateId + val remapped = RemappedRecords.getInstance().getThread(candidateId) + if (remapped.isPresent) { + if (areThreadIdAndRecipientAssociated(remapped.get(), recipient)) { + Log.i(TAG, "Using remapped threadId: $candidateId -> ${remapped.get()}") + remapped.get() + } else { + Log.i(TAG, "There's a remap for $candidateId -> ${remapped.get()}, but it's not associated with $recipient. Deleting old remap and throwing.") + writableDatabase.withinTransaction { + RemappedRecords.getInstance().deleteThread(candidateId) + } + throw IllegalArgumentException("Candidate threadId ($candidateId) is not associated with recipient ($recipient)") + } } else { - throw IllegalArgumentException() + throw IllegalArgumentException("Candidate threadId ($candidateId) is not associated with recipient ($recipient)") } } } else {