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)))
|
||||
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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 Stories? " + capabilities.isStories() +
|
||||
"\n Gift Badges? " + capabilities.isGiftBadges() +
|
||||
"\n PNP? " + capabilities.isPnp() +
|
||||
"\n UUID? " + capabilities.isUuid());
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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