From 4304ae2a96682e009296c7fda219fa46a4565b40 Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Mon, 21 Apr 2025 16:43:19 -0400 Subject: [PATCH] Add notification profile id for backupsv2. --- .../notification_profile_00.binproto | Bin 860 -> 878 bytes .../notification_profile_01.binproto | Bin 877 -> 895 bytes .../notification_profile_02.binproto | Bin 882 -> 900 bytes .../notification_profile_03.binproto | Bin 871 -> 889 bytes .../notification_profile_04.binproto | Bin 890 -> 908 bytes .../notification_profile_05.binproto | Bin 888 -> 906 bytes .../notification_profile_06.binproto | Bin 876 -> 894 bytes .../notification_profile_07.binproto | Bin 891 -> 909 bytes .../notification_profile_08.binproto | Bin 874 -> 892 bytes .../notification_profile_09.binproto | Bin 865 -> 883 bytes .../notification_profile_10.binproto | Bin 890 -> 908 bytes .../notification_profile_11.binproto | Bin 886 -> 904 bytes .../securesms/backup/v2/ArchiveErrorCases.kt | 4 +++ .../processor/NotificationProfileProcessor.kt | 14 +++++++-- .../database/NotificationProfileTables.kt | 14 +++++++-- .../helpers/SignalDatabaseMigrations.kt | 6 ++-- .../V271_AddNotificationProfileIdColumn.kt | 28 +++++++++++++++++ .../profiles/NotificationProfile.kt | 3 +- .../profiles/NotificationProfileId.kt | 29 ++++++++++++++++++ app/src/main/protowire/Backup.proto | 20 ++++++++++++ .../profiles/NotificationProfilesTest.kt | 6 ++-- 21 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V271_AddNotificationProfileIdColumn.kt create mode 100644 app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfileId.kt diff --git a/app/src/androidTest/assets/backupTests/notification_profile_00.binproto b/app/src/androidTest/assets/backupTests/notification_profile_00.binproto index 0509b3220a10c111410833deff54f2b38c56d49b..ba868415386b877b93420883a6c83af539adfe4a 100644 GIT binary patch delta 34 qcmcb^_Kt0X4KuT$mHuRVW?7*m0sZw!JVlzB3>il@%n8dZdkO%!tqZ#V delta 15 WcmaFIc86_)4KuU6mF#4DW?29!)&wyC diff --git a/app/src/androidTest/assets/backupTests/notification_profile_01.binproto b/app/src/androidTest/assets/backupTests/notification_profile_01.binproto index 8b86f53682aa5e074f211509eca5650d63cd6ba0..4bc4b9665762cfbc8c873bdad9d2332c61351696 100644 GIT binary patch delta 34 qcmaFM_MdHo4KuTomBVCvW<8-K0sZw!JVlzB3>il@%n8dZdkO%-wG0pd delta 15 Wcmey*_Lgmf4KuTWmEL4~W<3Bbj|7GQ diff --git a/app/src/androidTest/assets/backupTests/notification_profile_02.binproto b/app/src/androidTest/assets/backupTests/notification_profile_02.binproto index 26a5aa9ef092d0038c6e6131367c9d5499ba5c65..ff058e2ff77b0160d49535f58ebcf6309f9d4a84 100644 GIT binary patch delta 34 qcmeyw*22EQhMC#j%5}0mv$0T;fd2XI5JF diff --git a/app/src/androidTest/assets/backupTests/notification_profile_03.binproto b/app/src/androidTest/assets/backupTests/notification_profile_03.binproto index 9c6f4e3ac73f5a2082871131a06c58720b0ab4fa..de20ced25ed45ac8073e008eb3d1fbe61e8a229b 100644 GIT binary patch delta 34 qcmaFP_LFUc4KuTimDOZ>W(}bv0sZw!JVlzB3>il@%n8dZdkO%)g$wck delta 15 Wcmey#_MB~l4KuTrmBwUyW(@!JZ diff --git a/app/src/androidTest/assets/backupTests/notification_profile_06.binproto b/app/src/androidTest/assets/backupTests/notification_profile_06.binproto index 954f60944b1d5052e093377781c610974221d2d6..636634a2d99991e93f90c2cd7d4f8442331cc401 100644 GIT binary patch delta 34 qcmaFE_K$6Y4KuT&mHlLUW?i8q0sZw!JVlzB3>il@%n8dZdkO%-Ees0) delta 15 Wcmeyz_J(bP4KuU8mF{GFW?cX*W(0u% diff --git a/app/src/androidTest/assets/backupTests/notification_profile_07.binproto b/app/src/androidTest/assets/backupTests/notification_profile_07.binproto index 33b5feb1de81a38c73a1c8a8e3fd70ac7a68c859..4da8e55dc8d1c427d7924ca18e947626749a0fb1 100644 GIT binary patch delta 34 qcmey(*2})ZhM76Q%5SngvyD)afd2Xil@%n8dZdkO%+9Si{g delta 15 Wcmeyv_KIzT4KuT@mG)$NW^Dj06a;qw diff --git a/app/src/androidTest/assets/backupTests/notification_profile_09.binproto b/app/src/androidTest/assets/backupTests/notification_profile_09.binproto index cd608e7c1d6bc0273d6e359757e036f7d5d5bf77..e76246a52fc4a3f5a21f9d50e5fc46b60b6b592d 100644 GIT binary patch delta 34 qcmaFJ_L*&i4KuTumC0m#W+kB{0sZw!JVlzB3>il@%n8dZdkO%%RSVPr delta 15 Wcmey&_K Boolean): Frame { val profile = NotificationProfileProto( + id = UuidUtil.toByteArray(this.notificationProfileId.uuid).toByteString(), name = this.name, emoji = this.emoji.takeIf { it.isNotBlank() }, color = this.color.colorInt(), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt b/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt index 1cbb931d10..885a4f58ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/NotificationProfileTables.kt @@ -11,12 +11,14 @@ import org.signal.core.util.logging.Log import org.signal.core.util.requireBoolean import org.signal.core.util.requireInt import org.signal.core.util.requireLong +import org.signal.core.util.requireNonNullString import org.signal.core.util.requireString import org.signal.core.util.toInt import org.signal.core.util.update import org.thoughtcrime.securesms.conversation.colors.AvatarColor import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.notifications.profiles.NotificationProfile +import org.thoughtcrime.securesms.notifications.profiles.NotificationProfileId import org.thoughtcrime.securesms.notifications.profiles.NotificationProfileSchedule import org.thoughtcrime.securesms.recipients.RecipientId import java.time.DayOfWeek @@ -46,6 +48,7 @@ class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase const val CREATED_AT = "created_at" const val ALLOW_ALL_CALLS = "allow_all_calls" const val ALLOW_ALL_MENTIONS = "allow_all_mentions" + const val NOTIFICATION_PROFILE_ID = "notification_profile_id" val CREATE_TABLE = """ CREATE TABLE $TABLE_NAME ( @@ -55,7 +58,8 @@ class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase $COLOR TEXT NOT NULL, $CREATED_AT INTEGER NOT NULL, $ALLOW_ALL_CALLS INTEGER NOT NULL DEFAULT 0, - $ALLOW_ALL_MENTIONS INTEGER NOT NULL DEFAULT 0 + $ALLOW_ALL_MENTIONS INTEGER NOT NULL DEFAULT 0, + $NOTIFICATION_PROFILE_ID TEXT DEFAULT NULL ) """ } @@ -110,12 +114,14 @@ class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase db.beginTransaction() try { + val notificationProfileId = NotificationProfileId.generate() val profileValues = ContentValues().apply { put(NotificationProfileTable.NAME, name) put(NotificationProfileTable.EMOJI, emoji) put(NotificationProfileTable.COLOR, color.serialize()) put(NotificationProfileTable.CREATED_AT, createdAt) put(NotificationProfileTable.ALLOW_ALL_CALLS, 1) + put(NotificationProfileTable.NOTIFICATION_PROFILE_ID, notificationProfileId.serialize()) } val profileId = db.insert(NotificationProfileTable.TABLE_NAME, null, profileValues) @@ -140,7 +146,8 @@ class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase emoji = emoji, createdAt = createdAt, schedule = getProfileSchedule(profileId), - allowAllCalls = true + allowAllCalls = true, + notificationProfileId = notificationProfileId ) ) } finally { @@ -323,7 +330,8 @@ class NotificationProfileTables(context: Context, databaseHelper: SignalDatabase allowAllCalls = cursor.requireBoolean(NotificationProfileTable.ALLOW_ALL_CALLS), allowAllMentions = cursor.requireBoolean(NotificationProfileTable.ALLOW_ALL_MENTIONS), schedule = getProfileSchedule(profileId), - allowedMembers = getProfileAllowedMembers(profileId) + allowedMembers = getProfileAllowedMembers(profileId), + notificationProfileId = NotificationProfileId.from(cursor.requireNonNullString(NotificationProfileTable.NOTIFICATION_PROFILE_ID)) ) } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt index 019a2479a4..966299bc91 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SignalDatabaseMigrations.kt @@ -125,6 +125,7 @@ import org.thoughtcrime.securesms.database.helpers.migration.V267_FixGroupInvita import org.thoughtcrime.securesms.database.helpers.migration.V268_FixInAppPaymentsErrorStateConsistency import org.thoughtcrime.securesms.database.helpers.migration.V269_BackupMediaSnapshotChanges import org.thoughtcrime.securesms.database.helpers.migration.V270_FixChatFolderColumnsForStorageSync +import org.thoughtcrime.securesms.database.helpers.migration.V271_AddNotificationProfileIdColumn import org.thoughtcrime.securesms.database.SQLiteDatabase as SignalSqliteDatabase /** @@ -255,10 +256,11 @@ object SignalDatabaseMigrations { 267 to V267_FixGroupInvitationDeclinedUpdate, 268 to V268_FixInAppPaymentsErrorStateConsistency, 269 to V269_BackupMediaSnapshotChanges, - 270 to V270_FixChatFolderColumnsForStorageSync + 270 to V270_FixChatFolderColumnsForStorageSync, + 271 to V271_AddNotificationProfileIdColumn ) - const val DATABASE_VERSION = 270 + const val DATABASE_VERSION = 271 @JvmStatic fun migrate(context: Application, db: SignalSqliteDatabase, oldVersion: Int, newVersion: Int) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V271_AddNotificationProfileIdColumn.kt b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V271_AddNotificationProfileIdColumn.kt new file mode 100644 index 0000000000..6be622c3c7 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/migration/V271_AddNotificationProfileIdColumn.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.database.helpers.migration + +import android.app.Application +import org.signal.core.util.readToList +import org.signal.core.util.requireLong +import org.thoughtcrime.securesms.database.SQLiteDatabase +import java.util.UUID + +/** + * Add notification_profile_id column to Notification Profiles to support backups. + */ +@Suppress("ClassName") +object V271_AddNotificationProfileIdColumn : SignalDatabaseMigration { + override fun migrate(context: Application, db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { + db.execSQL("ALTER TABLE notification_profile ADD COLUMN notification_profile_id TEXT DEFAULT NULL") + + db.rawQuery("SELECT _id FROM notification_profile") + .readToList { it.requireLong("_id") } + .forEach { id -> + db.execSQL("UPDATE notification_profile SET notification_profile_id = '${UUID.randomUUID()}' WHERE _id = $id") + } + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt index 822bf7e26e..61bfa9c8da 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfile.kt @@ -12,7 +12,8 @@ data class NotificationProfile( val allowAllCalls: Boolean = true, val allowAllMentions: Boolean = false, val schedule: NotificationProfileSchedule, - val allowedMembers: Set = emptySet() + val allowedMembers: Set = emptySet(), + val notificationProfileId: NotificationProfileId ) : Comparable { fun isRecipientAllowed(id: RecipientId): Boolean { diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfileId.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfileId.kt new file mode 100644 index 0000000000..2888f6cb60 --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfileId.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2025 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.thoughtcrime.securesms.notifications.profiles + +import org.signal.core.util.DatabaseId +import org.whispersystems.signalservice.api.util.UuidUtil +import java.util.UUID + +/** + * Typed wrapper for notification profile uuid. + */ +data class NotificationProfileId(val uuid: UUID) : DatabaseId { + companion object { + fun from(id: String): NotificationProfileId { + return NotificationProfileId(UuidUtil.parseOrThrow(id)) + } + + fun generate(): NotificationProfileId { + return NotificationProfileId(UUID.randomUUID()) + } + } + + override fun serialize(): String { + return uuid.toString() + } +} diff --git a/app/src/main/protowire/Backup.proto b/app/src/main/protowire/Backup.proto index be94e934ea..5ac5ceb260 100644 --- a/app/src/main/protowire/Backup.proto +++ b/app/src/main/protowire/Backup.proto @@ -724,11 +724,30 @@ message FilePointer { message InvalidAttachmentLocator { } + // References attachments in a local encrypted backup. + // Importers should first attempt to read the file from the local backup, + // and on failure fallback to backup and transit cdn if possible. + message LocalLocator { + string mediaName = 1; + // Separate key used to encrypt this file for the local backup. + // Generally required. Missing field indicates attachment was not + // available locally when the backup was generated, but remote + // backup or transit info was available. + optional bytes localKey = 2; + bytes remoteKey = 3; + bytes remoteDigest = 4; + uint32 size = 5; + optional uint32 backupCdnNumber = 6; + optional string transitCdnKey = 7; + optional uint32 transitCdnNumber = 8; + } + // If unset, importers should consider it to be an InvalidAttachmentLocator without throwing an error. oneof locator { BackupLocator backupLocator = 1; AttachmentLocator attachmentLocator = 2; InvalidAttachmentLocator invalidAttachmentLocator = 3; + LocalLocator localLocator = 12; } optional string contentType = 4; @@ -1291,6 +1310,7 @@ message NotificationProfile { uint32 scheduleStartTime = 9; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345) uint32 scheduleEndTime = 10; // 24-hour clock int, 0000-2359 (e.g., 15, 900, 1130, 2345) repeated DayOfWeek scheduleDaysEnabled = 11; + bytes id = 12; // should be 16 bytes } message ChatFolder { diff --git a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt index 93e45b2621..6c8d80f23d 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt +++ b/app/src/test/java/org/thoughtcrime/securesms/notifications/profiles/NotificationProfilesTest.kt @@ -41,7 +41,8 @@ class NotificationProfilesTest { "first", "", createdAt = 1000L, - schedule = NotificationProfileSchedule(1) + schedule = NotificationProfileSchedule(1), + notificationProfileId = NotificationProfileId.generate() ) private val second = NotificationProfile( @@ -49,7 +50,8 @@ class NotificationProfilesTest { "second", "", createdAt = 2000L, - schedule = NotificationProfileSchedule(2) + schedule = NotificationProfileSchedule(2), + notificationProfileId = NotificationProfileId.generate() ) private lateinit var notificationProfileValues: NotificationProfileValues