From 172a43679d0026d4ef63171d14d7c9e3f6e0ad13 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Fri, 14 Feb 2020 12:00:32 -0400 Subject: [PATCH] Add GV2 recipient capability. --- .../securesms/database/RecipientDatabase.java | 26 +++++++++++--- .../database/helpers/SQLCipherOpenHelper.java | 7 +++- .../securesms/jobs/RefreshOwnProfileJob.java | 2 +- .../securesms/jobs/RetrieveProfileJob.java | 2 +- .../securesms/recipients/Recipient.java | 35 +++++++++++++++++++ .../recipients/RecipientDetails.java | 3 ++ .../api/profiles/SignalServiceProfile.java | 7 ++++ 7 files changed, 74 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index 692c8ee838..970506db9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -35,6 +35,7 @@ import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.IdentityKey; 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.SignalContactRecord; import org.whispersystems.signalservice.api.storage.SignalGroupV1Record; @@ -91,6 +92,7 @@ public class RecipientDatabase extends Database { private static final String UNIDENTIFIED_ACCESS_MODE = "unidentified_access_mode"; private static final String FORCE_SMS_SELECTION = "force_sms_selection"; private static final String UUID_SUPPORTED = "uuid_supported"; + private static final String GROUPS_V2_CAPABILITY = "gv2_capability"; private static final String STORAGE_SERVICE_KEY = "storage_service_key"; private static final String DIRTY = "dirty"; private static final String PROFILE_GIVEN_NAME = "signal_profile_name"; @@ -110,7 +112,9 @@ public class RecipientDatabase extends Database { SYSTEM_DISPLAY_NAME, SYSTEM_PHOTO_URI, SYSTEM_PHONE_LABEL, SYSTEM_PHONE_TYPE, SYSTEM_CONTACT_URI, PROFILE_GIVEN_NAME, PROFILE_FAMILY_NAME, SIGNAL_PROFILE_AVATAR, PROFILE_SHARING, NOTIFICATION_CHANNEL, UNIDENTIFIED_ACCESS_MODE, - FORCE_SMS_SELECTION, UUID_SUPPORTED, STORAGE_SERVICE_KEY, DIRTY + FORCE_SMS_SELECTION, + UUID_SUPPORTED, GROUPS_V2_CAPABILITY, + STORAGE_SERVICE_KEY, DIRTY }; private static final String[] RECIPIENT_FULL_PROJECTION = ArrayUtils.concat( @@ -278,6 +282,7 @@ public class RecipientDatabase extends Database { UNIDENTIFIED_ACCESS_MODE + " INTEGER DEFAULT 0, " + FORCE_SMS_SELECTION + " INTEGER DEFAULT 0, " + UUID_SUPPORTED + " INTEGER DEFAULT 0, " + + GROUPS_V2_CAPABILITY + " INTEGER DEFAULT " + Recipient.Capability.UNKNOWN.serialize() + ", " + STORAGE_SERVICE_KEY + " TEXT UNIQUE DEFAULT NULL, " + DIRTY + " INTEGER DEFAULT " + DirtyState.CLEAN.getId() + ");"; @@ -693,6 +698,7 @@ public class RecipientDatabase extends Database { int unidentifiedAccessMode = cursor.getInt(cursor.getColumnIndexOrThrow(UNIDENTIFIED_ACCESS_MODE)); boolean forceSmsSelection = cursor.getInt(cursor.getColumnIndexOrThrow(FORCE_SMS_SELECTION)) == 1; boolean uuidSupported = cursor.getInt(cursor.getColumnIndexOrThrow(UUID_SUPPORTED)) == 1; + int gv2SupportedValue = cursor.getInt(cursor.getColumnIndexOrThrow(GROUPS_V2_CAPABILITY)); String storageKeyRaw = cursor.getString(cursor.getColumnIndexOrThrow(STORAGE_SERVICE_KEY)); String identityKeyRaw = cursor.getString(cursor.getColumnIndexOrThrow(IDENTITY_KEY)); int identityStatusRaw = cursor.getInt(cursor.getColumnIndexOrThrow(IDENTITY_STATUS)); @@ -742,7 +748,9 @@ public class RecipientDatabase extends Database { systemPhoneLabel, systemContactUri, ProfileName.fromParts(profileGivenName, profileFamilyName), signalProfileAvatar, profileSharing, notificationChannel, UnidentifiedAccessMode.fromMode(unidentifiedAccessMode), - forceSmsSelection, uuidSupported, InsightsBannerTier.fromId(insightsBannerTier), + forceSmsSelection, + uuidSupported, Recipient.Capability.deserialize(gv2SupportedValue), + InsightsBannerTier.fromId(insightsBannerTier), storageKey, identityKey, identityStatus); } @@ -870,9 +878,10 @@ public class RecipientDatabase extends Database { Recipient.live(id).refresh(); } - public void setUuidSupported(@NonNull RecipientId id, boolean supported) { - ContentValues values = new ContentValues(1); - values.put(UUID_SUPPORTED, supported ? "1" : "0"); + public void setCapabilities(@NonNull RecipientId id, @NonNull SignalServiceProfile.Capabilities capabilities) { + ContentValues values = new ContentValues(2); + values.put(UUID_SUPPORTED, capabilities.isUuid() ? "1" : "0"); + values.put(GROUPS_V2_CAPABILITY, Recipient.Capability.fromBoolean(capabilities.isGv2()).serialize()); update(id, values); Recipient.live(id).refresh(); } @@ -1578,6 +1587,7 @@ public class RecipientDatabase extends Database { private final UnidentifiedAccessMode unidentifiedAccessMode; private final boolean forceSmsSelection; private final boolean uuidSupported; + private final Recipient.Capability groupsV2Capability; private final InsightsBannerTier insightsBannerTier; private final byte[] storageKey; private final byte[] identityKey; @@ -1613,6 +1623,7 @@ public class RecipientDatabase extends Database { @NonNull UnidentifiedAccessMode unidentifiedAccessMode, boolean forceSmsSelection, boolean uuidSupported, + Recipient.Capability groupsV2Capability, @NonNull InsightsBannerTier insightsBannerTier, @Nullable byte[] storageKey, @Nullable byte[] identityKey, @@ -1648,6 +1659,7 @@ public class RecipientDatabase extends Database { this.unidentifiedAccessMode = unidentifiedAccessMode; this.forceSmsSelection = forceSmsSelection; this.uuidSupported = uuidSupported; + this.groupsV2Capability = groupsV2Capability; this.insightsBannerTier = insightsBannerTier; this.storageKey = storageKey; this.identityKey = identityKey; @@ -1778,6 +1790,10 @@ public class RecipientDatabase extends Database { return uuidSupported; } + public Recipient.Capability getGroupsV2Capability() { + return groupsV2Capability; + } + public @Nullable byte[] getStorageKey() { return storageKey; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index b377ca08db..1ca0f5f5e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -113,8 +113,9 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { private static final int PROFILE_KEY_CREDENTIALS = 48; private static final int ATTACHMENT_FILE_INDEX = 49; private static final int STORAGE_SERVICE_ACTIVE = 50; + private static final int GROUPS_V2_RECIPIENT_CAPABILITY = 51; - private static final int DATABASE_VERSION = 50; + private static final int DATABASE_VERSION = 51; private static final String DATABASE_NAME = "signal.db"; private final Context context; @@ -761,6 +762,10 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { } } + if (oldVersion < GROUPS_V2_RECIPIENT_CAPABILITY) { + db.execSQL("ALTER TABLE recipient ADD COLUMN gv2_capability INTEGER DEFAULT 0"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 42e50b403e..a8dc10225d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -121,7 +121,7 @@ public class RefreshOwnProfileJob extends BaseJob { return; } - DatabaseFactory.getRecipientDatabase(context).setUuidSupported(Recipient.self().getId(), capabilities.isUuid()); + DatabaseFactory.getRecipientDatabase(context).setCapabilities(Recipient.self().getId(), capabilities); } public static final class Factory implements Job.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java index 6058e6092b..f6528f6b7b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RetrieveProfileJob.java @@ -240,7 +240,7 @@ public class RetrieveProfileJob extends BaseJob { return; } - DatabaseFactory.getRecipientDatabase(context).setUuidSupported(recipient.getId(), capabilities.isUuid()); + DatabaseFactory.getRecipientDatabase(context).setCapabilities(recipient.getId(), capabilities); } public static final class Factory implements Job.Factory { diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 9303fe23dc..83e5966f75 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -91,6 +91,7 @@ public class Recipient { private final UnidentifiedAccessMode unidentifiedAccessMode; private final boolean forceSmsSelection; private final boolean uuidSupported; + private final Capability groupsV2Capability; private final InsightsBannerTier insightsBannerTier; private final byte[] storageKey; private final byte[] identityKey; @@ -324,6 +325,7 @@ public class Recipient { this.unidentifiedAccessMode = UnidentifiedAccessMode.DISABLED; this.forceSmsSelection = false; this.uuidSupported = false; + this.groupsV2Capability = Capability.UNKNOWN; this.storageKey = null; this.identityKey = null; this.identityStatus = VerifiedStatus.DEFAULT; @@ -364,6 +366,7 @@ public class Recipient { this.unidentifiedAccessMode = details.unidentifiedAccessMode; this.forceSmsSelection = details.forceSmsSelection; this.uuidSupported = details.uuidSuported; + this.groupsV2Capability = details.groupsV2Capability; this.storageKey = details.storageKey; this.identityKey = details.identityKey; this.identityStatus = details.identityStatus; @@ -687,6 +690,10 @@ public class Recipient { } } + public Capability getGroupsV2Capability() { + return groupsV2Capability; + } + public @Nullable byte[] getProfileKey() { return profileKey; } @@ -762,6 +769,34 @@ public class Recipient { return id.equals(recipient.id); } + public enum Capability { + UNKNOWN(0), + SUPPORTED(1), + NOT_SUPPORTED(-1); + + private final int value; + + Capability(int value) { + this.value = value; + } + + public int serialize() { + return value; + } + + public static Capability deserialize(int value) { + switch (value) { + case 1 : return SUPPORTED; + case -1 : return NOT_SUPPORTED; + default : return UNKNOWN; + } + } + + public static Capability fromBoolean(boolean supported) { + return supported ? SUPPORTED : NOT_SUPPORTED; + } + } + @Override public int hashCode() { return Objects.hash(id); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java index 33abadf253..165e6c22f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/RecipientDetails.java @@ -56,6 +56,7 @@ public class RecipientDetails { final UnidentifiedAccessMode unidentifiedAccessMode; final boolean forceSmsSelection; final boolean uuidSuported; + final Recipient.Capability groupsV2Capability; final InsightsBannerTier insightsBannerTier; final byte[] storageKey; final byte[] identityKey; @@ -100,6 +101,7 @@ public class RecipientDetails { this.unidentifiedAccessMode = settings.getUnidentifiedAccessMode(); this.forceSmsSelection = settings.isForceSmsSelection(); this.uuidSuported = settings.isUuidSupported(); + this.groupsV2Capability = settings.getGroupsV2Capability(); this.insightsBannerTier = settings.getInsightsBannerTier(); this.storageKey = settings.getStorageKey(); this.identityKey = settings.getIdentityKey(); @@ -146,6 +148,7 @@ public class RecipientDetails { this.forceSmsSelection = false; this.name = null; this.uuidSuported = false; + this.groupsV2Capability = Recipient.Capability.UNKNOWN; this.storageKey = null; this.identityKey = null; this.identityStatus = VerifiedStatus.DEFAULT; diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java index 4acfaee3bd..03a5586634 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/profiles/SignalServiceProfile.java @@ -101,11 +101,18 @@ public class SignalServiceProfile { @JsonProperty private boolean uuid; + @JsonProperty + private boolean gv2; + public Capabilities() {} public boolean isUuid() { return uuid; } + + public boolean isGv2() { + return gv2; + } } public ProfileKeyCredentialResponse getProfileKeyCredentialResponse() {