Add support for system names on the ContactRecord.

fork-5.53.8
Greyson Parrelli 2022-09-28 14:09:44 -04:00
rodzic 6e5f28339d
commit 1999db97f2
11 zmienionych plików z 197 dodań i 66 usunięć

Wyświetl plik

@ -925,6 +925,21 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
ApplicationDependencies.getDatabaseObserver().notifyRecipientChanged(recipientId)
}
fun markAllSystemContactsNeedsSync() {
writableDatabase.withinTransaction { db ->
db
.select(ID)
.from(TABLE_NAME)
.where("$SYSTEM_CONTACT_URI NOT NULL")
.run()
.use { cursor ->
while (cursor.moveToNext()) {
rotateStorageId(RecipientId.from(cursor.requireLong(ID)))
}
}
}
}
fun applyStorageIdUpdates(storageIds: Map<RecipientId, StorageId>) {
val db = writableDatabase
db.beginTransaction()
@ -3667,8 +3682,9 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
private fun getValuesForStorageContact(contact: SignalContactRecord, isInsert: Boolean): ContentValues {
return ContentValues().apply {
val profileName = ProfileName.fromParts(contact.givenName.orElse(null), contact.familyName.orElse(null))
val username: String? = contact.username.orElse(null)
val profileName = ProfileName.fromParts(contact.profileGivenName.orElse(null), contact.profileFamilyName.orElse(null))
val systemName = ProfileName.fromParts(contact.systemGivenName.orElse(null), contact.systemFamilyName.orElse(null))
val username = contact.username.orElse(null)
if (contact.serviceId.isValid) {
put(SERVICE_ID, contact.serviceId.toString())
@ -3682,6 +3698,9 @@ open class RecipientDatabase(context: Context, databaseHelper: SignalDatabase) :
put(PROFILE_GIVEN_NAME, profileName.givenName)
put(PROFILE_FAMILY_NAME, profileName.familyName)
put(PROFILE_JOINED_NAME, profileName.toString())
put(SYSTEM_GIVEN_NAME, systemName.givenName)
put(SYSTEM_FAMILY_NAME, systemName.familyName)
put(SYSTEM_JOINED_NAME, systemName.toString())
put(PROFILE_KEY, contact.profileKey.map { source -> Base64.encodeBytes(source) }.orElse(null))
put(USERNAME, if (TextUtils.isEmpty(username)) null else username)
put(PROFILE_SHARING, if (contact.isProfileSharingEnabled) "1" else "0")

Wyświetl plik

@ -61,6 +61,7 @@ import org.thoughtcrime.securesms.migrations.StickerLaunchMigrationJob;
import org.thoughtcrime.securesms.migrations.StickerMyDailyLifeMigrationJob;
import org.thoughtcrime.securesms.migrations.StorageCapabilityMigrationJob;
import org.thoughtcrime.securesms.migrations.StorageServiceMigrationJob;
import org.thoughtcrime.securesms.migrations.StorageServiceSystemNameMigrationJob;
import org.thoughtcrime.securesms.migrations.SyncDistributionListsMigrationJob;
import org.thoughtcrime.securesms.migrations.TrimByLengthSettingsMigrationJob;
import org.thoughtcrime.securesms.migrations.UserNotificationMigrationJob;
@ -221,6 +222,7 @@ public final class JobManagerFactories {
put(StickerMyDailyLifeMigrationJob.KEY, new StickerMyDailyLifeMigrationJob.Factory());
put(StorageCapabilityMigrationJob.KEY, new StorageCapabilityMigrationJob.Factory());
put(StorageServiceMigrationJob.KEY, new StorageServiceMigrationJob.Factory());
put(StorageServiceSystemNameMigrationJob.KEY, new StorageServiceSystemNameMigrationJob.Factory());
put(TrimByLengthSettingsMigrationJob.KEY, new TrimByLengthSettingsMigrationJob.Factory());
put(UserNotificationMigrationJob.KEY, new UserNotificationMigrationJob.Factory());
put(UuidMigrationJob.KEY, new UuidMigrationJob.Factory());

Wyświetl plik

@ -108,9 +108,10 @@ public class ApplicationMigrations {
static final int REFRESH_PNI_REGISTRATION_ID = 64;
static final int KBS_MIGRATION_2 = 65;
static final int PNI_2 = 66;
static final int SYSTEM_NAME_SYNC = 67;
}
public static final int CURRENT_VERSION = 66;
public static final int CURRENT_VERSION = 67;
/**
* This *must* be called after the {@link JobManager} has been instantiated, but *before* the call
@ -476,6 +477,10 @@ public class ApplicationMigrations {
jobs.put(Version.PNI_2, new PniMigrationJob());
}
if (lastSeenVersion < Version.SYSTEM_NAME_SYNC) {
jobs.put(Version.SYSTEM_NAME_SYNC, new StorageServiceSystemNameMigrationJob());
}
return jobs;
}

Wyświetl plik

@ -0,0 +1,56 @@
package org.thoughtcrime.securesms.migrations;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.Job;
import org.thoughtcrime.securesms.jobs.DownloadLatestEmojiDataJob;
import org.thoughtcrime.securesms.jobs.EmojiSearchIndexDownloadJob;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
/**
* Added for when we started syncing contact names in storage service.
* Rotates the storageId of every system contact and then schedules a storage sync.
*/
public final class StorageServiceSystemNameMigrationJob extends MigrationJob {
public static final String KEY = "StorageServiceSystemNameMigrationJob";
StorageServiceSystemNameMigrationJob() {
this(new Parameters.Builder().build());
}
private StorageServiceSystemNameMigrationJob(@NonNull Parameters parameters) {
super(parameters);
}
@Override
public boolean isUiBlocking() {
return false;
}
@Override
public @NonNull String getFactoryKey() {
return KEY;
}
@Override
public void performMigration() {
SignalDatabase.recipients().markAllSystemContactsNeedsSync();
StorageSyncHelper.scheduleSyncForDataChange();
}
@Override
boolean shouldRetry(@NonNull Exception e) {
return false;
}
public static class Factory implements Job.Factory<StorageServiceSystemNameMigrationJob> {
@Override
public @NonNull StorageServiceSystemNameMigrationJob create(@NonNull Parameters parameters, @NonNull Data data) {
return new StorageServiceSystemNameMigrationJob(parameters);
}
}
}

Wyświetl plik

@ -1,7 +1,5 @@
package org.thoughtcrime.securesms.storage;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -17,7 +15,6 @@ import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.push.ACI;
import org.whispersystems.signalservice.api.push.PNI;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.util.OptionalUtil;
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState;
@ -122,15 +119,15 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
remote = remote.withoutPni();
}
String givenName;
String familyName;
String profileGivenName;
String profileFamilyName;
if (remote.getGivenName().isPresent() || remote.getFamilyName().isPresent()) {
givenName = remote.getGivenName().orElse("");
familyName = remote.getFamilyName().orElse("");
if (remote.getProfileGivenName().isPresent() || remote.getProfileFamilyName().isPresent()) {
profileGivenName = remote.getProfileGivenName().orElse("");
profileFamilyName = remote.getProfileFamilyName().orElse("");
} else {
givenName = local.getGivenName().orElse("");
familyName = local.getFamilyName().orElse("");
profileGivenName = local.getProfileGivenName().orElse("");
profileFamilyName = local.getProfileFamilyName().orElse("");
}
IdentityState identityState;
@ -198,8 +195,10 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
boolean hideStory = remote.shouldHideStory();
long unregisteredTimestamp = remote.getUnregisteredTimestamp();
boolean hidden = remote.isHidden();
boolean matchesRemote = doParamsMatch(remote, unknownFields, serviceId, pni, e164, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
boolean matchesLocal = doParamsMatch(local, unknownFields, serviceId, pni, e164, givenName, familyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
String systemGivenName = SignalStore.account().isPrimaryDevice() ? local.getSystemGivenName().orElse("") : remote.getSystemGivenName().orElse("");
String systemFamilyName = SignalStore.account().isPrimaryDevice() ? local.getSystemFamilyName().orElse("") : remote.getSystemFamilyName().orElse("");
boolean matchesRemote = doParamsMatch(remote, unknownFields, serviceId, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
boolean matchesLocal = doParamsMatch(local, unknownFields, serviceId, pni, e164, profileGivenName, profileFamilyName, systemGivenName, systemFamilyName, profileKey, username, identityState, identityKey, blocked, profileSharing, archived, forcedUnread, muteUntil, hideStory, unregisteredTimestamp, hidden);
if (matchesRemote) {
return remote;
@ -209,8 +208,10 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
return new SignalContactRecord.Builder(keyGenerator.generate(), serviceId, unknownFields)
.setE164(e164)
.setPni(pni)
.setGivenName(givenName)
.setFamilyName(familyName)
.setProfileGivenName(profileGivenName)
.setProfileFamilyName(profileFamilyName)
.setSystemGivenName(systemGivenName)
.setSystemFamilyName(systemFamilyName)
.setProfileKey(profileKey)
.setUsername(username)
.setIdentityState(identityState)
@ -254,8 +255,10 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
@NonNull ServiceId serviceId,
@Nullable PNI pni,
@Nullable String e164,
@NonNull String givenName,
@NonNull String familyName,
@NonNull String profileGivenName,
@NonNull String profileFamilyName,
@NonNull String systemGivenName,
@NonNull String systemFamilyName,
@Nullable byte[] profileKey,
@NonNull String username,
@Nullable IdentityState identityState,
@ -269,23 +272,25 @@ public class ContactRecordProcessor extends DefaultStorageRecordProcessor<Signal
long unregisteredTimestamp,
boolean hidden)
{
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
Objects.equals(contact.getServiceId(), serviceId) &&
Objects.equals(contact.getPni().orElse(null), pni) &&
Objects.equals(contact.getNumber().orElse(null), e164) &&
Objects.equals(contact.getGivenName().orElse(""), givenName) &&
Objects.equals(contact.getFamilyName().orElse(""), familyName) &&
Arrays.equals(contact.getProfileKey().orElse(null), profileKey) &&
Objects.equals(contact.getUsername().orElse(""), username) &&
Objects.equals(contact.getIdentityState(), identityState) &&
Arrays.equals(contact.getIdentityKey().orElse(null), identityKey) &&
contact.isBlocked() == blocked &&
contact.isProfileSharingEnabled() == profileSharing &&
contact.isArchived() == archived &&
contact.isForcedUnread() == forcedUnread &&
contact.getMuteUntil() == muteUntil &&
contact.shouldHideStory() == hideStory &&
contact.getUnregisteredTimestamp() == unregisteredTimestamp &&
return Arrays.equals(contact.serializeUnknownFields(), unknownFields) &&
Objects.equals(contact.getServiceId(), serviceId) &&
Objects.equals(contact.getPni().orElse(null), pni) &&
Objects.equals(contact.getNumber().orElse(null), e164) &&
Objects.equals(contact.getProfileGivenName().orElse(""), profileGivenName) &&
Objects.equals(contact.getProfileFamilyName().orElse(""), profileFamilyName) &&
Objects.equals(contact.getSystemGivenName().orElse(""), systemGivenName) &&
Objects.equals(contact.getSystemFamilyName().orElse(""), systemFamilyName) &&
Arrays.equals(contact.getProfileKey().orElse(null), profileKey) &&
Objects.equals(contact.getUsername().orElse(""), username) &&
Objects.equals(contact.getIdentityState(), identityState) &&
Arrays.equals(contact.getIdentityKey().orElse(null), identityKey) &&
contact.isBlocked() == blocked &&
contact.isProfileSharingEnabled() == profileSharing &&
contact.isArchived() == archived &&
contact.isForcedUnread() == forcedUnread &&
contact.getMuteUntil() == muteUntil &&
contact.shouldHideStory() == hideStory &&
contact.getUnregisteredTimestamp() == unregisteredTimestamp &&
contact.isHidden() == hidden;
}
}

Wyświetl plik

@ -11,15 +11,12 @@ import org.thoughtcrime.securesms.database.IdentityDatabase;
import org.thoughtcrime.securesms.database.RecipientDatabase;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.DistributionListId;
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode;
import org.thoughtcrime.securesms.database.model.DistributionListRecord;
import org.thoughtcrime.securesms.database.model.RecipientRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.keyvalue.PhoneNumberPrivacyValues;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.subscription.Subscriber;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
@ -34,7 +31,6 @@ import org.whispersystems.signalservice.internal.storage.protos.AccountRecord;
import org.whispersystems.signalservice.internal.storage.protos.ContactRecord.IdentityState;
import org.whispersystems.signalservice.internal.storage.protos.GroupV2Record;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@ -116,8 +112,10 @@ public final class StorageSyncModels {
.setE164(recipient.getE164())
.setPni(recipient.getPni())
.setProfileKey(recipient.getProfileKey())
.setGivenName(recipient.getProfileName().getGivenName())
.setFamilyName(recipient.getProfileName().getFamilyName())
.setProfileGivenName(recipient.getProfileName().getGivenName())
.setProfileFamilyName(recipient.getProfileName().getFamilyName())
.setSystemGivenName(recipient.getSystemProfileName().getGivenName())
.setSystemFamilyName(recipient.getSystemProfileName().getFamilyName())
.setBlocked(recipient.isBlocked())
.setProfileSharingEnabled(recipient.isProfileSharing() || recipient.getSystemContactUri() != null)
.setIdentityKey(recipient.getSyncExtras().getIdentityKey())

Wyświetl plik

@ -3,17 +3,23 @@ package org.thoughtcrime.securesms.storage
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Rule
import org.junit.Test
import org.mockito.Mock
import org.mockito.MockedStatic
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.internal.configuration.plugins.Plugins
import org.mockito.internal.junit.JUnitRule
import org.mockito.junit.MockitoRule
import org.mockito.quality.Strictness
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.keyvalue.AccountValues
import org.thoughtcrime.securesms.keyvalue.SignalStore
import org.thoughtcrime.securesms.testutil.EmptyLogger
import org.thoughtcrime.securesms.util.FeatureFlags
import org.whispersystems.signalservice.api.push.ACI
@ -35,6 +41,16 @@ class ContactRecordProcessorTest {
@Mock
lateinit var featureFlags: MockedStatic<FeatureFlags>
@Mock
lateinit var signalStore: MockedStatic<SignalStore>
@Before
fun setup() {
val mockAccountValues = mock(AccountValues::class.java)
Mockito.lenient().`when`(mockAccountValues.isPrimaryDevice).thenReturn(true)
signalStore.`when`<AccountValues> { SignalStore.account() }.thenReturn(mockAccountValues)
}
@Test
fun `isInvalid, normal, false`() {
// GIVEN

Wyświetl plik

@ -14,7 +14,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.storage.StorageSyncHelper.IdDifferenceResult;
import org.thoughtcrime.securesms.util.FeatureFlags;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.storage.SignalAccountRecord;
import org.whispersystems.signalservice.api.storage.SignalContactRecord;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
@ -178,7 +177,7 @@ public final class StorageSyncHelperTest {
{
return new SignalContactRecord.Builder(byteArray(key), aci, null)
.setE164(e164)
.setGivenName(profileName);
.setProfileGivenName(profileName);
}
private static <E extends SignalRecord> StorageRecordUpdate<E> update(E oldRecord, E newRecord) {

Wyświetl plik

@ -28,8 +28,10 @@ public final class SignalContactRecord implements SignalRecord {
private final ServiceId serviceId;
private final Optional<PNI> pni;
private final Optional<String> e164;
private final Optional<String> givenName;
private final Optional<String> familyName;
private final Optional<String> profileGivenName;
private final Optional<String> profileFamilyName;
private final Optional<String> systemGivenName;
private final Optional<String> systemFamilyName;
private final Optional<byte[]> profileKey;
private final Optional<String> username;
private final Optional<byte[]> identityKey;
@ -39,14 +41,16 @@ public final class SignalContactRecord implements SignalRecord {
this.proto = proto;
this.hasUnknownFields = ProtoUtil.hasUnknownFields(proto);
this.serviceId = ServiceId.parseOrUnknown(proto.getServiceId());
this.pni = OptionalUtil.absentIfEmpty(proto.getServicePni()).map(PNI::parseOrNull);
this.e164 = OptionalUtil.absentIfEmpty(proto.getServiceE164());
this.givenName = OptionalUtil.absentIfEmpty(proto.getGivenName());
this.familyName = OptionalUtil.absentIfEmpty(proto.getFamilyName());
this.profileKey = OptionalUtil.absentIfEmpty(proto.getProfileKey());
this.username = OptionalUtil.absentIfEmpty(proto.getUsername());
this.identityKey = OptionalUtil.absentIfEmpty(proto.getIdentityKey());
this.serviceId = ServiceId.parseOrUnknown(proto.getServiceId());
this.pni = OptionalUtil.absentIfEmpty(proto.getServicePni()).map(PNI::parseOrNull);
this.e164 = OptionalUtil.absentIfEmpty(proto.getServiceE164());
this.profileGivenName = OptionalUtil.absentIfEmpty(proto.getGivenName());
this.profileFamilyName = OptionalUtil.absentIfEmpty(proto.getFamilyName());
this.systemGivenName = OptionalUtil.absentIfEmpty(proto.getSystemGivenName());
this.systemFamilyName = OptionalUtil.absentIfEmpty(proto.getSystemFamilyName());
this.profileKey = OptionalUtil.absentIfEmpty(proto.getProfileKey());
this.username = OptionalUtil.absentIfEmpty(proto.getUsername());
this.identityKey = OptionalUtil.absentIfEmpty(proto.getIdentityKey());
}
@Override
@ -81,12 +85,20 @@ public final class SignalContactRecord implements SignalRecord {
diff.add("E164");
}
if (!Objects.equals(this.givenName, that.givenName)) {
diff.add("GivenName");
if (!Objects.equals(this.profileGivenName, that.profileGivenName)) {
diff.add("ProfileGivenName");
}
if (!Objects.equals(this.familyName, that.familyName)) {
diff.add("FamilyName");
if (!Objects.equals(this.profileFamilyName, that.profileFamilyName)) {
diff.add("ProfileFamilyName");
}
if (!Objects.equals(this.systemGivenName, that.systemGivenName)) {
diff.add("SystemGivenName");
}
if (!Objects.equals(this.systemFamilyName, that.systemFamilyName)) {
diff.add("SystemFamilyName");
}
if (!OptionalUtil.byteArrayEquals(this.profileKey, that.profileKey)) {
@ -167,12 +179,20 @@ public final class SignalContactRecord implements SignalRecord {
return e164;
}
public Optional<String> getGivenName() {
return givenName;
public Optional<String> getProfileGivenName() {
return profileGivenName;
}
public Optional<String> getFamilyName() {
return familyName;
public Optional<String> getProfileFamilyName() {
return profileFamilyName;
}
public Optional<String> getSystemGivenName() {
return systemGivenName;
}
public Optional<String> getSystemFamilyName() {
return systemFamilyName;
}
public Optional<byte[]> getProfileKey() {
@ -274,16 +294,26 @@ public final class SignalContactRecord implements SignalRecord {
return this;
}
public Builder setGivenName(String givenName) {
public Builder setProfileGivenName(String givenName) {
builder.setGivenName(givenName == null ? "" : givenName);
return this;
}
public Builder setFamilyName(String familyName) {
public Builder setProfileFamilyName(String familyName) {
builder.setFamilyName(familyName == null ? "" : familyName);
return this;
}
public Builder setSystemGivenName(String givenName) {
builder.setSystemGivenName(givenName == null ? "" : givenName);
return this;
}
public Builder setSystemFamilyName(String familyName) {
builder.setSystemFamilyName(familyName == null ? "" : familyName);
return this;
}
public Builder setProfileKey(byte[] profileKey) {
builder.setProfileKey(profileKey == null ? ByteString.EMPTY : ByteString.copyFrom(profileKey));
return this;

Wyświetl plik

@ -87,6 +87,8 @@ message ContactRecord {
uint64 mutedUntilTimestamp = 13;
bool hideStory = 14;
uint64 unregisteredAtTimestamp = 16;
string systemGivenName = 17;
string systemFamilyName = 18;
bool hidden = 19;
// NEXT ID: 20
}

Wyświetl plik

@ -2,7 +2,6 @@ package org.whispersystems.signalservice.api.storage;
import org.junit.Test;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@ -53,6 +52,6 @@ public class SignalContactRecordTest {
{
return new SignalContactRecord.Builder(byteArray(key), serviceId, null)
.setE164(e164)
.setGivenName(givenName);
.setProfileGivenName(givenName);
}
}