Add storage support for the AccountRecord.

This commit is contained in:
Greyson Parrelli
2020-03-18 16:31:45 -04:00
parent 7a038ab09d
commit 951a61117a
38 changed files with 1290 additions and 335 deletions

View File

@@ -39,6 +39,7 @@ import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
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;
import org.whispersystems.signalservice.api.storage.StorageId;
@@ -216,7 +217,7 @@ public class RecipientDatabase extends Database {
}
}
enum DirtyState {
public enum DirtyState {
CLEAN(0), UPDATE(1), INSERT(2), DELETE(3);
private final int id;
@@ -228,6 +229,10 @@ public class RecipientDatabase extends Database {
int getId() {
return id;
}
public static DirtyState fromId(int id) {
return values()[id];
}
}
public enum GroupType {
@@ -401,23 +406,35 @@ public class RecipientDatabase extends Database {
}
}
public @NonNull DirtyState getDirtyState(@NonNull RecipientId recipientId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
try (Cursor cursor = db.query(TABLE_NAME, new String[] { DIRTY }, ID_WHERE, new String[] { recipientId.serialize() }, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return DirtyState.fromId(cursor.getInt(cursor.getColumnIndexOrThrow(DIRTY)));
}
}
return DirtyState.CLEAN;
}
public @NonNull List<RecipientSettings> getPendingRecipientSyncUpdates() {
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL";
String[] args = new String[] { String.valueOf(DirtyState.UPDATE.getId()) };
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL AND " + TABLE_NAME + "." + ID + " != ?";
String[] args = new String[] { String.valueOf(DirtyState.UPDATE.getId()), Recipient.self().getId().serialize() };
return getRecipientSettings(query, args);
}
public @NonNull List<RecipientSettings> getPendingRecipientSyncInsertions() {
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL";
String[] args = new String[] { String.valueOf(DirtyState.INSERT.getId()) };
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL AND " + TABLE_NAME + "." + ID + " != ?";
String[] args = new String[] { String.valueOf(DirtyState.INSERT.getId()), Recipient.self().getId().serialize() };
return getRecipientSettings(query, args);
}
public @NonNull List<RecipientSettings> getPendingRecipientSyncDeletions() {
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL";
String[] args = new String[] { String.valueOf(DirtyState.DELETE.getId()) };
String query = DIRTY + " = ? AND " + STORAGE_SERVICE_ID + " NOT NULL AND " + TABLE_NAME + "." + ID + " != ?";
String[] args = new String[] { String.valueOf(DirtyState.DELETE.getId()), Recipient.self().getId().serialize() };
return getRecipientSettings(query, args);
}
@@ -432,6 +449,10 @@ public class RecipientDatabase extends Database {
return null;
}
public void markNeedsSync(@NonNull RecipientId recipientId) {
markDirty(recipientId, DirtyState.UPDATE);
}
public void applyStorageIdUpdates(@NonNull Map<RecipientId, StorageId> storageIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
@@ -459,6 +480,7 @@ public class RecipientDatabase extends Database {
{
SQLiteDatabase db = databaseHelper.getWritableDatabase();
IdentityDatabase identityDatabase = DatabaseFactory.getIdentityDatabase(context);
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context);
db.beginTransaction();
@@ -492,9 +514,8 @@ public class RecipientDatabase extends Database {
}
}
if (Recipient.self().getId().equals(recipientId)) {
TextSecurePreferences.setProfileName(context, ProfileName.fromParts(insert.getGivenName().orNull(), insert.getFamilyName().orNull()));
}
threadDatabase.setArchived(recipientId, insert.isArchived());
Recipient.live(recipientId).refresh();
}
}
@@ -534,10 +555,18 @@ public class RecipientDatabase extends Database {
} catch (InvalidKeyException e) {
Log.w(TAG, "Failed to process identity key during update! Skipping.", e);
}
threadDatabase.setArchived(recipientId, update.getNew().isArchived());
Recipient.live(recipientId).refresh();
}
for (SignalGroupV1Record insert : groupV1Inserts) {
db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert));
Recipient recipient = Recipient.externalGroup(context, GroupUtil.getEncodedId(insert.getGroupId(), false));
threadDatabase.setArchived(recipient.getId(), insert.isArchived());
recipient.live().refresh();
}
for (RecordUpdate<SignalGroupV1Record> update : groupV1Updates) {
@@ -547,6 +576,11 @@ public class RecipientDatabase extends Database {
if (updateCount < 1) {
throw new AssertionError("Had an update, but it didn't match any rows!");
}
Recipient recipient = Recipient.externalGroup(context, GroupUtil.getEncodedId(update.getOld().getGroupId(), false));
threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived());
recipient.live().refresh();
}
db.setTransactionSuccessful();
@@ -555,6 +589,27 @@ public class RecipientDatabase extends Database {
}
}
public void applyStorageSyncUpdates(@NonNull StorageId storageId, SignalAccountRecord update) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
ContentValues values = new ContentValues();
ProfileName profileName = ProfileName.fromParts(update.getGivenName().orNull(), update.getFamilyName().orNull());
values.put(PROFILE_GIVEN_NAME, profileName.getGivenName());
values.put(PROFILE_FAMILY_NAME, profileName.getFamilyName());
values.put(PROFILE_JOINED_NAME, profileName.toString());
values.put(PROFILE_KEY, update.getProfileKey().transform(Base64::encodeBytes).orNull());
values.put(STORAGE_SERVICE_ID, Base64.encodeBytes(update.getId().getRaw()));
values.put(DIRTY, DirtyState.CLEAN.getId());
int updateCount = db.update(TABLE_NAME, values, STORAGE_SERVICE_ID + " = ?", new String[]{Base64.encodeBytes(storageId.getRaw())});
if (updateCount < 1) {
throw new AssertionError("Account update didn't match any rows!");
}
Recipient.self().live().refresh();
}
public void updatePhoneNumbers(@NonNull Map<String, String> mapping) {
if (mapping.isEmpty()) return;
@@ -641,19 +696,19 @@ public class RecipientDatabase extends Database {
}
/**
* @return All storage keys, excluding the ones that need to be deleted.
* @return All storage ids for ContactRecords, excluding the ones that need to be deleted.
*/
public List<StorageId> getAllStorageSyncKeys() {
return new ArrayList<>(getAllStorageSyncKeysMap().values());
public List<StorageId> getContactStorageSyncIds() {
return new ArrayList<>(getContactStorageSyncIdsMap().values());
}
/**
* @return All storage keys, excluding the ones that need to be deleted.
* @return All storage IDs for ContactRecords, excluding the ones that need to be deleted.
*/
public @NonNull Map<RecipientId, StorageId> getAllStorageSyncKeysMap() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = STORAGE_SERVICE_ID + " NOT NULL AND " + DIRTY + " != ?";
String[] args = new String[]{String.valueOf(DirtyState.DELETE)};
public @NonNull Map<RecipientId, StorageId> getContactStorageSyncIdsMap() {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = STORAGE_SERVICE_ID + " NOT NULL AND " + DIRTY + " != ? AND " + ID + " != ?";
String[] args = new String[]{String.valueOf(DirtyState.DELETE), Recipient.self().getId().serialize() };
Map<RecipientId, StorageId> out = new HashMap<>();
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ID, STORAGE_SERVICE_ID, GROUP_TYPE }, query, args, null, null, null)) {
@@ -920,7 +975,7 @@ public class RecipientDatabase extends Database {
if (update(updateQuery, valuesToSet)) {
markDirty(id, DirtyState.UPDATE);
Recipient.live(id).refresh();
ApplicationDependencies.getJobManager().add(new StorageSyncJob());
StorageSyncHelper.scheduleSyncForDataChange();
return true;
} else {
return false;
@@ -966,7 +1021,7 @@ public class RecipientDatabase extends Database {
if (update(id, contentValues)) {
markDirty(id, DirtyState.UPDATE);
Recipient.live(id).refresh();
ApplicationDependencies.getJobManager().add(new StorageSyncJob());
StorageSyncHelper.scheduleSyncForDataChange();
}
}
@@ -975,6 +1030,11 @@ public class RecipientDatabase extends Database {
contentValues.put(SIGNAL_PROFILE_AVATAR, profileAvatar);
if (update(id, contentValues)) {
Recipient.live(id).refresh();
if (id.equals(Recipient.self().getId())) {
markDirty(id, DirtyState.UPDATE);
StorageSyncHelper.scheduleSyncForDataChange();
}
}
}
@@ -984,7 +1044,7 @@ public class RecipientDatabase extends Database {
if (update(id, contentValues)) {
markDirty(id, DirtyState.UPDATE);
Recipient.live(id).refresh();
ApplicationDependencies.getJobManager().add(new StorageSyncJob());
StorageSyncHelper.scheduleSyncForDataChange();
}
}
@@ -1002,6 +1062,7 @@ public class RecipientDatabase extends Database {
if (update(id, contentValues)) {
markDirty(id, DirtyState.UPDATE);
Recipient.live(id).refresh();
StorageSyncHelper.scheduleSyncForDataChange();
}
}
@@ -1017,8 +1078,10 @@ public class RecipientDatabase extends Database {
ContentValues contentValues = new ContentValues(1);
contentValues.put(USERNAME, username);
update(id, contentValues);
Recipient.live(id).refresh();
if (update(id, contentValues)) {
Recipient.live(id).refresh();
StorageSyncHelper.scheduleSyncForDataChange();
}
}
public void clearUsernameIfExists(@NonNull String username) {
@@ -1601,7 +1664,7 @@ public class RecipientDatabase extends Database {
private final Recipient.Capability uuidCapability;
private final Recipient.Capability groupsV2Capability;
private final InsightsBannerTier insightsBannerTier;
private final byte[] storageKey;
private final byte[] storageId;
private final byte[] identityKey;
private final IdentityDatabase.VerifiedStatus identityStatus;
@@ -1637,7 +1700,7 @@ public class RecipientDatabase extends Database {
Recipient.Capability uuidCapability,
Recipient.Capability groupsV2Capability,
@NonNull InsightsBannerTier insightsBannerTier,
@Nullable byte[] storageKey,
@Nullable byte[] storageId,
@Nullable byte[] identityKey,
@NonNull IdentityDatabase.VerifiedStatus identityStatus)
{
@@ -1673,7 +1736,7 @@ public class RecipientDatabase extends Database {
this.uuidCapability = uuidCapability;
this.groupsV2Capability = groupsV2Capability;
this.insightsBannerTier = insightsBannerTier;
this.storageKey = storageKey;
this.storageId = storageId;
this.identityKey = identityKey;
this.identityStatus = identityStatus;
}
@@ -1806,8 +1869,8 @@ public class RecipientDatabase extends Database {
return groupsV2Capability;
}
public @Nullable byte[] getStorageKey() {
return storageKey;
public @Nullable byte[] getStorageId() {
return storageId;
}
public @Nullable byte[] getIdentityKey() {

View File

@@ -45,6 +45,7 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.recipients.RecipientUtil;
import org.thoughtcrime.securesms.storage.StorageSyncHelper;
import org.thoughtcrime.securesms.util.JsonUtils;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.Util;
@@ -53,6 +54,7 @@ import org.whispersystems.libsignal.util.guava.Optional;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
@@ -422,10 +424,48 @@ public class ThreadDatabase extends Database {
return getConversationList("1");
}
public boolean isArchived(@NonNull RecipientId recipientId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String query = RECIPIENT_ID + " = ?";
String[] args = new String[]{ recipientId.serialize() };
try (Cursor cursor = db.query(TABLE_NAME, new String[] { ARCHIVED }, query, args, null, null, null)) {
if (cursor != null && cursor.moveToFirst()) {
return cursor.getInt(cursor.getColumnIndexOrThrow(ARCHIVED)) == 1;
}
}
return false;
}
public void setArchived(@NonNull RecipientId recipientId, boolean status) {
setArchived(Collections.singletonMap(recipientId, status));
}
public void setArchived(@NonNull Map<RecipientId, Boolean> status) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
db.beginTransaction();
try {
String query = RECIPIENT_ID + " = ?";
for (Map.Entry<RecipientId, Boolean> entry : status.entrySet()) {
ContentValues values = new ContentValues(1);
values.put(ARCHIVED, entry.getValue() ? "1" : "0");
db.update(TABLE_NAME, values, query, new String[] { entry.getKey().serialize() });
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
notifyConversationListListeners();
}
}
public @NonNull Set<RecipientId> getArchivedRecipients() {
Set<RecipientId> archived = new HashSet<>();
try (Cursor cursor = DatabaseFactory.getThreadDatabase(context).getArchivedConversationList()) {
try (Cursor cursor = getArchivedConversationList()) {
while (cursor != null && cursor.moveToNext()) {
archived.add(RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_ID))));
}
@@ -488,6 +528,12 @@ public class ThreadDatabase extends Database {
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
notifyConversationListListeners();
Recipient recipient = getRecipientForThreadId(threadId);
if (recipient != null) {
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(recipient.getId());
StorageSyncHelper.scheduleSyncForDataChange();
}
}
public void unarchiveConversation(long threadId) {
@@ -497,6 +543,12 @@ public class ThreadDatabase extends Database {
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""});
notifyConversationListListeners();
Recipient recipient = getRecipientForThreadId(threadId);
if (recipient != null) {
DatabaseFactory.getRecipientDatabase(context).markNeedsSync(recipient.getId());
StorageSyncHelper.scheduleSyncForDataChange();
}
}
public void setLastSeen(long threadId) {