Inline deleteSync and SSREv2 capabilities.

This commit is contained in:
Greyson Parrelli
2025-08-15 12:23:02 -04:00
committed by Jeffrey Starke
parent 8df8cdf28d
commit 1c7f6a68e4
12 changed files with 33 additions and 84 deletions

View File

@@ -11,9 +11,7 @@ object AppCapabilities {
fun getCapabilities(storageCapable: Boolean): AccountAttributes.Capabilities {
return AccountAttributes.Capabilities(
storage = storageCapable,
deleteSync = true,
versionedExpirationTimer = true,
storageServiceEncryptionV2 = true,
attachmentBackfill = true
)
}

View File

@@ -29,7 +29,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -74,20 +73,14 @@ class InternalStorageServicePlaygroundFragment : ComposeFragment() {
val storageRecords by viewModel.storageRecords
val storageInsights by viewModel.storageInsights
val oneOffEvent by viewModel.oneOffEvents
var forceSsreToggled by remember { mutableStateOf(SignalStore.internal.forceSsre2Capability) }
Screen(
onBackPressed = { findNavController().popBackStack() },
manifest = manifest,
storageRecords = storageRecords,
storageInsights = storageInsights,
oneOffEvent = oneOffEvent,
forceSsreCapability = forceSsreToggled,
onForceSsreToggled = { checked ->
SignalStore.internal.forceSsre2Capability = checked
forceSsreToggled = checked
},
onViewTabSelected = { viewModel.onViewTabSelected() }
onViewTabSelected = { viewModel.onViewTabSelected() },
onBackPressed = { findNavController().popBackStack() }
)
}
}
@@ -98,9 +91,7 @@ fun Screen(
manifest: SignalStorageManifest,
storageRecords: List<SignalStorageRecord>,
storageInsights: StorageInsights,
forceSsreCapability: Boolean,
oneOffEvent: OneOffEvent,
onForceSsreToggled: (Boolean) -> Unit = {},
onViewTabSelected: () -> Unit = {},
onBackPressed: () -> Unit = {}
) {
@@ -141,10 +132,7 @@ fun Screen(
) { contentPadding ->
Surface(modifier = Modifier.padding(contentPadding)) {
when (tabIndex) {
0 -> ToolScreen(
forceSsreCapability = forceSsreCapability,
onForceSsreToggled = onForceSsreToggled
)
0 -> ToolScreen()
1 -> ViewScreen(
manifest = manifest,
storageRecords = storageRecords,
@@ -157,10 +145,7 @@ fun Screen(
}
@Composable
fun ToolScreen(
forceSsreCapability: Boolean,
onForceSsreToggled: (Boolean) -> Unit = {}
) {
fun ToolScreen() {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.fillMaxWidth()
@@ -184,12 +169,6 @@ fun ToolScreen(
ActionRow("Clear initial master key", "Sets it to null.") {
SignalStore.svr.masterKeyForInitialDataRestore = null
}
Rows.ToggleRow(
text = "Force SSRE2 Capability",
checked = forceSsreCapability,
onCheckChanged = onForceSsreToggled
)
}
}
@@ -381,7 +360,6 @@ private fun RunButton(onClick: () -> Unit) {
fun ScreenPreview() {
Previews.Preview {
Screen(
forceSsreCapability = true,
manifest = SignalStorageManifest.EMPTY,
storageRecords = emptyList(),
storageInsights = StorageInsights(),

View File

@@ -7,12 +7,7 @@ package org.thoughtcrime.securesms.components.settings.conversation
import androidx.annotation.WorkerThread
import androidx.compose.runtime.Immutable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.withStyle
import org.signal.core.util.Base64
import org.signal.core.util.Hex
import org.thoughtcrime.securesms.components.settings.app.subscription.InAppPaymentsRepository
@@ -102,17 +97,19 @@ data class InternalConversationSettingsState(
} else {
val capabilities: RecipientRecord.Capabilities? = SignalDatabase.recipients.getCapabilities(recipient.id)
if (capabilities != null) {
val style: SpanStyle = when (capabilities.storageServiceEncryptionV2) {
Recipient.Capability.SUPPORTED -> SpanStyle(color = Color(0, 150, 0))
Recipient.Capability.NOT_SUPPORTED -> SpanStyle(color = Color.Red)
Recipient.Capability.UNKNOWN -> SpanStyle(fontStyle = FontStyle.Italic)
}
buildAnnotatedString {
withStyle(style = style) {
append("SSREv2")
}
}
AnnotatedString("No capabilities right now.")
// Left as an example in case we add one in the future
// val style: SpanStyle = when (capabilities.storageServiceEncryptionV2) {
// Recipient.Capability.SUPPORTED -> SpanStyle(color = Color(0, 150, 0))
// Recipient.Capability.NOT_SUPPORTED -> SpanStyle(color = Color.Red)
// Recipient.Capability.UNKNOWN -> SpanStyle(fontStyle = FontStyle.Italic)
// }
//
// buildAnnotatedString {
// withStyle(style = style) {
// append("SSREv2")
// }
// }
} else {
AnnotatedString("Recipient not found!")
}

View File

@@ -9,7 +9,6 @@ import android.content.Context
import android.database.Cursor
import com.google.protobuf.InvalidProtocolBufferException
import org.signal.core.util.Base64
import org.signal.core.util.Bitmask
import org.signal.core.util.logging.Log
import org.signal.core.util.optionalBlob
import org.signal.core.util.optionalBoolean
@@ -28,7 +27,6 @@ import org.thoughtcrime.securesms.badges.models.Badge
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
import org.thoughtcrime.securesms.conversation.colors.ChatColors
import org.thoughtcrime.securesms.database.IdentityTable.VerifiedStatus
import org.thoughtcrime.securesms.database.RecipientTable.Capabilities
import org.thoughtcrime.securesms.database.RecipientTable.RegisteredState
import org.thoughtcrime.securesms.database.model.DistributionListId
import org.thoughtcrime.securesms.database.model.RecipientRecord
@@ -175,8 +173,7 @@ object RecipientTableCursorUtil {
fun readCapabilities(cursor: Cursor): RecipientRecord.Capabilities {
val capabilities = cursor.requireLong(RecipientTable.CAPABILITIES)
return RecipientRecord.Capabilities(
rawBits = capabilities,
storageServiceEncryptionV2 = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH).toInt())
rawBits = capabilities
)
}

View File

@@ -119,14 +119,12 @@ data class RecipientRecord(
)
data class Capabilities(
val rawBits: Long,
val storageServiceEncryptionV2: Recipient.Capability
val rawBits: Long
) {
companion object {
@JvmField
val UNKNOWN = Capabilities(
rawBits = 0,
storageServiceEncryptionV2 = Recipient.Capability.UNKNOWN
rawBits = 0
)
}
}

View File

@@ -221,12 +221,6 @@ public class RefreshOwnProfileJob extends BaseJob {
Recipient selfSnapshot = Recipient.self();
SignalDatabase.recipients().setCapabilities(Recipient.self().getId(), capabilities);
if (selfSnapshot.getStorageServiceEncryptionV2Capability() == Recipient.Capability.NOT_SUPPORTED && capabilities.isStorageServiceEncryptionV2()) {
Log.i(TAG, "Transitioned to storageServiceEncryptionV2 capable. Notifying other devices and pushing to storage service with a recordIkm.");
AppDependencies.getJobManager().add(new MultiDeviceProfileContentUpdateJob());
AppDependencies.getJobManager().add(new StorageForcePushJob());
}
}
private void ensureUnidentifiedAccessCorrect(@Nullable String unidentifiedAccessVerifier, boolean universalUnidentifiedAccess) {

View File

@@ -124,13 +124,8 @@ class StorageForcePushJob private constructor(parameters: Parameters) : BaseJob(
inserts.addAll(newNotificationProfileInserts)
allNewStorageIds.addAll(newNotificationProfileStorageIds.values)
val recordIkm: RecordIkm? = if (Recipient.self().storageServiceEncryptionV2Capability.isSupported) {
Log.i(TAG, "Generating and including a new recordIkm.")
RecordIkm.generate()
} else {
Log.i(TAG, "SSRE2 not yet supported. Not including recordIkm.")
null
}
Log.i(TAG, "Generating and including a new recordIkm.")
val recordIkm: RecordIkm = RecordIkm.generate()
val manifest = SignalStorageManifest(newVersion, SignalStore.account.deviceId, recordIkm, allNewStorageIds)
StorageSyncValidations.validateForcePush(manifest, inserts, Recipient.self().fresh())

View File

@@ -359,7 +359,7 @@ class StorageSyncJob private constructor(parameters: Parameters, private var loc
Log.i(TAG, "We are up-to-date with the remote storage state.")
if (remoteManifest.recordIkm == null && Recipient.self().storageServiceEncryptionV2Capability.isSupported) {
if (remoteManifest.recordIkm == null) {
Log.w(TAG, "The SSRE2 capability is supported, but no recordIkm is set! Force pushing.")
AppDependencies.jobManager.add(StorageForcePushJob())
return false

View File

@@ -35,18 +35,18 @@ public final class LogSectionCapabilities implements LogSection {
RecipientRecord.Capabilities globalCapabilities = SignalDatabase.recipients().getCapabilities(self.getId());
StringBuilder builder = new StringBuilder().append("-- Local").append("\n")
.append("DeleteSync: ").append(localCapabilities.getDeleteSync()).append("\n")
.append("VersionedExpirationTimer: ").append(localCapabilities.getVersionedExpirationTimer()).append("\n")
.append("StorageServiceEncryptionV2: ").append(localCapabilities.getStorageServiceEncryptionV2()).append("\n")
.append("\n")
.append("-- Global").append("\n");
.append("-- Global").append("\n")
.append("None").append("\n");
if (globalCapabilities != null) {
builder.append("StorageServiceEncryptionV2: ").append(globalCapabilities.getStorageServiceEncryptionV2()).append("\n");
builder.append("\n");
} else {
builder.append("Self not found!");
}
// Left as an example for when we want to add new ones
// if (globalCapabilities != null) {
// builder.append("StorageServiceEncryptionV2: ").append(globalCapabilities.getStorageServiceEncryptionV2()).append("\n");
// builder.append("\n");
// } else {
// builder.append("Self not found!");
// }
return builder;
}

View File

@@ -327,10 +327,6 @@ class Recipient(
/** The notification channel, if both set and supported by the system. Otherwise null. */
val notificationChannel: String? = if (!NotificationChannels.supported()) null else notificationChannelValue
/** The user's capability to handle the new storage record encryption scheme. */
val storageServiceEncryptionV2Capability: Capability
get() = if (SignalStore.internal.forceSsre2Capability) Capability.SUPPORTED else capabilities.storageServiceEncryptionV2
/** The state around whether we can send sealed sender to this user. */
val sealedSenderAccessMode: SealedSenderAccessMode = if (pni.isPresent && pni == serviceId) {
SealedSenderAccessMode.DISABLED