Add PNP linked device initialization job.

Co-authored-by: Greyson Parrelli <greyson@signal.org>
fork-5.53.8
Cody Henthorne 2022-09-09 20:17:41 -04:00 zatwierdzone przez Greyson Parrelli
rodzic e2a7ed86e4
commit beee3b7dc3
18 zmienionych plików z 230 dodań i 68 usunięć

Wyświetl plik

@ -108,7 +108,7 @@ class SignalActivityRule(private val othersCount: Int = 4) : ExternalResource()
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, true, true, true, true, true))
SignalDatabase.recipients.setCapabilities(recipientId, SignalServiceProfile.Capabilities(true, true, true, true, true, true, true, true))
SignalDatabase.recipients.setProfileSharing(recipientId, true)
ApplicationDependencies.getProtocolStore().aci().saveIdentity(SignalProtocolAddress(aci.toString(), 0), IdentityKeyUtil.generateIdentityKeyPair().publicKey)
others += recipientId

Wyświetl plik

@ -20,6 +20,6 @@ public final class AppCapabilities {
* asking if the user has set a Signal PIN or not.
*/
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());
}
}

Wyświetl plik

@ -56,6 +56,7 @@ import org.thoughtcrime.securesms.jobs.FcmRefreshJob;
import org.thoughtcrime.securesms.jobs.FontDownloaderJob;
import org.thoughtcrime.securesms.jobs.GroupV2UpdateSelfProfileKeyJob;
import org.thoughtcrime.securesms.jobs.MultiDeviceContactUpdateJob;
import org.thoughtcrime.securesms.jobs.PnpInitializeDevicesJob;
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob;
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
import org.thoughtcrime.securesms.jobs.PushNotificationReceiveJob;
@ -206,6 +207,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
.addPostRender(CheckServiceReachabilityJob::enqueueIfNecessary)
.addPostRender(GroupV2UpdateSelfProfileKeyJob::enqueueForGroupsIfNecessary)
.addPostRender(StoryOnboardingDownloadJob.Companion::enqueueIfNeeded)
.addPostRender(PnpInitializeDevicesJob::enqueueIfNecessary)
.execute();
Log.d(TAG, "onCreate() took " + (System.currentTimeMillis() - startTime) + " ms");

Wyświetl plik

@ -58,7 +58,10 @@ class ChangeNumberLockActivity : PassphraseRequiredActivity() {
Single.just(false)
} else {
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 }
}
}

Wyświetl plik

