Add capability for KT username syncs.
This commit is contained in:
parent
1f0c24a5d5
commit
1371663163
@ -139,7 +139,7 @@ class SignalActivityRule(private val othersCount: Int = 4, private val createGro
|
||||
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true))
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
|
||||
SignalDatabase.recipients.setProfileSharing(recipientId, true)
|
||||
SignalDatabase.recipients.markRegistered(recipientId, aci)
|
||||
val otherIdentity = IdentityKeyPair.generate()
|
||||
|
||||
@ -133,7 +133,7 @@ object TestUsers {
|
||||
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true))
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
|
||||
SignalDatabase.recipients.setProfileSharing(recipientId, true)
|
||||
SignalDatabase.recipients.markRegistered(recipientId, aci)
|
||||
val otherIdentity = IdentityKeyPair.generate()
|
||||
@ -157,7 +157,7 @@ object TestUsers {
|
||||
val recipientId = RecipientId.from(SignalServiceAddress(otherClient.serviceId, otherClient.e164))
|
||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, otherClient.profileKey)
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true))
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
|
||||
SignalDatabase.recipients.setProfileSharing(recipientId, true)
|
||||
SignalDatabase.recipients.markRegistered(recipientId, otherClient.serviceId)
|
||||
AppDependencies.protocolStore.aci().saveIdentity(SignalProtocolAddress(otherClient.serviceId.toString(), 1), otherClient.identityKeyPair.publicKey)
|
||||
|
||||
@ -13,7 +13,8 @@ object AppCapabilities {
|
||||
storage = storageCapable,
|
||||
versionedExpirationTimer = true,
|
||||
attachmentBackfill = true,
|
||||
spqr = true
|
||||
spqr = true,
|
||||
usernameChangeSyncMessage = false // TODO(michelle): Turn on once all clients support it and add a migration
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,12 @@ 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
|
||||
@ -98,18 +103,18 @@ data class InternalConversationSettingsState(
|
||||
val capabilities: RecipientRecord.Capabilities? = SignalDatabase.recipients.getCapabilities(recipient.id)
|
||||
if (capabilities != null) {
|
||||
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")
|
||||
// }
|
||||
// }
|
||||
// Always leave one as an example in case we add one in the future
|
||||
val style: SpanStyle = when (capabilities.usernameSyncMessages) {
|
||||
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("usernameSyncMessages")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AnnotatedString("Recipient not found!")
|
||||
}
|
||||
|
||||
@ -424,6 +424,7 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
fun maskCapabilitiesToLong(capabilities: SignalServiceProfile.Capabilities): Long {
|
||||
var value: Long = 0
|
||||
value = Bitmask.update(value, Capabilities.STORAGE_SERVICE_ENCRYPTION_V2, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStorageServiceEncryptionV2).serialize().toLong())
|
||||
value = Bitmask.update(value, Capabilities.USERNAME_SYNC_MESSAGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isUsernameSyncMessages).serialize().toLong())
|
||||
return value
|
||||
}
|
||||
}
|
||||
@ -4953,8 +4954,9 @@ open class RecipientTable(context: Context, databaseHelper: SignalDatabase) : Da
|
||||
// const val DELETE_SYNC = 9
|
||||
// const val VERSIONED_EXPIRATION_TIMER = 10
|
||||
const val STORAGE_SERVICE_ENCRYPTION_V2 = 11
|
||||
const val USERNAME_SYNC_MESSAGES = 12
|
||||
|
||||
// IMPORTANT: We cannot sore more than 32 capabilities in the bitmask.
|
||||
// IMPORTANT: We cannot store more than 32 capabilities in the bitmask.
|
||||
}
|
||||
|
||||
enum class VibrateState(val id: Int) {
|
||||
|
||||
@ -10,6 +10,7 @@ import android.database.Cursor
|
||||
import com.google.protobuf.InvalidProtocolBufferException
|
||||
import org.signal.core.models.ServiceId
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.Bitmask
|
||||
import org.signal.core.util.Util
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.optionalBlob
|
||||
@ -174,7 +175,8 @@ object RecipientTableCursorUtil {
|
||||
fun readCapabilities(cursor: Cursor): RecipientRecord.Capabilities {
|
||||
val capabilities = cursor.requireLong(RecipientTable.CAPABILITIES)
|
||||
return RecipientRecord.Capabilities(
|
||||
rawBits = capabilities
|
||||
rawBits = capabilities,
|
||||
usernameSyncMessages = Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientTable.Capabilities.USERNAME_SYNC_MESSAGES, RecipientTable.Capabilities.BIT_LENGTH).toInt())
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -121,12 +121,14 @@ data class RecipientRecord(
|
||||
)
|
||||
|
||||
data class Capabilities(
|
||||
val rawBits: Long
|
||||
val rawBits: Long,
|
||||
val usernameSyncMessages: Recipient.Capability
|
||||
) {
|
||||
companion object {
|
||||
@JvmField
|
||||
val UNKNOWN = Capabilities(
|
||||
rawBits = 0
|
||||
rawBits = 0,
|
||||
usernameSyncMessages = Recipient.Capability.UNKNOWN
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,11 +117,11 @@ class CheckKeyTransparencyJob private constructor(
|
||||
aciIdentityKey = SignalStore.account.aciIdentityKey.publicKey,
|
||||
e164 = recipient.e164!!,
|
||||
unidentifiedAccessKey = ProfileKeyUtil.profileKeyOrNull(recipient.profileKey).let { UnidentifiedAccess.deriveAccessKeyFrom(it) },
|
||||
usernameHash = SignalStore.account.username?.let { Username(it).hash },
|
||||
usernameHash = SignalStore.account.username?.let { Username(it).hash }.takeIf { Recipient.self().usernameSyncMessagesCapability.isSupported },
|
||||
keyTransparencyStore = KeyTransparencyStore
|
||||
)
|
||||
|
||||
Log.i(TAG, "Key transparency complete, result: $result")
|
||||
Log.i(TAG, "Key transparency complete, result: $result. Included username in check: ${Recipient.self().usernameSyncMessagesCapability.isSupported}")
|
||||
return when (result) {
|
||||
is RequestResult.Success -> {
|
||||
SignalStore.misc.hasKeyTransparencyFailure = false
|
||||
|
||||
@ -224,6 +224,7 @@ public final class JobManagerFactories {
|
||||
put(MultiDeviceStickerPackSyncJob.KEY, new MultiDeviceStickerPackSyncJob.Factory());
|
||||
put(MultiDeviceStorageSyncRequestJob.KEY, new MultiDeviceStorageSyncRequestJob.Factory());
|
||||
put(MultiDeviceSubscriptionSyncRequestJob.KEY, new MultiDeviceSubscriptionSyncRequestJob.Factory());
|
||||
put(MultiDeviceUsernameChangeSyncJob.KEY, new MultiDeviceUsernameChangeSyncJob.Factory());
|
||||
put(MultiDeviceVerifiedUpdateJob.KEY, new MultiDeviceVerifiedUpdateJob.Factory());
|
||||
put(MultiDeviceViewOnceOpenJob.KEY, new MultiDeviceViewOnceOpenJob.Factory());
|
||||
put(MultiDeviceViewedUpdateJob.KEY, new MultiDeviceViewedUpdateJob.Factory());
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.Job
|
||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil.pad
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException
|
||||
import org.whispersystems.signalservice.internal.push.Content
|
||||
import org.whispersystems.signalservice.internal.push.SyncMessage
|
||||
import java.io.IOException
|
||||
import java.util.Optional
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
/**
|
||||
* Sends a sync message to alert linked devices of a username change so they can reset KT.
|
||||
*/
|
||||
class MultiDeviceUsernameChangeSyncJob private constructor(
|
||||
parameters: Parameters
|
||||
) : Job(parameters) {
|
||||
|
||||
companion object {
|
||||
const val KEY = "MultiDeviceUsernameChangeSyncJob"
|
||||
private val TAG = Log.tag(MultiDeviceUsernameChangeSyncJob::class.java)
|
||||
|
||||
@WorkerThread
|
||||
@JvmStatic
|
||||
fun enqueueUsernameChangeSync() {
|
||||
if (!SignalStore.account.isMultiDevice) {
|
||||
return
|
||||
}
|
||||
|
||||
AppDependencies.jobManager.add(
|
||||
MultiDeviceUsernameChangeSyncJob(
|
||||
parameters = Parameters.Builder()
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setMaxAttempts(Parameters.UNLIMITED)
|
||||
.setLifespan(1.days.inWholeMilliseconds)
|
||||
.build()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun serialize(): ByteArray? = null
|
||||
|
||||
override fun getFactoryKey(): String = KEY
|
||||
|
||||
override fun run(): Result {
|
||||
if (!Recipient.self().isRegistered) {
|
||||
Log.w(TAG, "Not registered")
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
if (!SignalStore.account.isMultiDevice) {
|
||||
Log.w(TAG, "Not multi-device")
|
||||
return Result.failure()
|
||||
}
|
||||
|
||||
val syncMessageContent = Content(
|
||||
syncMessage = SyncMessage.Builder()
|
||||
.pad()
|
||||
.usernameChange(SyncMessage.UsernameChange())
|
||||
.build()
|
||||
)
|
||||
|
||||
return try {
|
||||
Log.d(TAG, "Sending username change sync")
|
||||
val success = AppDependencies.signalServiceMessageSender.sendSyncMessage(syncMessageContent, true, Optional.empty()).isSuccess
|
||||
if (success) {
|
||||
Result.success()
|
||||
} else {
|
||||
Log.w(TAG, "Unsuccessful username change send. Retrying.")
|
||||
Result.retry(defaultBackoff())
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.w(TAG, "Unable to send username change sync due to io exception", e)
|
||||
Result.retry(defaultBackoff())
|
||||
} catch (e: UntrustedIdentityException) {
|
||||
Log.w(TAG, "Unable to send username change sync due to untrusted exception", e)
|
||||
Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure() = Unit
|
||||
|
||||
class Factory : Job.Factory<MultiDeviceUsernameChangeSyncJob> {
|
||||
override fun create(parameters: Parameters, serializedData: ByteArray?): MultiDeviceUsernameChangeSyncJob {
|
||||
return MultiDeviceUsernameChangeSyncJob(parameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ import org.signal.core.util.Util
|
||||
import org.signal.core.util.UuidUtil
|
||||
import org.signal.core.util.isNotEmpty
|
||||
import org.signal.core.util.orNull
|
||||
import org.signal.libsignal.net.KeyTransparency
|
||||
import org.signal.libsignal.protocol.IdentityKey
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.InvalidKeyException
|
||||
@ -42,6 +43,7 @@ import org.thoughtcrime.securesms.database.PaymentMetaDataUtil
|
||||
import org.thoughtcrime.securesms.database.SentStorySyncManifest
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.database.model.DistributionListId
|
||||
import org.thoughtcrime.securesms.database.model.KeyTransparencyStore
|
||||
import org.thoughtcrime.securesms.database.model.Mention
|
||||
import org.thoughtcrime.securesms.database.model.MessageRecord
|
||||
import org.thoughtcrime.securesms.database.model.MmsMessageRecord
|
||||
@ -58,6 +60,7 @@ import org.thoughtcrime.securesms.database.model.databaseprotos.PollTerminate
|
||||
import org.thoughtcrime.securesms.database.model.toBodyRangeList
|
||||
import org.thoughtcrime.securesms.database.withAttachments
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.dependencies.KeyTransparencyApi
|
||||
import org.thoughtcrime.securesms.groups.BadGroupIdException
|
||||
import org.thoughtcrime.securesms.groups.GroupChangeBusyException
|
||||
import org.thoughtcrime.securesms.groups.GroupId
|
||||
@ -188,6 +191,7 @@ object SyncMessageProcessor {
|
||||
syncMessage.deleteForMe != null -> handleSynchronizeDeleteForMe(context, syncMessage.deleteForMe!!, envelope.clientTimestamp!!, earlyMessageCacheEntry)
|
||||
syncMessage.attachmentBackfillRequest != null -> handleSynchronizeAttachmentBackfillRequest(syncMessage.attachmentBackfillRequest!!, envelope.clientTimestamp!!)
|
||||
syncMessage.attachmentBackfillResponse != null -> warn(envelope.clientTimestamp!!, "Contains a backfill response, but we don't handle these!")
|
||||
syncMessage.usernameChange != null -> handleSynchronizeUsernameChange(envelope.clientTimestamp!!)
|
||||
else -> warn(envelope.clientTimestamp!!, "Contains no known sync types...")
|
||||
}
|
||||
}
|
||||
@ -2026,6 +2030,12 @@ object SyncMessageProcessor {
|
||||
return threadId
|
||||
}
|
||||
|
||||
private fun handleSynchronizeUsernameChange(timestamp: Long) {
|
||||
log(timestamp, "[handleSynchronizeUsernameChange] Synchronize username change. Resetting KT.")
|
||||
|
||||
KeyTransparencyApi.reset(aci = SignalStore.account.requireAci().libSignalAci, field = KeyTransparency.AccountDataField.USERNAME_HASH, keyTransparencyStore = KeyTransparencyStore)
|
||||
}
|
||||
|
||||
private fun ConversationIdentifier.toRecipientId(): RecipientId? {
|
||||
val threadServiceId = ServiceId.parseOrNull(this.threadServiceId, this.threadServiceIdBinary)
|
||||
return when {
|
||||
|
||||
@ -20,9 +20,13 @@ import org.signal.network.NetworkResult
|
||||
import org.thoughtcrime.securesms.components.settings.app.usernamelinks.main.UsernameLinkResetResult
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.MultiDeviceUsernameChangeSyncJob
|
||||
import org.thoughtcrime.securesms.keyvalue.AccountValues
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.net.SignalNetwork
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.confirmUsernameAndCreateNewLink
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.reserveUsername
|
||||
import org.thoughtcrime.securesms.profiles.manage.UsernameRepository.updateUsernameDisplayForCurrentLink
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.storage.StorageSyncHelper
|
||||
import org.thoughtcrime.securesms.util.NetworkUtil
|
||||
@ -444,6 +448,9 @@ object UsernameRepository {
|
||||
SignalStore.account.usernameSyncErrorCount = 0
|
||||
SignalStore.misc.needsUsernameRestore = false
|
||||
|
||||
if (Recipient.self().usernameSyncMessagesCapability.isSupported) {
|
||||
MultiDeviceUsernameChangeSyncJob.enqueueUsernameChangeSync()
|
||||
}
|
||||
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
Log.i(TAG, "[updateUsernameDisplayForCurrentLink] Successfully updated username.")
|
||||
@ -477,6 +484,9 @@ object UsernameRepository {
|
||||
SignalStore.account.usernameSyncErrorCount = 0
|
||||
SignalStore.misc.needsUsernameRestore = false
|
||||
|
||||
if (Recipient.self().usernameSyncMessagesCapability.isSupported) {
|
||||
MultiDeviceUsernameChangeSyncJob.enqueueUsernameChangeSync()
|
||||
}
|
||||
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
Log.i(TAG, "[confirmUsernameAndCreateNewLink] Successfully confirmed username.")
|
||||
@ -534,6 +544,10 @@ object UsernameRepository {
|
||||
SignalStore.account.usernameSyncState = AccountValues.UsernameSyncState.IN_SYNC
|
||||
SignalStore.account.usernameSyncErrorCount = 0
|
||||
SignalStore.misc.needsUsernameRestore = false
|
||||
|
||||
if (Recipient.self().usernameSyncMessagesCapability.isSupported) {
|
||||
MultiDeviceUsernameChangeSyncJob.enqueueUsernameChangeSync()
|
||||
}
|
||||
SignalDatabase.recipients.markNeedsSync(Recipient.self().id)
|
||||
StorageSyncHelper.scheduleSyncForDataChange()
|
||||
Log.i(TAG, "[deleteUsername] Successfully deleted the username.")
|
||||
|
||||
@ -352,6 +352,9 @@ class Recipient(
|
||||
sealedSenderAccessModeValue
|
||||
}
|
||||
|
||||
/** The user's capability to receive username sync messages */
|
||||
val usernameSyncMessagesCapability: Capability = capabilities.usernameSyncMessages
|
||||
|
||||
/** The wallpaper to render as the chat background, if present. */
|
||||
val wallpaper: ChatWallpaper?
|
||||
get() {
|
||||
|
||||
@ -731,7 +731,8 @@ class AppRegistrationNetworkController(
|
||||
storage,
|
||||
versionedExpirationTimer,
|
||||
attachmentBackfill,
|
||||
spqr
|
||||
spqr,
|
||||
usernameChangeSyncMessage
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -368,7 +368,7 @@ class ApiPlugin : Plugin {
|
||||
}
|
||||
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true))
|
||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true))
|
||||
SignalDatabase.recipients.setProfileSharing(recipientId, profileSharing)
|
||||
|
||||
if (registered) {
|
||||
|
||||
@ -122,7 +122,8 @@ object RecipientDatabaseTestUtils {
|
||||
notificationChannel = notificationChannel,
|
||||
sealedSenderAccessMode = sealedSenderAccessMode,
|
||||
capabilities = RecipientRecord.Capabilities(
|
||||
rawBits = capabilities
|
||||
rawBits = capabilities,
|
||||
usernameSyncMessages = Recipient.Capability.SUPPORTED
|
||||
),
|
||||
storageId = storageId,
|
||||
mentionSetting = mentionSetting,
|
||||
|
||||
@ -184,7 +184,7 @@ class RecipientTestRule : TestRule {
|
||||
val id = SignalDatabase.recipients.getOrInsertFromServiceId(aci)
|
||||
SignalDatabase.recipients.setProfileName(id, profileName)
|
||||
SignalDatabase.recipients.setProfileKeyIfAbsent(id, ProfileKey(Random.nextBytes(32)))
|
||||
SignalDatabase.recipients.setCapabilities(id, SignalServiceProfile.Capabilities(true, true))
|
||||
SignalDatabase.recipients.setCapabilities(id, SignalServiceProfile.Capabilities(true, true, true))
|
||||
SignalDatabase.recipients.setProfileSharing(id, profileSharing)
|
||||
SignalDatabase.recipients.markRegistered(id, aci)
|
||||
return id
|
||||
|
||||
@ -885,7 +885,8 @@ class DemoNetworkController(
|
||||
storage = !RegistrationPreferences.pinsOptedOut,
|
||||
versionedExpirationTimer = true,
|
||||
attachmentBackfill = true,
|
||||
spqr = true
|
||||
spqr = true,
|
||||
usernameChangeSyncMessage = true
|
||||
),
|
||||
name = null,
|
||||
pniRegistrationId = RegistrationPreferences.pniRegistrationId,
|
||||
@ -1127,7 +1128,8 @@ class DemoNetworkController(
|
||||
storage,
|
||||
versionedExpirationTimer,
|
||||
attachmentBackfill,
|
||||
spqr
|
||||
spqr,
|
||||
usernameChangeSyncMessage
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -182,7 +182,8 @@ class PinSettingsViewModel(
|
||||
storage = !newOptedOut,
|
||||
versionedExpirationTimer = true,
|
||||
attachmentBackfill = true,
|
||||
spqr = true
|
||||
spqr = true,
|
||||
usernameChangeSyncMessage = true
|
||||
),
|
||||
name = null,
|
||||
pniRegistrationId = RegistrationPreferences.pniRegistrationId,
|
||||
|
||||
@ -369,7 +369,8 @@ interface NetworkController {
|
||||
val storage: Boolean,
|
||||
val versionedExpirationTimer: Boolean,
|
||||
val attachmentBackfill: Boolean,
|
||||
val spqr: Boolean
|
||||
val spqr: Boolean,
|
||||
val usernameChangeSyncMessage: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -369,7 +369,8 @@ class RegistrationRepository(val context: Context, val networkController: Networ
|
||||
storage = true, // True initially -- can turn off later if users opt-out
|
||||
versionedExpirationTimer = true,
|
||||
attachmentBackfill = true,
|
||||
spqr = true
|
||||
spqr = true,
|
||||
usernameChangeSyncMessage = false // TODO(michelle): Turn on once all clients support it
|
||||
),
|
||||
name = null,
|
||||
pniRegistrationId = keyMaterial.pniRegistrationId,
|
||||
|
||||
@ -57,6 +57,7 @@ class AccountAttributes @JsonCreator constructor(
|
||||
@JsonProperty val storage: Boolean,
|
||||
@JsonProperty val versionedExpirationTimer: Boolean,
|
||||
@JsonProperty val attachmentBackfill: Boolean,
|
||||
@JsonProperty val spqr: Boolean
|
||||
@JsonProperty val spqr: Boolean,
|
||||
@JsonProperty val usernameChangeSyncMessage: Boolean
|
||||
)
|
||||
}
|
||||
|
||||
@ -195,12 +195,16 @@ public class SignalServiceProfile {
|
||||
@JsonProperty("ssre2")
|
||||
private boolean storageServiceEncryptionV2;
|
||||
|
||||
@JsonProperty("usernameChangeSyncMessage")
|
||||
private boolean usernameSyncMessages;
|
||||
|
||||
@JsonCreator
|
||||
public Capabilities() {}
|
||||
|
||||
public Capabilities(boolean storage, boolean storageServiceEncryptionV2) {
|
||||
public Capabilities(boolean storage, boolean storageServiceEncryptionV2, boolean usernameSyncMessages) {
|
||||
this.storage = storage;
|
||||
this.storageServiceEncryptionV2 = storageServiceEncryptionV2;
|
||||
this.usernameSyncMessages = usernameSyncMessages;
|
||||
}
|
||||
|
||||
public boolean isStorage() {
|
||||
@ -210,6 +214,10 @@ public class SignalServiceProfile {
|
||||
public boolean isStorageServiceEncryptionV2() {
|
||||
return storageServiceEncryptionV2;
|
||||
}
|
||||
|
||||
public boolean isUsernameSyncMessages() {
|
||||
return usernameSyncMessages;
|
||||
}
|
||||
}
|
||||
|
||||
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {
|
||||
|
||||
@ -850,6 +850,8 @@ message SyncMessage {
|
||||
}
|
||||
}
|
||||
|
||||
message UsernameChange {}
|
||||
|
||||
oneof content {
|
||||
Sent sent = 1;
|
||||
Contacts contacts = 2;
|
||||
@ -870,6 +872,7 @@ message SyncMessage {
|
||||
DeviceNameChange deviceNameChange = 23;
|
||||
AttachmentBackfillRequest attachmentBackfillRequest = 24;
|
||||
AttachmentBackfillResponse attachmentBackfillResponse = 25;
|
||||
UsernameChange usernameChange = 26;
|
||||
}
|
||||
|
||||
reserved /*groups*/ 3;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user