From 7b0badef1933e1f7562fd32a8603f3f4974c19c1 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 24 Jul 2024 10:28:09 -0400 Subject: [PATCH] Get shared backup tests working. --- .../assets/backupTests/account-data.binproto | Bin 218 -> 273 bytes ...iration-timer-chat-update-message.binproto | Bin 0 -> 236 bytes ...rofile-change-chat-update-message.binproto | Bin 0 -> 240 bytes .../registered-blocked-contact.binproto | Bin 204 -> 252 bytes ...on-switchover-chat-update-message.binproto | Bin 0 -> 222 bytes .../simple-chat-update-message.binproto | Bin 0 -> 480 bytes .../story-distribution-list.binproto | Bin 454 -> 461 bytes .../thread-merge-chat-update-message.binproto | Bin 0 -> 222 bytes .../backupTests/unregistered-contact.binproto | Bin 209 -> 257 bytes .../securesms/backup/v2/ImportExportTest.kt | 6 +- .../backup/v2/ImportExportTestSuite.kt | 25 ++++-- .../securesms/util/MessageTableTestUtils.kt | 2 +- .../securesms/backup/v2/BackupRepository.kt | 23 +++--- .../v2/database/ChatItemExportIterator.kt | 33 +++++++- .../v2/database/ChatItemImportInserter.kt | 3 +- .../DistributionListTablesBackupExtensions.kt | 74 +++++++++++------- .../database/ThreadTableBackupExtensions.kt | 1 + .../v2/processor/AccountDataProcessor.kt | 35 +++++++-- .../v2/processor/RecipientBackupProcessor.kt | 1 + .../InternalBackupPlaygroundViewModel.kt | 2 +- .../conversation/ConversationUpdateItem.java | 4 +- .../ConversationListItem.java | 2 +- .../database/DistributionListTables.kt | 10 +-- .../securesms/database/MessageTable.kt | 6 +- .../securesms/database/MessageTypes.java | 42 +++++----- .../securesms/database/ThreadTable.kt | 10 +-- .../database/model/DisplayRecord.java | 4 +- .../database/model/MessageRecord.java | 4 +- app/src/main/protowire/Backup.proto | 3 +- .../securesms/database/SmsDatabaseTest.kt | 2 +- 30 files changed, 184 insertions(+), 108 deletions(-) create mode 100644 app/src/androidTest/assets/backupTests/expiration-timer-chat-update-message.binproto create mode 100644 app/src/androidTest/assets/backupTests/profile-change-chat-update-message.binproto create mode 100644 app/src/androidTest/assets/backupTests/session-switchover-chat-update-message.binproto create mode 100644 app/src/androidTest/assets/backupTests/simple-chat-update-message.binproto create mode 100644 app/src/androidTest/assets/backupTests/thread-merge-chat-update-message.binproto diff --git a/app/src/androidTest/assets/backupTests/account-data.binproto b/app/src/androidTest/assets/backupTests/account-data.binproto index 54ebb6e04ae667d9af177e3fb99b0799c6a15c9a..8bca63e4972c7c07f8fa723033e3e1889c1e8f44 100644 GIT binary patch delta 62 zcmcb`IFV_>Eps*@77j)&1`x$$#GoOh#=)$l#3jIh2Bi47ID9J=f=lv?Dg_uNm;nwl B2kig= delta 6 NcmbQpbc=DqEdU7H0@(ln diff --git a/app/src/androidTest/assets/backupTests/expiration-timer-chat-update-message.binproto b/app/src/androidTest/assets/backupTests/expiration-timer-chat-update-message.binproto new file mode 100644 index 0000000000000000000000000000000000000000..29cb01be84724d69a265cabac7c60ee0e84a48be GIT binary patch literal 236 zcmZSMU=%oTgx!+MoJ%2*Y2xa)hn+k<<}RLPcyj64nP)HMe{DBjejy zcc*-Kjejy zcc*-KV3`hH6G805=W=H~;_u diff --git a/app/src/androidTest/assets/backupTests/session-switchover-chat-update-message.binproto b/app/src/androidTest/assets/backupTests/session-switchover-chat-update-message.binproto new file mode 100644 index 0000000000000000000000000000000000000000..b856d50ac6d9923d16c6880081401973535b20b8 GIT binary patch literal 222 zcmZSMU=%oTgx!+MoJ%2*Y2xa)hn+k<<}RLPcyj64nP)HMe{DBjejy zcc*-Kjejy zcc*-K0T!qL*bJbF60A@GHkb-Fr~oTWfE_Bp4in&j3UI&#IH3ZZ qFaa*802f4nQGy#)fJK4_D!>g>!HX)uBEbh0;DM>Y5a5TZ-~|BlT2v$e literal 0 HcmV?d00001 diff --git a/app/src/androidTest/assets/backupTests/story-distribution-list.binproto b/app/src/androidTest/assets/backupTests/story-distribution-list.binproto index 369e68ab6bd517b36d999044165d032257e02ec3..e5eb145ca703e292ac46543ebeedfa9ec068b9cb 100644 GIT binary patch delta 84 zcmX@ce3p4aFb|s$3kQ=CgPV{G2lK=TH%69;^Gz68CtfjN)e_R+V4Lj8sLIA7FC@#s oKADqIo{?j+CZjZ~fsh^t=j0MbO%4_XR#hQo4z9^d85I~=0K!fV>Hq)$ delta 77 zcmX@he2jTQFsqx83kTE0csEAoiOWnFSti~#V3ik=jejy zcc*-KI> { - val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER) - return testFiles?.map { arrayOf(it) }!!.toList() + val testFiles = InstrumentationRegistry.getInstrumentation().context.resources.assets.list(TESTS_FOLDER)!! + return testFiles + .map { arrayOf(it) } + .toList() } } @@ -65,8 +65,11 @@ class ImportExportTestSuite(private val path: String) { assertTrue(importResult is ImportResult.Success) val success = importResult as ImportResult.Success - val generatedBackupData = BackupRepository.export(plaintext = true, currentTime = success.backupTime) - compare(binProtoBytes, generatedBackupData) + val generatedBackupData = BackupRepository.debugExport(plaintext = true, currentTime = success.backupTime) + + // TODO [backup] Currently fails, need to look into it +// assertPassesValidator(generatedBackupData) + assertEquivalent(binProtoBytes, generatedBackupData) } private fun import(importData: ByteArray): ImportResult { @@ -78,7 +81,15 @@ class ImportExportTestSuite(private val path: String) { ) } - private fun compare(import: ByteArray, export: ByteArray) { + private fun assertPassesValidator(generatedBackupData: ByteArray) { + BackupRepository.validate( + length = generatedBackupData.size.toLong(), + inputStreamFactory = { ByteArrayInputStream(generatedBackupData) }, + selfData = BackupRepository.SelfData(SELF_ACI, SELF_PNI, SELF_E164, SELF_PROFILE_KEY) + ) + } + + private fun assertEquivalent(import: ByteArray, export: ByteArray) { val importComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, import.inputStream(), import.size.toLong()) val exportComparable = ComparableBackup.readUnencrypted(MessageBackup.Purpose.REMOTE_BACKUP, export.inputStream(), import.size.toLong()) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/util/MessageTableTestUtils.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/util/MessageTableTestUtils.kt index 53c7afa7e6..ce5ef0e891 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/util/MessageTableTestUtils.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/util/MessageTableTestUtils.kt @@ -61,7 +61,7 @@ object MessageTableTestUtils { isProfileChange:${type == MessageTypes.PROFILE_CHANGE_TYPE} isGroupV1MigrationEvent:${type == MessageTypes.GV1_MIGRATION_TYPE} isChangeNumber:${type == MessageTypes.CHANGE_NUMBER_TYPE} - isBoostRequest:${type == MessageTypes.BOOST_REQUEST_TYPE} + isDonationChannelDonationRequest:${type == MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE} isThreadMerge:${type == MessageTypes.THREAD_MERGE_TYPE} isSmsExport:${type == MessageTypes.SMS_EXPORT_TYPE} isGroupV2LeaveOnly:${type and MessageTypes.GROUP_V2_LEAVE_BITS == MessageTypes.GROUP_V2_LEAVE_BITS} diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt index b8f3f411ac..b17007b882 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/BackupRepository.kt @@ -46,7 +46,6 @@ import org.thoughtcrime.securesms.backup.v2.ui.subscription.MessageBackupsTypeFe import org.thoughtcrime.securesms.components.settings.app.subscription.RecurringInAppPaymentRepository import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider import org.thoughtcrime.securesms.crypto.DatabaseSecretProvider -import org.thoughtcrime.securesms.database.DistributionListTables import org.thoughtcrime.securesms.database.KeyValueDatabase import org.thoughtcrime.securesms.database.SignalDatabase import org.thoughtcrime.securesms.database.model.InAppPaymentSubscriberRecord @@ -249,19 +248,15 @@ object BackupRepository { } } - fun export(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray { + /** + * Exports to a blob in memory. Should only be used for testing. + */ + fun debugExport(plaintext: Boolean = false, currentTime: Long = System.currentTimeMillis()): ByteArray { val outputStream = ByteArrayOutputStream() export(outputStream = outputStream, append = { mac -> outputStream.write(mac) }, plaintext = plaintext, currentTime = currentTime) return outputStream.toByteArray() } - fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult { - val masterKey = SignalStore.svr.getOrCreateMasterKey() - val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray())) - - return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length) - } - /** * @return The time the backup was created, or null if the backup could not be read. */ @@ -306,9 +301,6 @@ object BackupRepository { SignalDatabase.recipients.setProfileKey(selfId, selfData.profileKey) SignalDatabase.recipients.setProfileSharing(selfId, true) - // Add back my story after clearing data - DistributionListTables.insertInitialDistributionListAtCreationTime(it) - eventTimer.emit("setup") val backupState = BackupState(backupKey) val chatItemInserter: ChatItemImportInserter = ChatItemBackupProcessor.beginImport(backupState) @@ -373,6 +365,13 @@ object BackupRepository { return ImportResult.Success(backupTime = header.backupTimeMs) } + fun validate(length: Long, inputStreamFactory: () -> InputStream, selfData: SelfData): ValidationResult { + val masterKey = SignalStore.svr.getOrCreateMasterKey() + val key = MessageBackupKey(masterKey.serialize(), Aci.parseFromBinary(selfData.aci.toByteArray())) + + return MessageBackup.validate(key, MessageBackup.Purpose.REMOTE_BACKUP, inputStreamFactory, length) + } + fun listRemoteMediaObjects(limit: Int, cursor: String? = null): NetworkResult { val api = AppDependencies.signalServiceAccountManager.archiveApi val backupKey = SignalStore.svr.getOrCreateMasterKey().deriveBackupKey() diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index 48f161c08b..e8e35bbfc6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -158,8 +158,8 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: MessageTypes.isChangeNumber(record.type) -> { builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.CHANGE_NUMBER) } - MessageTypes.isBoostRequest(record.type) -> { - builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.BOOST_REQUEST) + MessageTypes.isReleaseChannelDonationRequest(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST) } MessageTypes.isEndSessionType(record.type) -> { builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.END_SESSION) @@ -176,6 +176,12 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: MessageTypes.isPaymentsRequestToActivate(record.type) -> { builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST) } + MessageTypes.isUnsupportedMessageType(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE) + } + MessageTypes.isReportedSpam(record.type) -> { + builder.updateMessage = simpleUpdate(SimpleChatUpdate.Type.REPORTED_SPAM) + } MessageTypes.isExpirationTimerUpdate(record.type) -> { builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn.toInt())) builder.expiresInMs = 0 @@ -265,7 +271,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: expiresInMs = if (record.expiresIn > 0) record.expiresIn else 0 revisions = emptyList() sms = record.type.isSmsType() - if (MessageTypes.isCallLog(record.type)) { + if (record.type.isDirectionlessType()) { directionless = ChatItem.DirectionlessMessageDetails() } else if (MessageTypes.isOutgoingMessageType(record.type)) { outgoing = ChatItem.OutgoingMessageDetails( @@ -999,6 +1005,27 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: return MessageTypes.isOutgoingMessageType(this) || MessageTypes.isInboxType(this) } + private fun Long.isDirectionlessType(): Boolean { + return MessageTypes.isCallLog(this) || + MessageTypes.isExpirationTimerUpdate(this) || + MessageTypes.isThreadMergeType(this) || + MessageTypes.isSessionSwitchoverType(this) || + MessageTypes.isProfileChange(this) || + MessageTypes.isJoinedType(this) || + MessageTypes.isIdentityUpdate(this) || + MessageTypes.isIdentityVerified(this) || + MessageTypes.isIdentityDefault(this) || + MessageTypes.isReleaseChannelDonationRequest(this) || + MessageTypes.isChangeNumber(this) || + MessageTypes.isEndSessionType(this) || + MessageTypes.isChatSessionRefresh(this) || + MessageTypes.isBadDecryptType(this) || + MessageTypes.isPaymentsActivated(this) || + MessageTypes.isPaymentsRequestToActivate(this) || + MessageTypes.isUnsupportedMessageType(this) || + MessageTypes.isReportedSpam(this) + } + private fun String.e164ToLong(): Long? { val fixed = if (this.startsWith("+")) { this.substring(1) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index 055f4f3b8d..3240522eb1 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -624,13 +624,14 @@ class ChatItemImportInserter( SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT or typeWithoutBase SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT or typeWithoutBase SimpleChatUpdate.Type.CHANGE_NUMBER -> MessageTypes.CHANGE_NUMBER_TYPE - SimpleChatUpdate.Type.BOOST_REQUEST -> MessageTypes.BOOST_REQUEST_TYPE + SimpleChatUpdate.Type.RELEASE_CHANNEL_DONATION_REQUEST -> MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT or typeWithoutBase SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT or typeWithoutBase SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE or typeWithoutBase SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED or typeWithoutBase SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST or typeWithoutBase SimpleChatUpdate.Type.UNSUPPORTED_PROTOCOL_MESSAGE -> MessageTypes.UNSUPPORTED_MESSAGE_TYPE or typeWithoutBase + SimpleChatUpdate.Type.REPORTED_SPAM -> MessageTypes.SPECIAL_TYPE_REPORTED_SPAM or typeWithoutBase } } updateMessage.expirationTimerChange != null -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt index 0c8898c204..e5809e5f88 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/DistributionListTablesBackupExtensions.kt @@ -6,14 +6,15 @@ package org.thoughtcrime.securesms.backup.v2.database import okio.ByteString.Companion.toByteString -import org.signal.core.util.CursorUtil import org.signal.core.util.deleteAll import org.signal.core.util.logging.Log import org.signal.core.util.readToList +import org.signal.core.util.requireBoolean import org.signal.core.util.requireLong import org.signal.core.util.requireNonNullString import org.signal.core.util.requireObject import org.signal.core.util.select +import org.signal.core.util.withinTransaction import org.thoughtcrime.securesms.backup.v2.BackupState import org.thoughtcrime.securesms.backup.v2.proto.DistributionList import org.thoughtcrime.securesms.backup.v2.proto.DistributionListItem @@ -36,7 +37,6 @@ fun DistributionListTables.getAllForBackup(): List { val records = readableDatabase .select() .from(DistributionListTables.ListTable.TABLE_NAME) - .where(DistributionListTables.ListTable.IS_NOT_DELETED) .run() .readToList { cursor -> val id: DistributionListId = DistributionListId.from(cursor.requireLong(DistributionListTables.ListTable.ID)) @@ -48,11 +48,11 @@ fun DistributionListTables.getAllForBackup(): List { id = id, name = cursor.requireNonNullString(DistributionListTables.ListTable.NAME), distributionId = DistributionId.from(cursor.requireNonNullString(DistributionListTables.ListTable.DISTRIBUTION_ID)), - allowsReplies = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.ALLOWS_REPLIES), + allowsReplies = cursor.requireBoolean(DistributionListTables.ListTable.ALLOWS_REPLIES), rawMembers = getRawMembers(id, privacyMode), - members = getMembers(id), - deletedAtTimestamp = 0L, - isUnknown = CursorUtil.requireBoolean(cursor, DistributionListTables.ListTable.IS_UNKNOWN), + members = getMembersForBackup(id), + deletedAtTimestamp = cursor.requireLong(DistributionListTables.ListTable.DELETION_TIMESTAMP), + isUnknown = cursor.requireBoolean(DistributionListTables.ListTable.IS_UNKNOWN), privacyMode = privacyMode ) ) @@ -82,7 +82,37 @@ fun DistributionListTables.getAllForBackup(): List { } } +fun DistributionListTables.getMembersForBackup(id: DistributionListId): List { + lateinit var privacyMode: DistributionListPrivacyMode + lateinit var rawMembers: List + + readableDatabase.withinTransaction { + privacyMode = getPrivacyMode(id) + rawMembers = getRawMembers(id, privacyMode) + } + + return when (privacyMode) { + DistributionListPrivacyMode.ALL -> emptyList() + DistributionListPrivacyMode.ONLY_WITH -> rawMembers + DistributionListPrivacyMode.ALL_EXCEPT -> rawMembers + } +} + fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, backupState: BackupState): RecipientId? { + if (dlistItem.deletionTimestamp != null && dlistItem.deletionTimestamp > 0) { + val dlistId = createList( + name = "", + members = emptyList(), + distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)), + allowsReplies = false, + deletionTimestamp = dlistItem.deletionTimestamp, + storageId = null, + privacyMode = DistributionListPrivacyMode.ONLY_WITH + )!! + + return SignalDatabase.distributionLists.getRecipientId(dlistId)!! + } + val dlist = dlistItem.distributionList ?: return null val members: List = dlist.memberRecipientIds .mapNotNull { backupState.backupToLocalRecipientId[it] } @@ -94,32 +124,22 @@ fun DistributionListTables.restoreFromBackup(dlistItem: DistributionListItem, ba val distributionId = DistributionId.from(UuidUtil.fromByteString(dlistItem.distributionId)) val privacyMode = dlist.privacyMode.toLocalPrivacyMode() - val dlistId = if (distributionId == DistributionId.MY_STORY) { - setPrivacyMode(DistributionListId.MY_STORY, privacyMode) - members.forEach { addMemberToList(DistributionListId.MY_STORY, privacyMode, it) } - setAllowsReplies(DistributionListId.MY_STORY, dlist.allowReplies) - DistributionListId.MY_STORY - } else { - createList( - name = dlist.name, - members = members, - distributionId = distributionId, - allowsReplies = dlist.allowReplies, - deletionTimestamp = dlistItem.deletionTimestamp ?: 0, - storageId = null, - privacyMode = privacyMode - )!! - } + val dlistId = createList( + name = dlist.name, + members = members, + distributionId = distributionId, + allowsReplies = dlist.allowReplies, + deletionTimestamp = dlistItem.deletionTimestamp ?: 0, + storageId = null, + privacyMode = privacyMode + )!! return SignalDatabase.distributionLists.getRecipientId(dlistId)!! } fun DistributionListTables.clearAllDataForBackupRestore() { - writableDatabase - .deleteAll(DistributionListTables.ListTable.TABLE_NAME) - - writableDatabase - .deleteAll(DistributionListTables.MembershipTable.TABLE_NAME) + writableDatabase.deleteAll(DistributionListTables.ListTable.TABLE_NAME) + writableDatabase.deleteAll(DistributionListTables.MembershipTable.TABLE_NAME) } private fun DistributionListPrivacyMode.toBackupPrivacyMode(): BackupDistributionList.PrivacyMode { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt index 1ac87d7907..d0c1c96fe7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ThreadTableBackupExtensions.kt @@ -45,6 +45,7 @@ fun ThreadTable.getThreadsForBackup(): ChatIterator { ${RecipientTable.TABLE_NAME}.${RecipientTable.CUSTOM_CHAT_COLORS_ID} FROM ${ThreadTable.TABLE_NAME} LEFT OUTER JOIN ${RecipientTable.TABLE_NAME} ON ${ThreadTable.TABLE_NAME}.${ThreadTable.RECIPIENT_ID} = ${RecipientTable.TABLE_NAME}.${RecipientTable.ID} + WHERE ${ThreadTable.ACTIVE} = 1 """ val cursor = readableDatabase.query(query) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt index fa7c5fc443..1e5c7b9002 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/AccountDataProcessor.kt @@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences import org.whispersystems.signalservice.api.push.UsernameLinkComponents import org.whispersystems.signalservice.api.subscriptions.SubscriberId import org.whispersystems.signalservice.api.util.UuidUtil +import org.whispersystems.signalservice.api.util.toByteArray import java.util.Currency object AccountDataProcessor { @@ -48,6 +49,15 @@ object AccountDataProcessor { familyName = selfRecord.signalProfileName.familyName, avatarUrlPath = selfRecord.signalProfileAvatar ?: "", username = selfRecord.username, + usernameLink = if (signalStore.accountValues.usernameLink != null) { + AccountData.UsernameLink( + entropy = signalStore.accountValues.usernameLink?.entropy?.toByteString() ?: EMPTY, + serverId = signalStore.accountValues.usernameLink?.serverId?.toByteArray()?.toByteString() ?: EMPTY, + color = signalStore.miscValues.usernameQrCodeColorScheme.toBackupUsernameColor() ?: AccountData.UsernameLink.Color.BLUE + ) + } else { + null + }, accountSettings = AccountData.AccountSettings( storyViewReceiptsEnabled = signalStore.storyValues.viewedReceiptsEnabled, typingIndicators = TextSecurePreferences.isTypingIndicatorsEnabled(context), @@ -73,12 +83,6 @@ object AccountDataProcessor { ) } - private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData { - val subscriberId = subscriberId.bytes.toByteString() - val currencyCode = currency.currencyCode - return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled) - } - fun import(accountData: AccountData, selfId: RecipientId) { SignalDatabase.recipients.restoreSelfFromBackup(accountData, selfId) @@ -181,4 +185,23 @@ object AccountDataProcessor { else -> UsernameQrCodeColorScheme.Blue } } + + private fun UsernameQrCodeColorScheme.toBackupUsernameColor(): AccountData.UsernameLink.Color { + return when (this) { + UsernameQrCodeColorScheme.Blue -> AccountData.UsernameLink.Color.BLUE + UsernameQrCodeColorScheme.White -> AccountData.UsernameLink.Color.WHITE + UsernameQrCodeColorScheme.Grey -> AccountData.UsernameLink.Color.GREY + UsernameQrCodeColorScheme.Tan -> AccountData.UsernameLink.Color.OLIVE + UsernameQrCodeColorScheme.Green -> AccountData.UsernameLink.Color.GREEN + UsernameQrCodeColorScheme.Orange -> AccountData.UsernameLink.Color.ORANGE + UsernameQrCodeColorScheme.Pink -> AccountData.UsernameLink.Color.PINK + UsernameQrCodeColorScheme.Purple -> AccountData.UsernameLink.Color.PURPLE + } + } + + private fun InAppPaymentSubscriberRecord.toSubscriberData(manuallyCancelled: Boolean): AccountData.SubscriberData { + val subscriberId = subscriberId.bytes.toByteString() + val currencyCode = currency.currencyCode + return AccountData.SubscriberData(subscriberId = subscriberId, currencyCode = currencyCode, manuallyCancelled = manuallyCancelled) + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt index 8ce704c43b..97b31d9331 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/processor/RecipientBackupProcessor.kt @@ -32,6 +32,7 @@ object RecipientBackupProcessor { val selfId = db.recipientTable.getByAci(signalStore.accountValues.aci!!).get().toLong() val releaseChannelId = signalStore.releaseChannelValues.releaseChannelRecipientId if (releaseChannelId != null) { + state.recipientIds.add(releaseChannelId.toLong()) emitter.emit( Frame( recipient = BackupRecipient( diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt index fc17946092..3371f81f42 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/backup/InternalBackupPlaygroundViewModel.kt @@ -57,7 +57,7 @@ class InternalBackupPlaygroundViewModel : ViewModel() { _state.value = _state.value.copy(backupState = BackupState.EXPORT_IN_PROGRESS) val plaintext = _state.value.plaintext - disposables += Single.fromCallable { BackupRepository.export(plaintext = plaintext) } + disposables += Single.fromCallable { BackupRepository.debugExport(plaintext = plaintext) } .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { data -> diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 0688d27ea2..c2e0c33471 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -186,7 +186,7 @@ public final class ConversationUpdateItem extends FrameLayout shouldCollapse(messageRecord, nextMessageRecord), hasWallpaper); - presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isBoostRequest()); + presentActionButton(hasWallpaper, conversationMessage.getMessageRecord().isReleaseChannelDonationRequest()); updateSelectedState(); } @@ -538,7 +538,7 @@ public final class ConversationUpdateItem extends FrameLayout eventListener.onBlockJoinRequest(conversationMessage.getMessageRecord().getFromRecipient()); } }); - } else if (conversationMessage.getMessageRecord().isBoostRequest()) { + } else if (conversationMessage.getMessageRecord().isReleaseChannelDonationRequest()) { actionButton.setVisibility(VISIBLE); actionButton.setOnClickListener(v -> { if (batchSelected.isEmpty() && eventListener != null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index 5f937e78fd..d83d05e331 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -650,7 +650,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind return emphasisAdded(context, context.getString(R.string.ThreadRecord_message_could_not_be_processed), defaultTint); } else if (MessageTypes.isProfileChange(thread.getType())) { return emphasisAdded(context, "", defaultTint); - } else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isBoostRequest(thread.getType())) { + } else if (MessageTypes.isChangeNumber(thread.getType()) || MessageTypes.isReleaseChannelDonationRequest(thread.getType())) { return emphasisAdded(context, "", defaultTint); } else if (MessageTypes.isBadDecryptType(thread.getType())) { return emphasisAdded(context, context.getString(R.string.ThreadRecord_delivery_issue), defaultTint); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt index e39d9f3774..5a6e42edcc 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/DistributionListTables.kt @@ -411,11 +411,8 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign } return when (privacyMode) { - DistributionListPrivacyMode.ALL -> { - SignalDatabase.recipients - .getSignalContacts(false)!! - .readToList { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) } - } + DistributionListPrivacyMode.ALL -> emptyList() + DistributionListPrivacyMode.ONLY_WITH -> rawMembers DistributionListPrivacyMode.ALL_EXCEPT -> { SignalDatabase.recipients .getSignalContacts(false)!! @@ -424,7 +421,6 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign mapper = { it.requireObject(RecipientTable.ID, RecipientId.SERIALIZER) } ) } - DistributionListPrivacyMode.ONLY_WITH -> rawMembers } } @@ -477,7 +473,7 @@ class DistributionListTables constructor(context: Context?, databaseHelper: Sign } } - private fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode { + fun getPrivacyMode(listId: DistributionListId): DistributionListPrivacyMode { return readableDatabase .select(ListTable.PRIVACY_MODE) .from(ListTable.TABLE_NAME) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt index e98b91b340..ff55d50b4e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTable.kt @@ -420,7 +420,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat ${MessageTypes.PROFILE_CHANGE_TYPE}, ${MessageTypes.GV1_MIGRATION_TYPE}, ${MessageTypes.CHANGE_NUMBER_TYPE}, - ${MessageTypes.BOOST_REQUEST_TYPE}, + ${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE}, ${MessageTypes.SMS_EXPORT_TYPE} ) ORDER BY $DATE_RECEIVED DESC LIMIT 1 @@ -1237,7 +1237,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat DATE_RECEIVED to System.currentTimeMillis(), DATE_SENT to System.currentTimeMillis(), READ to 1, - TYPE to MessageTypes.BOOST_REQUEST_TYPE, + TYPE to MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE, THREAD_ID to threadId, BODY to null ) @@ -1882,7 +1882,7 @@ open class MessageTable(context: Context?, databaseHelper: SignalDatabase) : Dat $TYPE != ${MessageTypes.PROFILE_CHANGE_TYPE} AND $TYPE != ${MessageTypes.CHANGE_NUMBER_TYPE} AND $TYPE != ${MessageTypes.SMS_EXPORT_TYPE} AND - $TYPE != ${MessageTypes.BOOST_REQUEST_TYPE} AND + $TYPE != ${MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE} AND $TYPE & ${MessageTypes.GROUP_V2_LEAVE_BITS} != ${MessageTypes.GROUP_V2_LEAVE_BITS} AND $TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_REPORTED_SPAM} AND $TYPE & ${MessageTypes.SPECIAL_TYPES_MASK} != ${MessageTypes.SPECIAL_TYPE_MESSAGE_REQUEST_ACCEPTED} diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTypes.java b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTypes.java index a6cefcb948..416f731a90 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MessageTypes.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MessageTypes.java @@ -33,24 +33,24 @@ public interface MessageTypes { // Base Types long BASE_TYPE_MASK = 0x1F; - long INCOMING_AUDIO_CALL_TYPE = 1; - long OUTGOING_AUDIO_CALL_TYPE = 2; - long MISSED_AUDIO_CALL_TYPE = 3; - long JOINED_TYPE = 4; - long UNSUPPORTED_MESSAGE_TYPE = 5; - long INVALID_MESSAGE_TYPE = 6; - long PROFILE_CHANGE_TYPE = 7; - long MISSED_VIDEO_CALL_TYPE = 8; - long GV1_MIGRATION_TYPE = 9; - long INCOMING_VIDEO_CALL_TYPE = 10; - long OUTGOING_VIDEO_CALL_TYPE = 11; - long GROUP_CALL_TYPE = 12; - long BAD_DECRYPT_TYPE = 13; - long CHANGE_NUMBER_TYPE = 14; - long BOOST_REQUEST_TYPE = 15; - long THREAD_MERGE_TYPE = 16; - long SMS_EXPORT_TYPE = 17; - long SESSION_SWITCHOVER_TYPE = 18; + long INCOMING_AUDIO_CALL_TYPE = 1; + long OUTGOING_AUDIO_CALL_TYPE = 2; + long MISSED_AUDIO_CALL_TYPE = 3; + long JOINED_TYPE = 4; + long UNSUPPORTED_MESSAGE_TYPE = 5; + long INVALID_MESSAGE_TYPE = 6; + long PROFILE_CHANGE_TYPE = 7; + long MISSED_VIDEO_CALL_TYPE = 8; + long GV1_MIGRATION_TYPE = 9; + long INCOMING_VIDEO_CALL_TYPE = 10; + long OUTGOING_VIDEO_CALL_TYPE = 11; + long GROUP_CALL_TYPE = 12; + long BAD_DECRYPT_TYPE = 13; + long CHANGE_NUMBER_TYPE = 14; + long RELEASE_CHANNEL_DONATION_REQUEST_TYPE = 15; + long THREAD_MERGE_TYPE = 16; + long SMS_EXPORT_TYPE = 17; + long SESSION_SWITCHOVER_TYPE = 18; long BASE_INBOX_TYPE = 20; long BASE_OUTBOX_TYPE = 21; @@ -59,7 +59,7 @@ public interface MessageTypes { long BASE_SENT_FAILED_TYPE = 24; long BASE_PENDING_SECURE_SMS_FALLBACK = 25; long BASE_PENDING_INSECURE_SMS_FALLBACK = 26; - long BASE_DRAFT_TYPE = 27; + long BASE_DRAFT_TYPE = 27; long[] OUTGOING_MESSAGE_TYPES = { BASE_OUTBOX_TYPE, BASE_SENT_TYPE, BASE_SENDING_TYPE, BASE_SENT_FAILED_TYPE, @@ -351,8 +351,8 @@ public interface MessageTypes { return type == CHANGE_NUMBER_TYPE; } - static boolean isBoostRequest(long type) { - return type == BOOST_REQUEST_TYPE; + static boolean isReleaseChannelDonationRequest(long type) { + return type == RELEASE_CHANNEL_DONATION_REQUEST_TYPE; } static boolean isSmsExport(long type) { 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 8647b4a7d7..a16e0024ab 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadTable.kt @@ -1788,10 +1788,6 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa deactivateThread(query = null) } - private fun SQLiteDatabase.deactivateThread(threadId: Long) { - deactivateThread(SqlUtil.Query("$ID = ?", SqlUtil.buildArgs(threadId))) - } - private fun SQLiteDatabase.deactivateThread(query: SqlUtil.Query?) { val contentValues = contentValuesOf( DATE to 0, @@ -1820,13 +1816,13 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa ) if (query != null) { - writableDatabase + this .update(TABLE_NAME) .values(contentValues) .where(query.where, query.whereArgs) .run() } else { - writableDatabase + this .updateAll(TABLE_NAME) .values(contentValues) .run() @@ -1979,7 +1975,7 @@ class ThreadTable(context: Context, databaseHelper: SignalDatabase) : DatabaseTa return MessageTypes.isProfileChange(type) || MessageTypes.isGroupV1MigrationEvent(type) || MessageTypes.isChangeNumber(type) || - MessageTypes.isBoostRequest(type) || + MessageTypes.isReleaseChannelDonationRequest(type) || MessageTypes.isGroupV2LeaveOnly(type) || MessageTypes.isThreadMergeType(type) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java index 54c58da417..affb1d8a80 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/DisplayRecord.java @@ -196,8 +196,8 @@ public abstract class DisplayRecord { return MessageTypes.isChangeNumber(type); } - public boolean isBoostRequest() { - return MessageTypes.isBoostRequest(type); + public boolean isReleaseChannelDonationRequest() { + return MessageTypes.isReleaseChannelDonationRequest(type); } public int getDeliveryStatus() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index b3de9666ff..a339d9121f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -232,7 +232,7 @@ public abstract class MessageRecord extends DisplayRecord { return getProfileChangeDescription(context); } else if (isChangeNumber()) { return fromRecipient(getFromRecipient(), r -> context.getString(R.string.MessageRecord_s_changed_their_phone_number, r.getDisplayName(context)), R.drawable.ic_phone_16); - } else if (isBoostRequest()) { + } else if (isReleaseChannelDonationRequest()) { return staticUpdateDescription(context.getString(R.string.MessageRecord_like_this_new_feature_help_support_signal_with_a_one_time_donation), 0); } else if (isEndSession()) { if (isOutgoing()) return staticUpdateDescription(context.getString(R.string.SmsMessageRecord_secure_session_reset), R.drawable.ic_update_info_16); @@ -684,7 +684,7 @@ public abstract class MessageRecord extends DisplayRecord { return isGroupAction() || isJoined() || isExpirationTimerUpdate() || isCallLog() || isEndSession() || isIdentityUpdate() || isIdentityVerified() || isIdentityDefault() || isProfileChange() || isGroupV1MigrationEvent() || isChatSessionRefresh() || isBadDecryptType() || - isChangeNumber() || isBoostRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() || + isChangeNumber() || isReleaseChannelDonationRequest() || isThreadMergeEventType() || isSmsExportType() || isSessionSwitchoverEventType() || isPaymentsRequestToActivate() || isPaymentsActivated() || isReportedSpam() || isMessageRequestAccepted(); } diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index b516ce25a0..691316c60c 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -740,13 +740,14 @@ message SimpleChatUpdate { IDENTITY_VERIFIED = 3; IDENTITY_DEFAULT = 4; // marking as unverified CHANGE_NUMBER = 5; - BOOST_REQUEST = 6; + RELEASE_CHANNEL_DONATION_REQUEST = 6; END_SESSION = 7; CHAT_SESSION_REFRESH = 8; BAD_DECRYPT = 9; PAYMENTS_ACTIVATED = 10; PAYMENT_ACTIVATION_REQUEST = 11; UNSUPPORTED_PROTOCOL_MESSAGE = 12; + REPORTED_SPAM = 13; } Type type = 1; diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt b/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt index e81e6e3ffe..a239d3e31c 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/database/SmsDatabaseTest.kt @@ -81,7 +81,7 @@ class SmsDatabaseTest { TestSms.insert(db, type = MessageTypes.CHANGE_NUMBER_TYPE) assertFalse(messageTable.hasMeaningfulMessage(1)) - TestSms.insert(db, type = MessageTypes.BOOST_REQUEST_TYPE) + TestSms.insert(db, type = MessageTypes.RELEASE_CHANNEL_DONATION_REQUEST_TYPE) assertFalse(messageTable.hasMeaningfulMessage(1)) TestSms.insert(db, type = MessageTypes.SMS_EXPORT_TYPE)