kopia lustrzana https://github.com/ryukoposting/Signal-Android
Add PNP linked device initialization job.
Co-authored-by: Greyson Parrelli <greyson@signal.org>fork-5.53.8
rodzic
e2a7ed86e4
commit
beee3b7dc3
|
@ -108,7 +108,7 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
|
||||||
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
val recipientId = RecipientId.from(SignalServiceAddress(aci, "+15555551%03d".format(i)))
|
||||||
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
SignalDatabase.recipients.setProfileName(recipientId, ProfileName.fromParts("Buddy", "#$i"))
|
||||||
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
SignalDatabase.recipients.setProfileKeyIfAbsent(recipientId, ProfileKeyUtil.createNew())
|
||||||
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true, true, true, true, true))
|
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true, true, true, true, true, true))
|
||||||
SignalDatabase.recipients.setProfileSharing(recipientId, true)
|
SignalDatabase.recipients.setProfileSharing(recipientId, true)
|
||||||
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), IdentityKeyUtil.generateIdentityKeyPair().publicKey)
|
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), IdentityKeyUtil.generateIdentityKeyPair().publicKey)
|
||||||
others += recipientId
|
others += recipientId
|
||||||
|
|
|
@ -20,6 +20,6 @@ public final class AppCapabilities {
|
||||||
* asking if the user has set a Signal PIN or not.
|
* asking if the user has set a Signal PIN or not.
|
||||||
*/
|
*/
|
||||||
public static AccountAttributes.Capabilities getCapabilities(boolean storageCapable) {
|
public static AccountAttributes.Capabilities getCapabilities(boolean storageCapable) {
|
||||||
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION, SENDER_KEY, ANNOUNCEMENT_GROUPS, CHANGE_NUMBER, FeatureFlags.stories(), FeatureFlags.giftBadgeReceiveSupport());
|
return new AccountAttributes.Capabilities(UUID_CAPABLE, GV2_CAPABLE, storageCapable, GV1_MIGRATION, SENDER_KEY, ANNOUNCEMENT_GROUPS, CHANGE_NUMBER, FeatureFlags.stories(), FeatureFlags.giftBadgeReceiveSupport(), FeatureFlags.phoneNumberPrivacy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
|
||||||
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
|
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
|
||||||
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
|
||||||
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
|
||||||
|
import org.thoughtcrime.securesms.jobs.PnpInitializeDevicesJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
|
||||||
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
|
||||||
|
@ -206,6 +207,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
||||||
.addPostRender(CheckServiceReachabilityJob::enqueueIfNecessary)
|
.addPostRender(CheckServiceReachabilityJob::enqueueIfNecessary)
|
||||||
.addPostRender(GroupV2UpdateSelfProfileKeyJob::enqueueForGroupsIfNecessary)
|
.addPostRender(GroupV2UpdateSelfProfileKeyJob::enqueueForGroupsIfNecessary)
|
||||||
.addPostRender(StoryOnboardingDownloadJob.Companion::enqueueIfNeeded)
|
.addPostRender(StoryOnboardingDownloadJob.Companion::enqueueIfNeeded)
|
||||||
|
.addPostRender(PnpInitializeDevicesJob::enqueueIfNecessary)
|
||||||
.execute();
|
.execute();
|
||||||
|
|
||||||
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
|
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||||
|
|
|
@ -58,7 +58,10 @@ class ChangeNumberLockActivity : PassphraseRequiredActivity() {
|
||||||
Single.just(false)
|
Single.just(false)
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Local (${SignalStore.account().e164}) and remote (${whoAmI.number}) numbers do not match, updating local.")
|
Log.i(TAG, "Local (${SignalStore.account().e164}) and remote (${whoAmI.number}) numbers do not match, updating local.")
|
||||||
changeNumberRepository.changeLocalNumber(whoAmI.number, PNI.parseOrThrow(whoAmI.pni))
|
Single
|
||||||
|
.just(true)
|
||||||
|
.flatMap { changeNumberRepository.changeLocalNumber(whoAmI.number, PNI.parseOrThrow(whoAmI.pni)) }
|
||||||
|
.compose(ChangeNumberRepository::acquireReleaseChangeNumberLock)
|
||||||
.map { true }
|
.map { true }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,14 +44,44 @@ import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevic
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.security.MessageDigest
|
import java.security.MessageDigest
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
|
|
||||||
private val TAG: String = Log.tag(ChangeNumberRepository::class.java)
|
private val TAG: String = Log.tag(ChangeNumberRepository::class.java)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides various change number operations. All operations must run on [Schedulers.single] to support
|
||||||
|
* the global "I am changing the number" lock exclusivity.
|
||||||
|
*/
|
||||||
class ChangeNumberRepository(
|
class ChangeNumberRepository(
|
||||||
private val accountManager: SignalServiceAccountManager = ApplicationDependencies.getSignalServiceAccountManager(),
|
private val accountManager: SignalServiceAccountManager = ApplicationDependencies.getSignalServiceAccountManager(),
|
||||||
private val messageSender: SignalServiceMessageSender = ApplicationDependencies.getSignalServiceMessageSender()
|
private val messageSender: SignalServiceMessageSender = ApplicationDependencies.getSignalServiceMessageSender()
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* This lock should be held by anyone who is performing a change number operation, so that two different parties cannot change the user's number
|
||||||
|
* at the same time.
|
||||||
|
*/
|
||||||
|
val CHANGE_NUMBER_LOCK = ReentrantLock()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds Rx operators to chain to acquire and release the [CHANGE_NUMBER_LOCK] on subscribe and on finish.
|
||||||
|
*/
|
||||||
|
fun <T : Any> acquireReleaseChangeNumberLock(upstream: Single<T>): Single<T> {
|
||||||
|
return upstream.doOnSubscribe {
|
||||||
|
CHANGE_NUMBER_LOCK.lock()
|
||||||
|
SignalStore.misc().lockChangeNumber()
|
||||||
|
}
|
||||||
|
.subscribeOn(Schedulers.single())
|
||||||
|
.observeOn(Schedulers.single())
|
||||||
|
.doFinally {
|
||||||
|
if (CHANGE_NUMBER_LOCK.isHeldByCurrentThread) {
|
||||||
|
CHANGE_NUMBER_LOCK.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun ensureDecryptionsDrained(): Completable {
|
fun ensureDecryptionsDrained(): Completable {
|
||||||
return Completable.create { emitter ->
|
return Completable.create { emitter ->
|
||||||
ApplicationDependencies
|
ApplicationDependencies
|
||||||
|
@ -59,17 +89,23 @@ class ChangeNumberRepository(
|
||||||
.addDecryptionDrainedListener {
|
.addDecryptionDrainedListener {
|
||||||
emitter.onComplete()
|
emitter.onComplete()
|
||||||
}
|
}
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.single())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun changeNumber(code: String, newE164: String): Single<ServiceResponse<VerifyAccountResponse>> {
|
fun changeNumber(code: String, newE164: String, pniUpdateMode: Boolean = false): Single<ServiceResponse<VerifyAccountResponse>> {
|
||||||
return Single.fromCallable {
|
return Single.fromCallable {
|
||||||
var completed = false
|
var completed = false
|
||||||
var attempts = 0
|
var attempts = 0
|
||||||
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
|
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
|
||||||
|
|
||||||
while (!completed && attempts < 5) {
|
while (!completed && attempts < 5) {
|
||||||
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(code, newE164, null)
|
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(
|
||||||
|
code = code,
|
||||||
|
newE164 = newE164,
|
||||||
|
registrationLock = null,
|
||||||
|
pniUpdateMode = pniUpdateMode
|
||||||
|
)
|
||||||
|
|
||||||
SignalStore.misc().setPendingChangeNumberMetadata(metadata)
|
SignalStore.misc().setPendingChangeNumberMetadata(metadata)
|
||||||
|
|
||||||
changeNumberResponse = accountManager.changeNumber(request)
|
changeNumberResponse = accountManager.changeNumber(request)
|
||||||
|
@ -84,7 +120,7 @@ class ChangeNumberRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
changeNumberResponse
|
changeNumberResponse
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.single())
|
||||||
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +150,13 @@ class ChangeNumberRepository(
|
||||||
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
|
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
|
||||||
|
|
||||||
while (!completed && attempts < 5) {
|
while (!completed && attempts < 5) {
|
||||||
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(code, newE164, registrationLock)
|
val (request: ChangePhoneNumberRequest, metadata: PendingChangeNumberMetadata) = createChangeNumberRequest(
|
||||||
|
code = code,
|
||||||
|
newE164 = newE164,
|
||||||
|
registrationLock = registrationLock,
|
||||||
|
pniUpdateMode = false
|
||||||
|
)
|
||||||
|
|
||||||
SignalStore.misc().setPendingChangeNumberMetadata(metadata)
|
SignalStore.misc().setPendingChangeNumberMetadata(metadata)
|
||||||
|
|
||||||
changeNumberResponse = accountManager.changeNumber(request)
|
changeNumberResponse = accountManager.changeNumber(request)
|
||||||
|
@ -129,14 +171,14 @@ class ChangeNumberRepository(
|
||||||
}
|
}
|
||||||
|
|
||||||
VerifyAccountRepository.VerifyAccountWithRegistrationLockResponse.from(changeNumberResponse, kbsData)
|
VerifyAccountRepository.VerifyAccountWithRegistrationLockResponse.from(changeNumberResponse, kbsData)
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.single())
|
||||||
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
fun whoAmI(): Single<WhoAmIResponse> {
|
fun whoAmI(): Single<WhoAmIResponse> {
|
||||||
return Single.fromCallable { ApplicationDependencies.getSignalServiceAccountManager().getWhoAmI() }
|
return Single.fromCallable { ApplicationDependencies.getSignalServiceAccountManager().getWhoAmI() }
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.single())
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
@ -145,7 +187,7 @@ class ChangeNumberRepository(
|
||||||
SignalDatabase.recipients.updateSelfPhone(e164, pni)
|
SignalDatabase.recipients.updateSelfPhone(e164, pni)
|
||||||
val newStorageId: ByteArray? = Recipient.self().storageServiceId
|
val newStorageId: ByteArray? = Recipient.self().storageServiceId
|
||||||
|
|
||||||
if (MessageDigest.isEqual(oldStorageId, newStorageId)) {
|
if (e164 != SignalStore.account().requireE164() && MessageDigest.isEqual(oldStorageId, newStorageId)) {
|
||||||
Log.w(TAG, "Self storage id was not rotated, attempting to rotate again")
|
Log.w(TAG, "Self storage id was not rotated, attempting to rotate again")
|
||||||
SignalDatabase.recipients.rotateStorageId(Recipient.self().id)
|
SignalDatabase.recipients.rotateStorageId(Recipient.self().id)
|
||||||
StorageSyncHelper.scheduleSyncForDataChange()
|
StorageSyncHelper.scheduleSyncForDataChange()
|
||||||
|
@ -198,6 +240,9 @@ class ChangeNumberRepository(
|
||||||
System.currentTimeMillis(),
|
System.currentTimeMillis(),
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SignalStore.misc().setPniInitializedDevices(true)
|
||||||
|
ApplicationDependencies.getGroupsV2Authorization().clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
Recipient.self().live().refresh()
|
Recipient.self().live().refresh()
|
||||||
|
@ -227,7 +272,7 @@ class ChangeNumberRepository(
|
||||||
|
|
||||||
SignalStore.certificateValues().setUnidentifiedAccessCertificate(certificateType, certificate)
|
SignalStore.certificateValues().setUnidentifiedAccessCertificate(certificateType, certificate)
|
||||||
}
|
}
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.single())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UsePropertyAccessSyntax")
|
@Suppress("UsePropertyAccessSyntax")
|
||||||
|
@ -235,12 +280,13 @@ class ChangeNumberRepository(
|
||||||
private fun createChangeNumberRequest(
|
private fun createChangeNumberRequest(
|
||||||
code: String,
|
code: String,
|
||||||
newE164: String,
|
newE164: String,
|
||||||
registrationLock: String?
|
registrationLock: String?,
|
||||||
|
pniUpdateMode: Boolean
|
||||||
): ChangeNumberRequestData {
|
): ChangeNumberRequestData {
|
||||||
val selfIdentifier: String = SignalStore.account().requireAci().toString()
|
val selfIdentifier: String = SignalStore.account().requireAci().toString()
|
||||||
val aciProtocolStore: SignalProtocolStore = ApplicationDependencies.getProtocolStore().aci()
|
val aciProtocolStore: SignalProtocolStore = ApplicationDependencies.getProtocolStore().aci()
|
||||||
|
|
||||||
val pniIdentity: IdentityKeyPair = IdentityKeyUtil.generateIdentityKeyPair()
|
val pniIdentity: IdentityKeyPair = if (pniUpdateMode) SignalStore.account().pniIdentityKey else IdentityKeyUtil.generateIdentityKeyPair()
|
||||||
val deviceMessages = mutableListOf<OutgoingPushMessage>()
|
val deviceMessages = mutableListOf<OutgoingPushMessage>()
|
||||||
val devicePniSignedPreKeys = mutableMapOf<Int, SignedPreKeyEntity>()
|
val devicePniSignedPreKeys = mutableMapOf<Int, SignedPreKeyEntity>()
|
||||||
val pniRegistrationIds = mutableMapOf<Int, Int>()
|
val pniRegistrationIds = mutableMapOf<Int, Int>()
|
||||||
|
@ -253,14 +299,23 @@ class ChangeNumberRepository(
|
||||||
.forEach { deviceId ->
|
.forEach { deviceId ->
|
||||||
// Signed Prekeys
|
// Signed Prekeys
|
||||||
val signedPreKeyRecord = if (deviceId == primaryDeviceId) {
|
val signedPreKeyRecord = if (deviceId == primaryDeviceId) {
|
||||||
PreKeyUtil.generateAndStoreSignedPreKey(ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys, pniIdentity.privateKey)
|
if (pniUpdateMode) {
|
||||||
|
ApplicationDependencies.getProtocolStore().pni().loadSignedPreKey(SignalStore.account().pniPreKeys.activeSignedPreKeyId)
|
||||||
|
} else {
|
||||||
|
PreKeyUtil.generateAndStoreSignedPreKey(ApplicationDependencies.getProtocolStore().pni(), SignalStore.account().pniPreKeys, pniIdentity.privateKey)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
|
||||||
}
|
}
|
||||||
devicePniSignedPreKeys[deviceId] = SignedPreKeyEntity(signedPreKeyRecord.id, signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature)
|
devicePniSignedPreKeys[deviceId] = SignedPreKeyEntity(signedPreKeyRecord.id, signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature)
|
||||||
|
|
||||||
// Registration Ids
|
// Registration Ids
|
||||||
var pniRegistrationId = -1
|
var pniRegistrationId = if (deviceId == primaryDeviceId && pniUpdateMode) {
|
||||||
|
SignalStore.account().pniRegistrationId
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
}
|
||||||
|
|
||||||
while (pniRegistrationId < 0 || pniRegistrationIds.values.contains(pniRegistrationId)) {
|
while (pniRegistrationId < 0 || pniRegistrationIds.values.contains(pniRegistrationId)) {
|
||||||
pniRegistrationId = KeyHelper.generateRegistrationId(false)
|
pniRegistrationId = KeyHelper.generateRegistrationId(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,13 +123,13 @@ class ChangeNumberViewModel(
|
||||||
|
|
||||||
override fun verifyCodeWithoutRegistrationLock(code: String): Single<VerifyAccountResponseProcessor> {
|
override fun verifyCodeWithoutRegistrationLock(code: String): Single<VerifyAccountResponseProcessor> {
|
||||||
return super.verifyCodeWithoutRegistrationLock(code)
|
return super.verifyCodeWithoutRegistrationLock(code)
|
||||||
.doOnSubscribe { SignalStore.misc().lockChangeNumber() }
|
.compose(ChangeNumberRepository::acquireReleaseChangeNumberLock)
|
||||||
.flatMap(this::attemptToUnlockChangeNumber)
|
.flatMap(this::attemptToUnlockChangeNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun verifyCodeAndRegisterAccountWithRegistrationLock(pin: String): Single<VerifyCodeWithRegistrationLockResponseProcessor> {
|
override fun verifyCodeAndRegisterAccountWithRegistrationLock(pin: String): Single<VerifyCodeWithRegistrationLockResponseProcessor> {
|
||||||
return super.verifyCodeAndRegisterAccountWithRegistrationLock(pin)
|
return super.verifyCodeAndRegisterAccountWithRegistrationLock(pin)
|
||||||
.doOnSubscribe { SignalStore.misc().lockChangeNumber() }
|
.compose(ChangeNumberRepository::acquireReleaseChangeNumberLock)
|
||||||
.flatMap(this::attemptToUnlockChangeNumber)
|
.flatMap(this::attemptToUnlockChangeNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1607,6 +1607,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||||
value = Bitmask.update(value, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isChangeNumber).serialize().toLong())
|
value = Bitmask.update(value, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isChangeNumber).serialize().toLong())
|
||||||
value = Bitmask.update(value, Capabilities.STORIES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStories).serialize().toLong())
|
value = Bitmask.update(value, Capabilities.STORIES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isStories).serialize().toLong())
|
||||||
value = Bitmask.update(value, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGiftBadges).serialize().toLong())
|
value = Bitmask.update(value, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isGiftBadges).serialize().toLong())
|
||||||
|
value = Bitmask.update(value, Capabilities.PNP, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPnp).serialize().toLong())
|
||||||
|
|
||||||
val values = ContentValues(1).apply {
|
val values = ContentValues(1).apply {
|
||||||
put(CAPABILITIES, value)
|
put(CAPABILITIES, value)
|
||||||
|
@ -3873,6 +3874,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||||
changeNumberCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH).toInt()),
|
changeNumberCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.CHANGE_NUMBER, Capabilities.BIT_LENGTH).toInt()),
|
||||||
storiesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORIES, Capabilities.BIT_LENGTH).toInt()),
|
storiesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.STORIES, Capabilities.BIT_LENGTH).toInt()),
|
||||||
giftBadgesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH).toInt()),
|
giftBadgesCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.GIFT_BADGES, Capabilities.BIT_LENGTH).toInt()),
|
||||||
|
pnpCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PNP, Capabilities.BIT_LENGTH).toInt()),
|
||||||
insightsBannerTier = InsightsBannerTier.fromId(cursor.requireInt(SEEN_INVITE_REMINDER)),
|
insightsBannerTier = InsightsBannerTier.fromId(cursor.requireInt(SEEN_INVITE_REMINDER)),
|
||||||
storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)),
|
storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)),
|
||||||
mentionSetting = MentionSetting.fromId(cursor.requireInt(MENTION_SETTING)),
|
mentionSetting = MentionSetting.fromId(cursor.requireInt(MENTION_SETTING)),
|
||||||
|
@ -4192,6 +4194,7 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
|
||||||
const val CHANGE_NUMBER = 4
|
const val CHANGE_NUMBER = 4
|
||||||
const val STORIES = 5
|
const val STORIES = 5
|
||||||
const val GIFT_BADGES = 6
|
const val GIFT_BADGES = 6
|
||||||
|
const val PNP = 7
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class VibrateState(val id: Int) {
|
enum class VibrateState(val id: Int) {
|
||||||
|
|
|
@ -70,6 +70,7 @@ data class RecipientRecord(
|
||||||
val changeNumberCapability: Recipient.Capability,
|
val changeNumberCapability: Recipient.Capability,
|
||||||
val storiesCapability: Recipient.Capability,
|
val storiesCapability: Recipient.Capability,
|
||||||
val giftBadgesCapability: Recipient.Capability,
|
val giftBadgesCapability: Recipient.Capability,
|
||||||
|
val pnpCapability: Recipient.Capability,
|
||||||
val insightsBannerTier: InsightsBannerTier,
|
val insightsBannerTier: InsightsBannerTier,
|
||||||
val storageId: ByteArray?,
|
val storageId: ByteArray?,
|
||||||
val mentionSetting: MentionSetting,
|
val mentionSetting: MentionSetting,
|
||||||
|
|
|
@ -135,6 +135,7 @@ public final class JobManagerFactories {
|
||||||
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
|
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
|
||||||
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
|
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
|
||||||
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
|
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
|
||||||
|
put(PnpInitializeDevicesJob.KEY, new PnpInitializeDevicesJob.Factory());
|
||||||
put(PreKeysSyncJob.KEY, new PreKeysSyncJob.Factory());
|
put(PreKeysSyncJob.KEY, new PreKeysSyncJob.Factory());
|
||||||
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
|
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
|
||||||
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
|
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());
|
||||||
|
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.thoughtcrime.securesms.jobs
|
||||||
|
|
||||||
|
import org.signal.core.util.concurrent.safeBlockingGet
|
||||||
|
import org.signal.core.util.logging.Log
|
||||||
|
import org.thoughtcrime.securesms.components.settings.app.changenumber.ChangeNumberRepository
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Data
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.Job
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
|
||||||
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.registration.VerifyAccountResponseWithoutKbs
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
/**
|
||||||
|
* To be run when all clients support PNP and we need to initialize all linked devices with appropriate PNP data.
|
||||||
|
*
|
||||||
|
* We reuse the change number flow as it already support distributing the necessary data in a way linked devices can understand.
|
||||||
|
*/
|
||||||
|
class PnpInitializeDevicesJob private constructor(parameters: Parameters) : BaseJob(parameters) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY = "PnpInitializeDevicesJob"
|
||||||
|
private val TAG = Log.tag(PnpInitializeDevicesJob::class.java)
|
||||||
|
private const val PLACEHOLDER_CODE = "123456"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun enqueueIfNecessary() {
|
||||||
|
if (SignalStore.misc().hasPniInitializedDevices() || !SignalStore.account().isRegistered || SignalStore.account().aci == null || Recipient.self().pnpCapability != Recipient.Capability.SUPPORTED) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationDependencies.getJobManager().add(PnpInitializeDevicesJob())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor() : this(Parameters.Builder().addConstraint(NetworkConstraint.KEY).build())
|
||||||
|
|
||||||
|
override fun serialize(): Data {
|
||||||
|
return Data.EMPTY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getFactoryKey(): String {
|
||||||
|
return KEY
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure() = Unit
|
||||||
|
|
||||||
|
@Throws(Exception::class)
|
||||||
|
public override fun onRun() {
|
||||||
|
if (!SignalStore.account().isRegistered || SignalStore.account().aci == null) {
|
||||||
|
Log.w(TAG, "Not registered! Skipping, as it wouldn't do anything.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextSecurePreferences.isMultiDevice(context)) {
|
||||||
|
Log.i(TAG, "Not multi device, aborting...")
|
||||||
|
SignalStore.misc().setPniInitializedDevices(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SignalStore.account().isLinkedDevice) {
|
||||||
|
Log.i(TAG, "Not primary device, aborting...")
|
||||||
|
SignalStore.misc().setPniInitializedDevices(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeNumberRepository.CHANGE_NUMBER_LOCK.lock()
|
||||||
|
try {
|
||||||
|
if (SignalStore.misc().hasPniInitializedDevices()) {
|
||||||
|
Log.w(TAG, "We found out that things have been initialized after we got the lock! No need to do anything else.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val changeNumberRepository = ChangeNumberRepository()
|
||||||
|
val e164 = SignalStore.account().requireE164()
|
||||||
|
|
||||||
|
try {
|
||||||
|
Log.i(TAG, "Calling change number with our current number to distribute PNI messages")
|
||||||
|
changeNumberRepository
|
||||||
|
.changeNumber(code = PLACEHOLDER_CODE, newE164 = e164, pniUpdateMode = true)
|
||||||
|
.map(::VerifyAccountResponseWithoutKbs)
|
||||||
|
.safeBlockingGet()
|
||||||
|
.resultOrThrow
|
||||||
|
} catch (e: InterruptedException) {
|
||||||
|
throw IOException("Retry", e)
|
||||||
|
} catch (t: Throwable) {
|
||||||
|
Log.w(TAG, "Unable to initialize PNI for linked devices", t)
|
||||||
|
throw t
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalStore.misc().setPniInitializedDevices(true)
|
||||||
|
} finally {
|
||||||
|
ChangeNumberRepository.CHANGE_NUMBER_LOCK.unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
return e is IOException
|
||||||
|
}
|
||||||
|
|
||||||
|
class Factory : Job.Factory<PnpInitializeDevicesJob?> {
|
||||||
|
override fun create(parameters: Parameters, data: Data): PnpInitializeDevicesJob {
|
||||||
|
return PnpInitializeDevicesJob(parameters)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -117,6 +117,7 @@ public class RefreshAttributesJob extends BaseJob {
|
||||||
"\n Change Number? " + capabilities.isChangeNumber() +
|
"\n Change Number? " + capabilities.isChangeNumber() +
|
||||||
"\n Stories? " + capabilities.isStories() +
|
"\n Stories? " + capabilities.isStories() +
|
||||||
"\n Gift Badges? " + capabilities.isGiftBadges() +
|
"\n Gift Badges? " + capabilities.isGiftBadges() +
|
||||||
|
"\n PNP? " + capabilities.isPnp() +
|
||||||
"\n UUID? " + capabilities.isUuid());
|
"\n UUID? " + capabilities.isUuid());
|
||||||
|
|
||||||
SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager();
|
||||||
|
|
|
@ -26,6 +26,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
||||||
private static final String CDS_TOKEN = "misc.cds_token";
|
private static final String CDS_TOKEN = "misc.cds_token";
|
||||||
private static final String LAST_FCM_FOREGROUND_TIME = "misc.last_fcm_foreground_time";
|
private static final String LAST_FCM_FOREGROUND_TIME = "misc.last_fcm_foreground_time";
|
||||||
private static final String LAST_FOREGROUND_TIME = "misc.last_foreground_time";
|
private static final String LAST_FOREGROUND_TIME = "misc.last_foreground_time";
|
||||||
|
private static final String PNI_INITIALIZED_DEVICES = "misc.pni_initialized_devices";
|
||||||
|
|
||||||
MiscellaneousValues(@NonNull KeyValueStore store) {
|
MiscellaneousValues(@NonNull KeyValueStore store) {
|
||||||
super(store);
|
super(store);
|
||||||
|
@ -184,4 +185,12 @@ public final class MiscellaneousValues extends SignalStoreValues {
|
||||||
public void setLastForegroundTime(long time) {
|
public void setLastForegroundTime(long time) {
|
||||||
putLong(LAST_FOREGROUND_TIME, time);
|
putLong(LAST_FOREGROUND_TIME, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasPniInitializedDevices() {
|
||||||
|
return getBoolean(PNI_INITIALIZED_DEVICES, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPniInitializedDevices(boolean value) {
|
||||||
|
putBoolean(PNI_INITIALIZED_DEVICES, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,6 +124,7 @@ public class Recipient {
|
||||||
private final Capability changeNumberCapability;
|
private final Capability changeNumberCapability;
|
||||||
private final Capability storiesCapability;
|
private final Capability storiesCapability;
|
||||||
private final Capability giftBadgesCapability;
|
private final Capability giftBadgesCapability;
|
||||||
|
private final Capability pnpCapability;
|
||||||
private final InsightsBannerTier insightsBannerTier;
|
private final InsightsBannerTier insightsBannerTier;
|
||||||
private final byte[] storageId;
|
private final byte[] storageId;
|
||||||
private final MentionSetting mentionSetting;
|
private final MentionSetting mentionSetting;
|
||||||
|
@ -426,6 +427,7 @@ public class Recipient {
|
||||||
this.changeNumberCapability = Capability.UNKNOWN;
|
this.changeNumberCapability = Capability.UNKNOWN;
|
||||||
this.storiesCapability = Capability.UNKNOWN;
|
this.storiesCapability = Capability.UNKNOWN;
|
||||||
this.giftBadgesCapability = Capability.UNKNOWN;
|
this.giftBadgesCapability = Capability.UNKNOWN;
|
||||||
|
this.pnpCapability = Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
this.wallpaper = null;
|
this.wallpaper = null;
|
||||||
|
@ -485,6 +487,7 @@ public class Recipient {
|
||||||
this.changeNumberCapability = details.changeNumberCapability;
|
this.changeNumberCapability = details.changeNumberCapability;
|
||||||
this.storiesCapability = details.storiesCapability;
|
this.storiesCapability = details.storiesCapability;
|
||||||
this.giftBadgesCapability = details.giftBadgesCapability;
|
this.giftBadgesCapability = details.giftBadgesCapability;
|
||||||
|
this.pnpCapability = details.pnpCapability;
|
||||||
this.storageId = details.storageId;
|
this.storageId = details.storageId;
|
||||||
this.mentionSetting = details.mentionSetting;
|
this.mentionSetting = details.mentionSetting;
|
||||||
this.wallpaper = details.wallpaper;
|
this.wallpaper = details.wallpaper;
|
||||||
|
@ -1043,6 +1046,10 @@ public class Recipient {
|
||||||
return giftBadgesCapability;
|
return giftBadgesCapability;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @NonNull Capability getPnpCapability() {
|
||||||
|
return pnpCapability;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if this recipient supports the message retry system, or false if we should use the legacy session reset system.
|
* True if this recipient supports the message retry system, or false if we should use the legacy session reset system.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -75,6 +75,7 @@ public class RecipientDetails {
|
||||||
final Recipient.Capability changeNumberCapability;
|
final Recipient.Capability changeNumberCapability;
|
||||||
final Recipient.Capability storiesCapability;
|
final Recipient.Capability storiesCapability;
|
||||||
final Recipient.Capability giftBadgesCapability;
|
final Recipient.Capability giftBadgesCapability;
|
||||||
|
final Recipient.Capability pnpCapability;
|
||||||
final InsightsBannerTier insightsBannerTier;
|
final InsightsBannerTier insightsBannerTier;
|
||||||
final byte[] storageId;
|
final byte[] storageId;
|
||||||
final MentionSetting mentionSetting;
|
final MentionSetting mentionSetting;
|
||||||
|
@ -139,6 +140,7 @@ public class RecipientDetails {
|
||||||
this.changeNumberCapability = record.getChangeNumberCapability();
|
this.changeNumberCapability = record.getChangeNumberCapability();
|
||||||
this.storiesCapability = record.getStoriesCapability();
|
this.storiesCapability = record.getStoriesCapability();
|
||||||
this.giftBadgesCapability = record.getGiftBadgesCapability();
|
this.giftBadgesCapability = record.getGiftBadgesCapability();
|
||||||
|
this.pnpCapability = record.getPnpCapability();
|
||||||
this.insightsBannerTier = record.getInsightsBannerTier();
|
this.insightsBannerTier = record.getInsightsBannerTier();
|
||||||
this.storageId = record.getStorageId();
|
this.storageId = record.getStorageId();
|
||||||
this.mentionSetting = record.getMentionSetting();
|
this.mentionSetting = record.getMentionSetting();
|
||||||
|
@ -199,6 +201,7 @@ public class RecipientDetails {
|
||||||
this.changeNumberCapability = Recipient.Capability.UNKNOWN;
|
this.changeNumberCapability = Recipient.Capability.UNKNOWN;
|
||||||
this.storiesCapability = Recipient.Capability.UNKNOWN;
|
this.storiesCapability = Recipient.Capability.UNKNOWN;
|
||||||
this.giftBadgesCapability = Recipient.Capability.UNKNOWN;
|
this.giftBadgesCapability = Recipient.Capability.UNKNOWN;
|
||||||
|
this.pnpCapability = Recipient.Capability.UNKNOWN;
|
||||||
this.storageId = null;
|
this.storageId = null;
|
||||||
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
|
||||||
this.wallpaper = null;
|
this.wallpaper = null;
|
||||||
|
|
|
@ -133,6 +133,7 @@ object RecipientDatabaseTestUtils {
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.CHANGE_NUMBER, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.CHANGE_NUMBER, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.STORIES, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.STORIES, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
||||||
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.GIFT_BADGES, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.GIFT_BADGES, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
||||||
|
Recipient.Capability.deserialize(Bitmask.read(capabilities, RecipientDatabase.Capabilities.PNP, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
|
||||||
insightBannerTier,
|
insightBannerTier,
|
||||||
storageId,
|
storageId,
|
||||||
mentionSetting,
|
mentionSetting,
|
||||||
|
|
|
@ -159,10 +159,13 @@ public class AccountAttributes {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean giftBadges;
|
private boolean giftBadges;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean pnp;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Capabilities() {}
|
public Capabilities() {}
|
||||||
|
|
||||||
public Capabilities(boolean uuid, boolean gv2, boolean storage, boolean gv1Migration, boolean senderKey, boolean announcementGroup, boolean changeNumber, boolean stories, boolean giftBadges) {
|
public Capabilities(boolean uuid, boolean gv2, boolean storage, boolean gv1Migration, boolean senderKey, boolean announcementGroup, boolean changeNumber, boolean stories, boolean giftBadges, boolean pnp) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.gv2 = gv2;
|
this.gv2 = gv2;
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
|
@ -172,6 +175,7 @@ public class AccountAttributes {
|
||||||
this.changeNumber = changeNumber;
|
this.changeNumber = changeNumber;
|
||||||
this.stories = stories;
|
this.stories = stories;
|
||||||
this.giftBadges = giftBadges;
|
this.giftBadges = giftBadges;
|
||||||
|
this.pnp = pnp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUuid() {
|
public boolean isUuid() {
|
||||||
|
@ -209,5 +213,9 @@ public class AccountAttributes {
|
||||||
public boolean isGiftBadges() {
|
public boolean isGiftBadges() {
|
||||||
return giftBadges;
|
return giftBadges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPnp() {
|
||||||
|
return pnp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,10 +203,13 @@ public class SignalServiceProfile {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean giftBadges;
|
private boolean giftBadges;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean pnp;
|
||||||
|
|
||||||
@JsonCreator
|
@JsonCreator
|
||||||
public Capabilities() {}
|
public Capabilities() {}
|
||||||
|
|
||||||
public Capabilities(boolean storage, boolean gv1Migration, boolean senderKey, boolean announcementGroup, boolean changeNumber, boolean stories, boolean giftBadges) {
|
public Capabilities(boolean storage, boolean gv1Migration, boolean senderKey, boolean announcementGroup, boolean changeNumber, boolean stories, boolean giftBadges, boolean pnp) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.gv1Migration = gv1Migration;
|
this.gv1Migration = gv1Migration;
|
||||||
this.senderKey = senderKey;
|
this.senderKey = senderKey;
|
||||||
|
@ -214,6 +217,7 @@ public class SignalServiceProfile {
|
||||||
this.changeNumber = changeNumber;
|
this.changeNumber = changeNumber;
|
||||||
this.stories = stories;
|
this.stories = stories;
|
||||||
this.giftBadges = giftBadges;
|
this.giftBadges = giftBadges;
|
||||||
|
this.pnp = pnp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStorage() {
|
public boolean isStorage() {
|
||||||
|
@ -243,6 +247,10 @@ public class SignalServiceProfile {
|
||||||
public boolean isGiftBadges() {
|
public boolean isGiftBadges() {
|
||||||
return giftBadges;
|
return giftBadges;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPnp() {
|
||||||
|
return pnp;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {
|
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
package org.whispersystems.signalservice.api.account;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
public final class AccountAttributesTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void can_write_account_attributes() {
|
|
||||||
String json = JsonUtil.toJson(new AccountAttributes("skey",
|
|
||||||
123,
|
|
||||||
true,
|
|
||||||
"1234",
|
|
||||||
"reglock1234",
|
|
||||||
new byte[10],
|
|
||||||
false,
|
|
||||||
new AccountAttributes.Capabilities(true, true, true, true, true, true, true, true, true),
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
321));
|
|
||||||
assertEquals("{\"signalingKey\":\"skey\"," +
|
|
||||||
"\"registrationId\":123," +
|
|
||||||
"\"voice\":true," +
|
|
||||||
"\"video\":true," +
|
|
||||||
"\"fetchesMessages\":true," +
|
|
||||||
"\"pin\":\"1234\"," +
|
|
||||||
"\"registrationLock\":\"reglock1234\"," +
|
|
||||||
"\"unidentifiedAccessKey\":\"AAAAAAAAAAAAAA==\"," +
|
|
||||||
"\"unrestrictedUnidentifiedAccess\":false," +
|
|
||||||
"\"discoverableByPhoneNumber\":false," +
|
|
||||||
"\"capabilities\":{\"uuid\":true,\"storage\":true,\"senderKey\":true,\"announcementGroup\":true,\"changeNumber\":true,\"stories\":true,\"giftBadges\":true,\"gv2-3\":true,\"gv1-migration\":true}," +
|
|
||||||
"\"name\":null,\"pniRegistrationId\":321}", json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gv2_true() {
|
|
||||||
String json = JsonUtil.toJson(new AccountAttributes.Capabilities(false, true, false, false, false, false, false, false, false));
|
|
||||||
assertEquals("{\"uuid\":false,\"storage\":false,\"senderKey\":false,\"announcementGroup\":false,\"changeNumber\":false,\"stories\":false,\"giftBadges\":false,\"gv2-3\":true,\"gv1-migration\":false}", json);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void gv2_false() {
|
|
||||||
String json = JsonUtil.toJson(new AccountAttributes.Capabilities(false, false, false, false, false, false, false, false, false));
|
|
||||||
assertEquals("{\"uuid\":false,\"storage\":false,\"senderKey\":false,\"announcementGroup\":false,\"changeNumber\":false,\"stories\":false,\"giftBadges\":false,\"gv2-3\":false,\"gv1-migration\":false}", json);
|
|
||||||
}
|
|
||||||
}
|
|
Ładowanie…
Reference in New Issue