@ -44,14 +44,44 @@ import org.whispersystems.signalservice.internal.push.exceptions.MismatchedDevic
import java.io.IOException
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.concurrent.locks.ReentrantLock
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(
private val accountManager: SignalServiceAccountManager = ApplicationDependencies.getSignalServiceAccountManager(),
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 {
return Completable.create { emitter ->
ApplicationDependencies
@ -59,17 +89,23 @@ class ChangeNumberRepository(
.addDecryptionDrainedListener {
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 {
var completed = false
var attempts = 0
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
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)
changeNumberResponse = accountManager.changeNumber(request)
@ -84,7 +120,7 @@ class ChangeNumberRepository(
}
changeNumberResponse
}.subscribeOn(Schedulers.io())
}.subscribeOn(Schedulers.single())
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
}
@ -114,7 +150,13 @@ class ChangeNumberRepository(
lateinit var changeNumberResponse: ServiceResponse<VerifyAccountResponse>
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)
changeNumberResponse = accountManager.changeNumber(request)
@ -129,14 +171,14 @@ class ChangeNumberRepository(
}
VerifyAccountRepository.VerifyAccountWithRegistrationLockResponse.from(changeNumberResponse, kbsData)
}.subscribeOn(Schedulers.io())
}.subscribeOn(Schedulers.single())
.onErrorReturn { t -> ServiceResponse.forExecutionError(t) }
}
@Suppress("UsePropertyAccessSyntax")
fun whoAmI(): Single<WhoAmIResponse> {
return Single.fromCallable { ApplicationDependencies.getSignalServiceAccountManager().getWhoAmI() }
.subscribeOn(Schedulers.io())
.subscribeOn(Schedulers.single())
}
@WorkerThread
@ -145,7 +187,7 @@ class ChangeNumberRepository(
SignalDatabase.recipients.updateSelfPhone(e164, pni)
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")
SignalDatabase.recipients.rotateStorageId(Recipient.self().id)
StorageSyncHelper.scheduleSyncForDataChange()
@ -198,6 +240,9 @@ class ChangeNumberRepository(
System.currentTimeMillis(),
true
)
SignalStore.misc().setPniInitializedDevices(true)
ApplicationDependencies.getGroupsV2Authorization().clear()
}
Recipient.self().live().refresh()
@ -227,7 +272,7 @@ class ChangeNumberRepository(
SignalStore.certificateValues().setUnidentifiedAccessCertificate(certificateType, certificate)
}
}.subscribeOn(Schedulers.io())
}.subscribeOn(Schedulers.single())
}
@Suppress("UsePropertyAccessSyntax")
@ -235,12 +280,13 @@ class ChangeNumberRepository(
private fun createChangeNumberRequest(
code: String,
newE164: String,
registrationLock: String?
registrationLock: String?,
pniUpdateMode: Boolean
): ChangeNumberRequestData {
val selfIdentifier: String = SignalStore.account().requireAci().toString()
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 devicePniSignedPreKeys = mutableMapOf<Int, SignedPreKeyEntity>()
val pniRegistrationIds = mutableMapOf<Int, Int>()
@ -253,14 +299,23 @@ class ChangeNumberRepository(
.forEach { deviceId ->
// Signed Prekeys
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 {
PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), pniIdentity.privateKey)
}
devicePniSignedPreKeys[deviceId] = SignedPreKeyEntity(signedPreKeyRecord.id, signedPreKeyRecord.keyPair.publicKey, signedPreKeyRecord.signature)
// Registration Ids
var pniRegistrationId = -1
var pniRegistrationId = if (deviceId == primaryDeviceId && pniUpdateMode) {
SignalStore.account().pniRegistrationId
} else {
-1
}
while (pniRegistrationId < 0 || pniRegistrationIds.values.contains(pniRegistrationId)) {
pniRegistrationId = KeyHelper.generateRegistrationId(false)
}

Wyświetl plik

@ -123,13 +123,13 @@ class ChangeNumberViewModel(
override fun verifyCodeWithoutRegistrationLock(code: String): Single<VerifyAccountResponseProcessor> {
return super.verifyCodeWithoutRegistrationLock(code)
.doOnSubscribe { SignalStore.misc().lockChangeNumber() }
.compose(ChangeNumberRepository::acquireReleaseChangeNumberLock)
.flatMap(this::attemptToUnlockChangeNumber)
}
override fun verifyCodeAndRegisterAccountWithRegistrationLock(pin: String): Single<VerifyCodeWithRegistrationLockResponseProcessor> {
return super.verifyCodeAndRegisterAccountWithRegistrationLock(pin)
.doOnSubscribe { SignalStore.misc().lockChangeNumber() }
.compose(ChangeNumberRepository::acquireReleaseChangeNumberLock)
.flatMap(this::attemptToUnlockChangeNumber)
}

Wyświetl plik

@ -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.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.PNP, Capabilities.BIT_LENGTH, Recipient.Capability.fromBoolean(capabilities.isPnp).serialize().toLong())
val values = ContentValues(1).apply {
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()),
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()),
pnpCapability = Recipient.Capability.deserialize(Bitmask.read(capabilities, Capabilities.PNP, Capabilities.BIT_LENGTH).toInt()),
insightsBannerTier = InsightsBannerTier.fromId(cursor.requireInt(SEEN_INVITE_REMINDER)),
storageId = Base64.decodeNullableOrThrow(cursor.requireString(STORAGE_SERVICE_ID)),
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 STORIES = 5
const val GIFT_BADGES = 6
const val PNP = 7
}
enum class VibrateState(val id: Int) {

Wyświetl plik

@ -70,6 +70,7 @@ data class RecipientRecord(
val changeNumberCapability: Recipient.Capability,
val storiesCapability: Recipient.Capability,
val giftBadgesCapability: Recipient.Capability,
val pnpCapability: Recipient.Capability,
val insightsBannerTier: InsightsBannerTier,
val storageId: ByteArray?,
val mentionSetting: MentionSetting,

Wyświetl plik

@ -135,6 +135,7 @@ public final class JobManagerFactories {
put(PaymentNotificationSendJob.KEY, new PaymentNotificationSendJob.Factory());
put(PaymentSendJob.KEY, new PaymentSendJob.Factory());
put(PaymentTransactionCheckJob.KEY, new PaymentTransactionCheckJob.Factory());
put(PnpInitializeDevicesJob.KEY, new PnpInitializeDevicesJob.Factory());
put(PreKeysSyncJob.KEY, new PreKeysSyncJob.Factory());
put(ProfileKeySendJob.KEY, new ProfileKeySendJob.Factory());
put(ProfileUploadJob.KEY, new ProfileUploadJob.Factory());

Wyświetl plik

@ -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)
}
}
}

Wyświetl plik

@ -117,6 +117,7 @@ public class RefreshAttributesJob extends BaseJob {
"\n Change Number? " + capabilities.isChangeNumber() +
"\n Stories? " + capabilities.isStories() +
"\n Gift Badges? " + capabilities.isGiftBadges() +
"\n PNP? " + capabilities.isPnp() +
"\n UUID? " + capabilities.isUuid());
SignalServiceAccountManager signalAccountManager = ApplicationDependencies.getSignalServiceAccountManager();

Wyświetl plik

@ -26,6 +26,7 @@ public final class MiscellaneousValues extends SignalStoreValues {
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_FOREGROUND_TIME = "misc.last_foreground_time";
private static final String PNI_INITIALIZED_DEVICES = "misc.pni_initialized_devices";
MiscellaneousValues(@NonNull KeyValueStore store) {
super(store);
@ -184,4 +185,12 @@ public final class MiscellaneousValues extends SignalStoreValues {
public void setLastForegroundTime(long 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);
}
}

Wyświetl plik

@ -124,6 +124,7 @@ public class Recipient {
private final Capability changeNumberCapability;
private final Capability storiesCapability;
private final Capability giftBadgesCapability;
private final Capability pnpCapability;
private final InsightsBannerTier insightsBannerTier;
private final byte[] storageId;
private final MentionSetting mentionSetting;
@ -426,6 +427,7 @@ public class Recipient {
this.changeNumberCapability = Capability.UNKNOWN;
this.storiesCapability = Capability.UNKNOWN;
this.giftBadgesCapability = Capability.UNKNOWN;
this.pnpCapability = Capability.UNKNOWN;
this.storageId = null;
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
this.wallpaper = null;
@ -485,6 +487,7 @@ public class Recipient {
this.changeNumberCapability = details.changeNumberCapability;
this.storiesCapability = details.storiesCapability;
this.giftBadgesCapability = details.giftBadgesCapability;
this.pnpCapability = details.pnpCapability;
this.storageId = details.storageId;
this.mentionSetting = details.mentionSetting;
this.wallpaper = details.wallpaper;
@ -1043,6 +1046,10 @@ public class Recipient {
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.
*/

Wyświetl plik

@ -75,6 +75,7 @@ public class RecipientDetails {
final Recipient.Capability changeNumberCapability;
final Recipient.Capability storiesCapability;
final Recipient.Capability giftBadgesCapability;
final Recipient.Capability pnpCapability;
final InsightsBannerTier insightsBannerTier;
final byte[] storageId;
final MentionSetting mentionSetting;
@ -139,6 +140,7 @@ public class RecipientDetails {
this.changeNumberCapability = record.getChangeNumberCapability();
this.storiesCapability = record.getStoriesCapability();
this.giftBadgesCapability = record.getGiftBadgesCapability();
this.pnpCapability = record.getPnpCapability();
this.insightsBannerTier = record.getInsightsBannerTier();
this.storageId = record.getStorageId();
this.mentionSetting = record.getMentionSetting();
@ -199,6 +201,7 @@ public class RecipientDetails {
this.changeNumberCapability = Recipient.Capability.UNKNOWN;
this.storiesCapability = Recipient.Capability.UNKNOWN;
this.giftBadgesCapability = Recipient.Capability.UNKNOWN;
this.pnpCapability = Recipient.Capability.UNKNOWN;
this.storageId = null;
this.mentionSetting = MentionSetting.ALWAYS_NOTIFY;
this.wallpaper = null;

Wyświetl plik

@ -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.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.PNP, RecipientDatabase.Capabilities.BIT_LENGTH).toInt()),
insightBannerTier,
storageId,
mentionSetting,

Wyświetl plik

@ -159,10 +159,13 @@ public class AccountAttributes {
@JsonProperty
private boolean giftBadges;
@JsonProperty
private boolean pnp;
@JsonCreator
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.gv2 = gv2;
this.storage = storage;
@ -172,6 +175,7 @@ public class AccountAttributes {
this.changeNumber = changeNumber;
this.stories = stories;
this.giftBadges = giftBadges;
this.pnp = pnp;
}
public boolean isUuid() {
@ -209,5 +213,9 @@ public class AccountAttributes {
public boolean isGiftBadges() {
return giftBadges;
}
public boolean isPnp() {
return pnp;
}
}
}

Wyświetl plik

@ -203,10 +203,13 @@ public class SignalServiceProfile {
@JsonProperty
private boolean giftBadges;
@JsonProperty
private boolean pnp;
@JsonCreator
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.gv1Migration = gv1Migration;
this.senderKey = senderKey;
@ -214,6 +217,7 @@ public class SignalServiceProfile {
this.changeNumber = changeNumber;
this.stories = stories;
this.giftBadges = giftBadges;
this.pnp = pnp;
}
public boolean isStorage() {
@ -243,6 +247,10 @@ public class SignalServiceProfile {
public boolean isGiftBadges() {
return giftBadges;
}
public boolean isPnp() {
return pnp;
}
}
public ExpiringProfileKeyCredentialResponse getExpiringProfileKeyCredentialResponse() {

Wyświetl plik

@ -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);
}
}