]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/Manager.java
Rename group v2 helper
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / Manager.java
index 3c2d130d3d378e57ad0a1642cd7c86cd5e488b8d..cd0ce561a4b9ce08edc0fb91fa4281ca35f87fc5 100644 (file)
@@ -16,6 +16,7 @@
  */
 package org.asamk.signal.manager;
 
+import org.asamk.signal.manager.api.Device;
 import org.asamk.signal.manager.config.ServiceConfig;
 import org.asamk.signal.manager.config.ServiceEnvironment;
 import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
@@ -25,7 +26,7 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
 import org.asamk.signal.manager.groups.GroupNotFoundException;
 import org.asamk.signal.manager.groups.GroupUtils;
 import org.asamk.signal.manager.groups.NotAGroupMemberException;
-import org.asamk.signal.manager.helper.GroupHelper;
+import org.asamk.signal.manager.helper.GroupV2Helper;
 import org.asamk.signal.manager.helper.PinHelper;
 import org.asamk.signal.manager.helper.ProfileHelper;
 import org.asamk.signal.manager.helper.UnidentifiedAccessHelper;
@@ -82,6 +83,7 @@ import org.whispersystems.signalservice.api.SignalServiceAccountManager;
 import org.whispersystems.signalservice.api.SignalServiceMessagePipe;
 import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
 import org.whispersystems.signalservice.api.SignalServiceMessageSender;
+import org.whispersystems.signalservice.api.SignalSessionLock;
 import org.whispersystems.signalservice.api.crypto.SignalServiceCipher;
 import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
 import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
@@ -108,7 +110,6 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsO
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroup;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsInputStream;
 import org.whispersystems.signalservice.api.messages.multidevice.DeviceGroupsOutputStream;
-import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo;
 import org.whispersystems.signalservice.api.messages.multidevice.RequestMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SentTranscriptMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
@@ -118,6 +119,8 @@ import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
 import org.whispersystems.signalservice.api.profiles.SignalServiceProfile;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.api.push.exceptions.MissingConfigurationException;
+import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
+import org.whispersystems.signalservice.api.util.DeviceNameUtil;
 import org.whispersystems.signalservice.api.util.InvalidNumberException;
 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
 import org.whispersystems.signalservice.api.util.SleepTimer;
@@ -159,6 +162,7 @@ import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
@@ -187,10 +191,19 @@ public class Manager implements Closeable {
 
     private final UnidentifiedAccessHelper unidentifiedAccessHelper;
     private final ProfileHelper profileHelper;
-    private final GroupHelper groupHelper;
+    private final GroupV2Helper groupV2Helper;
     private final PinHelper pinHelper;
     private final AvatarStore avatarStore;
     private final AttachmentStore attachmentStore;
+    private final SignalSessionLock sessionLock = new SignalSessionLock() {
+        private final ReentrantLock LEGACY_LOCK = new ReentrantLock();
+
+        @Override
+        public Lock acquire() {
+            LEGACY_LOCK.lock();
+            return LEGACY_LOCK::unlock;
+        }
+    };
 
     Manager(
             SignalAccount account,
@@ -246,7 +259,7 @@ public class Manager implements Closeable {
                 unidentified -> unidentified ? getOrCreateUnidentifiedMessagePipe() : getOrCreateMessagePipe(),
                 () -> messageReceiver,
                 this::resolveSignalServiceAddress);
-        this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
+        this.groupV2Helper = new GroupV2Helper(this::getRecipientProfileKeyCredential,
                 this::getRecipientProfile,
                 account::getSelfRecipientId,
                 groupsV2Operations,
@@ -286,7 +299,7 @@ public class Manager implements Closeable {
             throw new NotRegisteredException();
         }
 
-        var account = SignalAccount.load(pathConfig.getDataPath(), username);
+        var account = SignalAccount.load(pathConfig.getDataPath(), username, true);
 
         if (!account.isRegistered()) {
             throw new NotRegisteredException();
@@ -316,11 +329,9 @@ public class Manager implements Closeable {
     public void checkAccountState() throws IOException {
         if (accountManager.getPreKeysCount() < ServiceConfig.PREKEY_MINIMUM_COUNT) {
             refreshPreKeys();
-            account.save();
         }
         if (account.getUuid() == null) {
             account.setUuid(accountManager.getOwnUuid());
-            account.save();
         }
         updateAccountAttributes();
     }
@@ -342,7 +353,8 @@ public class Manager implements Closeable {
     }
 
     public void updateAccountAttributes() throws IOException {
-        accountManager.setAccountAttributes(null,
+        accountManager.setAccountAttributes(account.getEncryptedDeviceName(),
+                null,
                 account.getLocalRegistrationId(),
                 true,
                 // set legacy pin only if no KBS master key is set
@@ -355,18 +367,22 @@ public class Manager implements Closeable {
     }
 
     /**
-     * @param name       if null, the previous name will be kept
+     * @param givenName  if null, the previous givenName will be kept
+     * @param familyName if null, the previous familyName will be kept
      * @param about      if null, the previous about text will be kept
      * @param aboutEmoji if null, the previous about emoji will be kept
      * @param avatar     if avatar is null the image from the local avatar store is used (if present),
-     *                   if it's Optional.absent(), the avatar will be removed
      */
-    public void setProfile(String name, String about, String aboutEmoji, Optional<File> avatar) throws IOException {
+    public void setProfile(
+            String givenName, final String familyName, String about, String aboutEmoji, Optional<File> avatar
+    ) throws IOException {
         var profile = getRecipientProfile(account.getSelfRecipientId());
         var builder = profile == null ? Profile.newBuilder() : Profile.newBuilder(profile);
-        if (name != null) {
-            builder.withGivenName(name);
-            builder.withFamilyName(null);
+        if (givenName != null) {
+            builder.withGivenName(givenName);
+        }
+        if (familyName != null) {
+            builder.withFamilyName(familyName);
         }
         if (about != null) {
             builder.withAbout(about);
@@ -382,8 +398,9 @@ public class Manager implements Closeable {
             accountManager.setVersionedProfile(account.getUuid(),
                     account.getProfileKey(),
                     newProfile.getInternalServiceName(),
-                    newProfile.getAbout(),
-                    newProfile.getAboutEmoji(),
+                    newProfile.getAbout() == null ? "" : newProfile.getAbout(),
+                    newProfile.getAboutEmoji() == null ? "" : newProfile.getAboutEmoji(),
+                    Optional.absent(),
                     streamDetails);
         }
 
@@ -408,24 +425,37 @@ public class Manager implements Closeable {
         // If this is the master device, other users can't send messages to this number anymore.
         // If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
         accountManager.setGcmId(Optional.absent());
+
+        account.setRegistered(false);
+    }
+
+    public void deleteAccount() throws IOException {
         accountManager.deleteAccount();
 
         account.setRegistered(false);
-        account.save();
     }
 
-    public List<DeviceInfo> getLinkedDevices() throws IOException {
+    public List<Device> getLinkedDevices() throws IOException {
         var devices = accountManager.getDevices();
         account.setMultiDevice(devices.size() > 1);
-        account.save();
-        return devices;
+        var identityKey = account.getIdentityKeyPair().getPrivateKey();
+        return devices.stream().map(d -> {
+            String deviceName = d.getName();
+            if (deviceName != null) {
+                try {
+                    deviceName = DeviceNameUtil.decryptDeviceName(deviceName, identityKey);
+                } catch (IOException e) {
+                    logger.debug("Failed to decrypt device name, maybe plain text?", e);
+                }
+            }
+            return new Device(d.getId(), deviceName, d.getCreated(), d.getLastSeen());
+        }).collect(Collectors.toList());
     }
 
     public void removeLinkedDevices(int deviceId) throws IOException {
         accountManager.removeDevice(deviceId);
         var devices = accountManager.getDevices();
         account.setMultiDevice(devices.size() > 1);
-        account.save();
     }
 
     public void addDeviceLink(URI linkUri) throws IOException, InvalidKeyException {
@@ -444,7 +474,6 @@ public class Manager implements Closeable {
                 Optional.of(account.getProfileKey().serialize()),
                 verificationCode);
         account.setMultiDevice(true);
-        account.save();
     }
 
     public void setRegistrationLockPin(Optional<String> pin) throws IOException, UnauthenticatedResponseException {
@@ -458,8 +487,7 @@ public class Manager implements Closeable {
 
             pinHelper.setRegistrationLockPin(pin.get(), masterKey);
 
-            account.setRegistrationLockPin(pin.get());
-            account.setPinMasterKey(masterKey);
+            account.setRegistrationLockPin(pin.get(), masterKey);
         } else {
             // Remove legacy registration lock
             accountManager.removeRegistrationLockV1();
@@ -467,10 +495,8 @@ public class Manager implements Closeable {
             // Remove KBS Pin
             pinHelper.removeRegistrationLockPin();
 
-            account.setRegistrationLockPin(null);
-            account.setPinMasterKey(null);
+            account.setRegistrationLockPin(null, null);
         }
-        account.save();
     }
 
     void refreshPreKeys() throws IOException {
@@ -520,6 +546,7 @@ public class Manager implements Closeable {
                 account.getPassword(),
                 account.getDeviceId(),
                 account.getSignalProtocolStore(),
+                sessionLock,
                 userAgent,
                 account.isMultiDevice(),
                 Optional.fromNullable(messagePipe),
@@ -531,12 +558,6 @@ public class Manager implements Closeable {
                 ServiceConfig.AUTOMATIC_NETWORK_RETRY);
     }
 
-    public Profile getRecipientProfile(
-            SignalServiceAddress address
-    ) {
-        return getRecipientProfile(resolveRecipient(address), false);
-    }
-
     public Profile getRecipientProfile(
             RecipientId recipientId
     ) {
@@ -548,14 +569,6 @@ public class Manager implements Closeable {
     Profile getRecipientProfile(
             RecipientId recipientId, boolean force
     ) {
-        var profileKey = account.getProfileStore().getProfileKey(recipientId);
-        if (profileKey == null) {
-            if (force) {
-                // retrieve profile to get identity key
-                retrieveEncryptedProfile(recipientId);
-            }
-            return null;
-        }
         var profile = account.getProfileStore().getProfile(recipientId);
 
         var now = new Date().getTime();
@@ -582,7 +595,18 @@ public class Manager implements Closeable {
             return null;
         }
 
-        profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile);
+        var profileKey = account.getProfileStore().getProfileKey(recipientId);
+        if (profileKey == null) {
+            profile = new Profile(new Date().getTime(),
+                    null,
+                    null,
+                    null,
+                    null,
+                    ProfileUtils.getUnidentifiedAccessMode(encryptedProfile, null),
+                    ProfileUtils.getCapabilities(encryptedProfile));
+        } else {
+            profile = decryptProfileAndDownloadAvatar(recipientId, profileKey, encryptedProfile);
+        }
         account.getProfileStore().storeProfile(recipientId, profile);
 
         return profile;
@@ -604,10 +628,14 @@ public class Manager implements Closeable {
         final var profile = profileAndCredential.getProfile();
 
         try {
-            account.getIdentityKeyStore()
+            var newIdentity = account.getIdentityKeyStore()
                     .saveIdentity(recipientId,
                             new IdentityKey(Base64.getDecoder().decode(profile.getIdentityKey())),
                             new Date());
+
+            if (newIdentity) {
+                account.getSessionStore().archiveSessions(recipientId);
+            }
         } catch (InvalidKeyException ignored) {
             logger.warn("Got invalid identity key in profile for {}",
                     resolveSignalServiceAddress(recipientId).getLegacyIdentifier());
@@ -712,9 +740,10 @@ public class Manager implements Closeable {
     public Pair<Long, List<SendMessageResult>> sendGroupMessageReaction(
             String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, GroupId groupId
     ) throws IOException, InvalidNumberException, NotAGroupMemberException, GroupNotFoundException {
+        var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor);
         var reaction = new SignalServiceDataMessage.Reaction(emoji,
                 remove,
-                canonicalizeAndResolveSignalServiceAddress(targetAuthor),
+                resolveSignalServiceAddress(targetAuthorRecipientId),
                 targetSentTimestamp);
         final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
 
@@ -744,7 +773,7 @@ public class Manager implements Closeable {
             account.getGroupStore().updateGroup(groupInfoV1);
         } else {
             final var groupInfoV2 = (GroupInfoV2) g;
-            final var groupGroupChangePair = groupHelper.leaveGroup(groupInfoV2);
+            final var groupGroupChangePair = groupV2Helper.leaveGroup(groupInfoV2);
             groupInfoV2.setGroup(groupGroupChangePair.first(), this::resolveRecipient);
             messageBuilder = getGroupUpdateMessageBuilder(groupInfoV2, groupGroupChangePair.second().toByteArray());
             account.getGroupStore().updateGroup(groupInfoV2);
@@ -753,94 +782,94 @@ public class Manager implements Closeable {
         return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfRecipientId()));
     }
 
-    public Pair<GroupId, List<SendMessageResult>> updateGroup(
-            GroupId groupId, String name, List<String> members, File avatarFile
+    public Pair<GroupId, List<SendMessageResult>> createGroup(
+            String name, List<String> members, File avatarFile
+    ) throws IOException, AttachmentInvalidException, InvalidNumberException {
+        return createGroup(name, members == null ? null : getSignalServiceAddresses(members), avatarFile);
+    }
+
+    private Pair<GroupId, List<SendMessageResult>> createGroup(
+            String name, Set<RecipientId> members, File avatarFile
+    ) throws IOException, AttachmentInvalidException {
+        final var selfRecipientId = account.getSelfRecipientId();
+        if (members != null && members.contains(selfRecipientId)) {
+            members = new HashSet<>(members);
+            members.remove(selfRecipientId);
+        }
+
+        var gv2Pair = groupV2Helper.createGroup(name == null ? "" : name,
+                members == null ? Set.of() : members,
+                avatarFile);
+
+        SignalServiceDataMessage.Builder messageBuilder;
+        if (gv2Pair == null) {
+            // Failed to create v2 group, creating v1 group instead
+            var gv1 = new GroupInfoV1(GroupIdV1.createRandom());
+            gv1.addMembers(List.of(selfRecipientId));
+            final var result = updateGroupV1(gv1, name, members, avatarFile);
+            return new Pair<>(gv1.getGroupId(), result.second());
+        }
+
+        final var gv2 = gv2Pair.first();
+        final var decryptedGroup = gv2Pair.second();
+
+        gv2.setGroup(decryptedGroup, this::resolveRecipient);
+        if (avatarFile != null) {
+            avatarStore.storeGroupAvatar(gv2.getGroupId(),
+                    outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream));
+        }
+        messageBuilder = getGroupUpdateMessageBuilder(gv2, null);
+        account.getGroupStore().updateGroup(gv2);
+
+        final var result = sendMessage(messageBuilder, gv2.getMembersIncludingPendingWithout(selfRecipientId));
+        return new Pair<>(gv2.getGroupId(), result.second());
+    }
+
+    public Pair<Long, List<SendMessageResult>> updateGroup(
+            GroupId groupId,
+            String name,
+            String description,
+            List<String> members,
+            List<String> removeMembers,
+            File avatarFile
     ) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
-        return sendUpdateGroupMessage(groupId,
+        return updateGroup(groupId,
                 name,
+                description,
                 members == null ? null : getSignalServiceAddresses(members),
+                removeMembers == null ? null : getSignalServiceAddresses(removeMembers),
                 avatarFile);
     }
 
-    private Pair<GroupId, List<SendMessageResult>> sendUpdateGroupMessage(
-            GroupId groupId, String name, Set<RecipientId> members, File avatarFile
+    private Pair<Long, List<SendMessageResult>> updateGroup(
+            GroupId groupId,
+            String name,
+            String description,
+            Set<RecipientId> members,
+            final Set<RecipientId> removeMembers,
+            File avatarFile
     ) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
-        GroupInfo g;
-        SignalServiceDataMessage.Builder messageBuilder;
-        if (groupId == null) {
-            // Create new group
-            var gv2Pair = groupHelper.createGroupV2(name == null ? "" : name,
-                    members == null ? Set.of() : members,
-                    avatarFile);
-            if (gv2Pair == null) {
-                var gv1 = new GroupInfoV1(GroupIdV1.createRandom());
-                gv1.addMembers(List.of(account.getSelfRecipientId()));
-                updateGroupV1(gv1, name, members, avatarFile);
-                messageBuilder = getGroupUpdateMessageBuilder(gv1);
-                g = gv1;
-            } else {
-                final var gv2 = gv2Pair.first();
-                final var decryptedGroup = gv2Pair.second();
+        var group = getGroupForUpdating(groupId);
 
-                gv2.setGroup(decryptedGroup, this::resolveRecipient);
-                if (avatarFile != null) {
-                    avatarStore.storeGroupAvatar(gv2.getGroupId(),
-                            outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream));
-                }
-                messageBuilder = getGroupUpdateMessageBuilder(gv2, null);
-                g = gv2;
-            }
-        } else {
-            var group = getGroupForUpdating(groupId);
-            if (group instanceof GroupInfoV2) {
-                final var groupInfoV2 = (GroupInfoV2) group;
-
-                Pair<Long, List<SendMessageResult>> result = null;
-                if (groupInfoV2.isPendingMember(account.getSelfRecipientId())) {
-                    var groupGroupChangePair = groupHelper.acceptInvite(groupInfoV2);
-                    result = sendUpdateGroupMessage(groupInfoV2,
-                            groupGroupChangePair.first(),
-                            groupGroupChangePair.second());
-                }
+        if (group instanceof GroupInfoV2) {
+            return updateGroupV2((GroupInfoV2) group, name, description, members, removeMembers, avatarFile);
+        }
 
-                if (members != null) {
-                    final var newMembers = new HashSet<>(members);
-                    newMembers.removeAll(group.getMembers());
-                    if (newMembers.size() > 0) {
-                        var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, newMembers);
-                        result = sendUpdateGroupMessage(groupInfoV2,
-                                groupGroupChangePair.first(),
-                                groupGroupChangePair.second());
-                    }
-                }
-                if (result == null || name != null || avatarFile != null) {
-                    var groupGroupChangePair = groupHelper.updateGroupV2(groupInfoV2, name, avatarFile);
-                    if (avatarFile != null) {
-                        avatarStore.storeGroupAvatar(groupInfoV2.getGroupId(),
-                                outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream));
-                    }
-                    result = sendUpdateGroupMessage(groupInfoV2,
-                            groupGroupChangePair.first(),
-                            groupGroupChangePair.second());
-                }
+        return updateGroupV1((GroupInfoV1) group, name, members, avatarFile);
+    }
 
-                return new Pair<>(group.getGroupId(), result.second());
-            } else {
-                var gv1 = (GroupInfoV1) group;
-                updateGroupV1(gv1, name, members, avatarFile);
-                messageBuilder = getGroupUpdateMessageBuilder(gv1);
-                g = gv1;
-            }
-        }
+    private Pair<Long, List<SendMessageResult>> updateGroupV1(
+            final GroupInfoV1 gv1, final String name, final Set<RecipientId> members, final File avatarFile
+    ) throws IOException, AttachmentInvalidException {
+        updateGroupV1Details(gv1, name, members, avatarFile);
+        var messageBuilder = getGroupUpdateMessageBuilder(gv1);
 
-        account.getGroupStore().updateGroup(g);
+        account.getGroupStore().updateGroup(gv1);
 
-        final var result = sendMessage(messageBuilder,
-                g.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
-        return new Pair<>(g.getGroupId(), result.second());
+        return sendMessage(messageBuilder, gv1.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
     }
 
-    private void updateGroupV1(
+    private void updateGroupV1Details(
             final GroupInfoV1 g, final String name, final Collection<RecipientId> members, final File avatarFile
     ) throws IOException {
         if (name != null) {
@@ -878,18 +907,64 @@ public class Manager implements Closeable {
         }
     }
 
-    public Pair<GroupId, List<SendMessageResult>> joinGroup(
-            GroupInviteLinkUrl inviteLinkUrl
-    ) throws IOException, GroupLinkNotActiveException {
-        return sendJoinGroupMessage(inviteLinkUrl);
+    private Pair<Long, List<SendMessageResult>> updateGroupV2(
+            final GroupInfoV2 group,
+            final String name,
+            final String description,
+            final Set<RecipientId> members,
+            final Set<RecipientId> removeMembers,
+            final File avatarFile
+    ) throws IOException {
+        Pair<Long, List<SendMessageResult>> result = null;
+        if (group.isPendingMember(account.getSelfRecipientId())) {
+            var groupGroupChangePair = groupV2Helper.acceptInvite(group);
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        if (members != null) {
+            final var newMembers = new HashSet<>(members);
+            newMembers.removeAll(group.getMembers());
+            if (newMembers.size() > 0) {
+                var groupGroupChangePair = groupV2Helper.addMembers(group, newMembers);
+                result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+            }
+        }
+
+        if (removeMembers != null) {
+            var existingRemoveMembers = new HashSet<>(removeMembers);
+            existingRemoveMembers.retainAll(group.getMembers());
+            existingRemoveMembers.remove(getSelfRecipientId());// self can be removed with sendQuitGroupMessage
+            if (existingRemoveMembers.size() > 0) {
+                var groupGroupChangePair = groupV2Helper.removeMembers(group, existingRemoveMembers);
+                result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+            }
+
+            var pendingRemoveMembers = new HashSet<>(removeMembers);
+            pendingRemoveMembers.retainAll(group.getPendingMembers());
+            if (pendingRemoveMembers.size() > 0) {
+                var groupGroupChangePair = groupV2Helper.revokeInvitedMembers(group, pendingRemoveMembers);
+                result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+            }
+        }
+
+        if (result == null || name != null || description != null || avatarFile != null) {
+            var groupGroupChangePair = groupV2Helper.updateGroup(group, name, description, avatarFile);
+            if (avatarFile != null) {
+                avatarStore.storeGroupAvatar(group.getGroupId(),
+                        outputStream -> IOUtils.copyFileToStream(avatarFile, outputStream));
+            }
+            result = sendUpdateGroupV2Message(group, groupGroupChangePair.first(), groupGroupChangePair.second());
+        }
+
+        return result;
     }
 
-    private Pair<GroupId, List<SendMessageResult>> sendJoinGroupMessage(
+    public Pair<GroupId, List<SendMessageResult>> joinGroup(
             GroupInviteLinkUrl inviteLinkUrl
     ) throws IOException, GroupLinkNotActiveException {
-        final var groupJoinInfo = groupHelper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
+        final var groupJoinInfo = groupV2Helper.getDecryptedGroupJoinInfo(inviteLinkUrl.getGroupMasterKey(),
                 inviteLinkUrl.getPassword());
-        final var groupChange = groupHelper.joinGroup(inviteLinkUrl.getGroupMasterKey(),
+        final var groupChange = groupV2Helper.joinGroup(inviteLinkUrl.getGroupMasterKey(),
                 inviteLinkUrl.getPassword(),
                 groupJoinInfo);
         final var group = getOrMigrateGroup(inviteLinkUrl.getGroupMasterKey(),
@@ -901,11 +976,24 @@ public class Manager implements Closeable {
             return new Pair<>(group.getGroupId(), List.of());
         }
 
-        final var result = sendUpdateGroupMessage(group, group.getGroup(), groupChange);
+        final var result = sendUpdateGroupV2Message(group, group.getGroup(), groupChange);
 
         return new Pair<>(group.getGroupId(), result.second());
     }
 
+    private Pair<Long, List<SendMessageResult>> sendUpdateGroupV2Message(
+            GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange
+    ) throws IOException {
+        final var selfRecipientId = account.getSelfRecipientId();
+        final var members = group.getMembersIncludingPendingWithout(selfRecipientId);
+        group.setGroup(newDecryptedGroup, this::resolveRecipient);
+        members.addAll(group.getMembersIncludingPendingWithout(selfRecipientId));
+
+        final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray());
+        account.getGroupStore().updateGroup(group);
+        return sendMessage(messageBuilder, members);
+    }
+
     private static int currentTimeDays() {
         return (int) TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis());
     }
@@ -928,15 +1016,6 @@ public class Manager implements Closeable {
         }
     }
 
-    private Pair<Long, List<SendMessageResult>> sendUpdateGroupMessage(
-            GroupInfoV2 group, DecryptedGroup newDecryptedGroup, GroupChange groupChange
-    ) throws IOException {
-        group.setGroup(newDecryptedGroup, this::resolveRecipient);
-        final var messageBuilder = getGroupUpdateMessageBuilder(group, groupChange.toByteArray());
-        account.getGroupStore().updateGroup(group);
-        return sendMessage(messageBuilder, group.getMembersIncludingPendingWithout(account.getSelfRecipientId()));
-    }
-
     Pair<Long, List<SendMessageResult>> sendGroupInfoMessage(
             GroupIdV1 groupId, SignalServiceAddress recipient
     ) throws IOException, NotAGroupMemberException, GroupNotFoundException, AttachmentInvalidException {
@@ -947,14 +1026,15 @@ public class Manager implements Closeable {
         }
         g = (GroupInfoV1) group;
 
-        if (!g.isMember(resolveRecipient(recipient))) {
+        final var recipientId = resolveRecipient(recipient);
+        if (!g.isMember(recipientId)) {
             throw new NotAGroupMemberException(groupId, g.name);
         }
 
         var messageBuilder = getGroupUpdateMessageBuilder(g);
 
         // Send group message only to the recipient who requested it
-        return sendMessage(messageBuilder, Set.of(resolveRecipient(recipient)));
+        return sendMessage(messageBuilder, Set.of(recipientId));
     }
 
     private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV1 g) throws AttachmentInvalidException {
@@ -1064,9 +1144,10 @@ public class Manager implements Closeable {
     public Pair<Long, List<SendMessageResult>> sendMessageReaction(
             String emoji, boolean remove, String targetAuthor, long targetSentTimestamp, List<String> recipients
     ) throws IOException, InvalidNumberException {
+        var targetAuthorRecipientId = canonicalizeAndResolveRecipient(targetAuthor);
         var reaction = new SignalServiceDataMessage.Reaction(emoji,
                 remove,
-                canonicalizeAndResolveSignalServiceAddress(targetAuthor),
+                resolveSignalServiceAddress(targetAuthorRecipientId),
                 targetSentTimestamp);
         final var messageBuilder = SignalServiceDataMessage.newBuilder().withReaction(reaction);
         return sendMessage(messageBuilder, getSignalServiceAddresses(recipients));
@@ -1082,25 +1163,38 @@ public class Manager implements Closeable {
             for (var address : signalServiceAddresses) {
                 handleEndSession(address);
             }
-            account.save();
             throw e;
         }
     }
 
+    void renewSession(RecipientId recipientId) throws IOException {
+        account.getSessionStore().archiveSessions(recipientId);
+        if (!recipientId.equals(getSelfRecipientId())) {
+            sendNullMessage(recipientId);
+        }
+    }
+
     public String getContactName(String number) throws InvalidNumberException {
         var contact = account.getContactStore().getContact(canonicalizeAndResolveRecipient(number));
         return contact == null || contact.getName() == null ? "" : contact.getName();
     }
 
-    public void setContactName(String number, String name) throws InvalidNumberException {
+    public void setContactName(String number, String name) throws InvalidNumberException, NotMasterDeviceException {
+        if (!account.isMasterDevice()) {
+            throw new NotMasterDeviceException();
+        }
         final var recipientId = canonicalizeAndResolveRecipient(number);
         var contact = account.getContactStore().getContact(recipientId);
         final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
         account.getContactStore().storeContact(recipientId, builder.withName(name).build());
-        account.save();
     }
 
-    public void setContactBlocked(String number, boolean blocked) throws InvalidNumberException {
+    public void setContactBlocked(
+            String number, boolean blocked
+    ) throws InvalidNumberException, NotMasterDeviceException {
+        if (!account.isMasterDevice()) {
+            throw new NotMasterDeviceException();
+        }
         setContactBlocked(canonicalizeAndResolveRecipient(number), blocked);
     }
 
@@ -1108,7 +1202,6 @@ public class Manager implements Closeable {
         var contact = account.getContactStore().getContact(recipientId);
         final var builder = contact == null ? Contact.newBuilder() : Contact.newBuilder(contact);
         account.getContactStore().storeContact(recipientId, builder.withBlocked(blocked).build());
-        account.save();
     }
 
     public void setGroupBlocked(final GroupId groupId, final boolean blocked) throws GroupNotFoundException {
@@ -1119,7 +1212,6 @@ public class Manager implements Closeable {
 
         group.setBlocked(blocked);
         account.getGroupStore().updateGroup(group);
-        account.save();
     }
 
     private void setExpirationTimer(RecipientId recipientId, int messageExpirationTimer) {
@@ -1146,7 +1238,6 @@ public class Manager implements Closeable {
         var recipientId = canonicalizeAndResolveRecipient(number);
         setExpirationTimer(recipientId, messageExpirationTimer);
         sendExpirationTimerUpdate(recipientId);
-        account.save();
     }
 
     /**
@@ -1179,7 +1270,6 @@ public class Manager implements Closeable {
 
         var sticker = new Sticker(StickerPackId.deserialize(Hex.fromStringCondensed(packId)), packKey);
         account.getStickerStore().updateSticker(sticker);
-        account.save();
 
         try {
             return new URI("https",
@@ -1193,7 +1283,15 @@ public class Manager implements Closeable {
         }
     }
 
-    void requestSyncGroups() throws IOException {
+    public void requestAllSyncData() throws IOException {
+        requestSyncGroups();
+        requestSyncContacts();
+        requestSyncBlocked();
+        requestSyncConfiguration();
+        requestSyncKeys();
+    }
+
+    private void requestSyncGroups() throws IOException {
         var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
                 .setType(SignalServiceProtos.SyncMessage.Request.Type.GROUPS)
                 .build();
@@ -1205,7 +1303,7 @@ public class Manager implements Closeable {
         }
     }
 
-    void requestSyncContacts() throws IOException {
+    private void requestSyncContacts() throws IOException {
         var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
                 .setType(SignalServiceProtos.SyncMessage.Request.Type.CONTACTS)
                 .build();
@@ -1217,7 +1315,7 @@ public class Manager implements Closeable {
         }
     }
 
-    void requestSyncBlocked() throws IOException {
+    private void requestSyncBlocked() throws IOException {
         var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
                 .setType(SignalServiceProtos.SyncMessage.Request.Type.BLOCKED)
                 .build();
@@ -1229,7 +1327,7 @@ public class Manager implements Closeable {
         }
     }
 
-    void requestSyncConfiguration() throws IOException {
+    private void requestSyncConfiguration() throws IOException {
         var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
                 .setType(SignalServiceProtos.SyncMessage.Request.Type.CONFIGURATION)
                 .build();
@@ -1241,7 +1339,7 @@ public class Manager implements Closeable {
         }
     }
 
-    void requestSyncKeys() throws IOException {
+    private void requestSyncKeys() throws IOException {
         var r = SignalServiceProtos.SyncMessage.Request.newBuilder()
                 .setType(SignalServiceProtos.SyncMessage.Request.Type.KEYS)
                 .build();
@@ -1254,11 +1352,13 @@ public class Manager implements Closeable {
     }
 
     private byte[] getSenderCertificate() {
-        // TODO support UUID capable sender certificates
-        // byte[] certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy();
         byte[] certificate;
         try {
-            certificate = accountManager.getSenderCertificate();
+            if (account.isPhoneNumberShared()) {
+                certificate = accountManager.getSenderCertificate();
+            } else {
+                certificate = accountManager.getSenderCertificateForPhoneNumberPrivacy();
+            }
         } catch (IOException e) {
             logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
             return null;
@@ -1277,7 +1377,7 @@ public class Manager implements Closeable {
         final var addressesMissingUuid = new HashSet<SignalServiceAddress>();
 
         for (var number : numbers) {
-            final var resolvedAddress = canonicalizeAndResolveSignalServiceAddress(number);
+            final var resolvedAddress = resolveSignalServiceAddress(canonicalizeAndResolveRecipient(number));
             if (resolvedAddress.getUuid().isPresent()) {
                 signalServiceAddresses.add(resolvedAddress);
             } else {
@@ -1311,10 +1411,20 @@ public class Manager implements Closeable {
         return signalServiceAddresses.stream().map(this::resolveRecipient).collect(Collectors.toSet());
     }
 
-    private Map<String, UUID> getRegisteredUsers(final Set<String> numbersMissingUuid) throws IOException {
+    private RecipientId refreshRegisteredUser(RecipientId recipientId) throws IOException {
+        final var address = resolveSignalServiceAddress(recipientId);
+        if (!address.getNumber().isPresent()) {
+            return recipientId;
+        }
+        final var number = address.getNumber().get();
+        final var uuidMap = getRegisteredUsers(Set.of(number));
+        return resolveRecipientTrusted(new SignalServiceAddress(uuidMap.getOrDefault(number, null), number));
+    }
+
+    private Map<String, UUID> getRegisteredUsers(final Set<String> numbers) throws IOException {
         try {
             return accountManager.getRegisteredUsers(ServiceConfig.getIasKeyStore(),
-                    numbersMissingUuid,
+                    numbers,
                     serviceEnvironmentConfig.getCdsMrenclave());
         } catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | SignatureException | UnauthenticatedResponseException | InvalidKeyException e) {
             throw new IOException(e);
@@ -1346,10 +1456,12 @@ public class Manager implements Closeable {
 
                     for (var r : result) {
                         if (r.getIdentityFailure() != null) {
-                            account.getIdentityKeyStore().
-                                    saveIdentity(resolveRecipient(r.getAddress()),
-                                            r.getIdentityFailure().getIdentityKey(),
-                                            new Date());
+                            final var recipientId = resolveRecipient(r.getAddress());
+                            final var newIdentity = account.getIdentityKeyStore()
+                                    .saveIdentity(recipientId, r.getIdentityFailure().getIdentityKey(), new Date());
+                            if (newIdentity) {
+                                account.getSessionStore().archiveSessions(recipientId);
+                            }
                         }
                     }
 
@@ -1366,7 +1478,7 @@ public class Manager implements Closeable {
                     final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
                     messageBuilder.withExpiration(expirationTime);
                     message = messageBuilder.build();
-                    results.add(sendMessage(resolveSignalServiceAddress(recipientId), message));
+                    results.add(sendMessage(recipientId, message));
                 }
                 return new Pair<>(timestamp, results);
             }
@@ -1376,7 +1488,6 @@ public class Manager implements Closeable {
                     handleEndSession(recipient);
                 }
             }
-            account.save();
         }
     }
 
@@ -1387,27 +1498,24 @@ public class Manager implements Closeable {
         messageBuilder.withTimestamp(timestamp);
         getOrCreateMessagePipe();
         getOrCreateUnidentifiedMessagePipe();
-        try {
-            final var recipientId = account.getSelfRecipientId();
+        final var recipientId = account.getSelfRecipientId();
 
-            final var contact = account.getContactStore().getContact(recipientId);
-            final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
-            messageBuilder.withExpiration(expirationTime);
+        final var contact = account.getContactStore().getContact(recipientId);
+        final var expirationTime = contact != null ? contact.getMessageExpirationTime() : 0;
+        messageBuilder.withExpiration(expirationTime);
 
-            var message = messageBuilder.build();
-            final var result = sendSelfMessage(message);
-            return new Pair<>(timestamp, result);
-        } finally {
-            account.save();
-        }
+        var message = messageBuilder.build();
+        final var result = sendSelfMessage(message);
+        return new Pair<>(timestamp, result);
     }
 
     private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
         var messageSender = createMessageSender();
 
-        var recipient = account.getSelfAddress();
+        var recipientId = account.getSelfRecipientId();
 
-        final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(resolveRecipient(recipient));
+        final var unidentifiedAccess = unidentifiedAccessHelper.getAccessFor(recipientId);
+        var recipient = resolveSignalServiceAddress(recipientId);
         var transcript = new SentTranscriptMessage(Optional.of(recipient),
                 message.getTimestamp(),
                 message,
@@ -1429,33 +1537,50 @@ public class Manager implements Closeable {
     }
 
     private SendMessageResult sendMessage(
-            SignalServiceAddress address, SignalServiceDataMessage message
+            RecipientId recipientId, SignalServiceDataMessage message
     ) throws IOException {
         var messageSender = createMessageSender();
 
+        final var address = resolveSignalServiceAddress(recipientId);
         try {
-            return messageSender.sendMessage(address,
-                    unidentifiedAccessHelper.getAccessFor(resolveRecipient(address)),
-                    message);
+            try {
+                return messageSender.sendMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId), message);
+            } catch (UnregisteredUserException e) {
+                final var newRecipientId = refreshRegisteredUser(recipientId);
+                return messageSender.sendMessage(resolveSignalServiceAddress(newRecipientId),
+                        unidentifiedAccessHelper.getAccessFor(newRecipientId),
+                        message);
+            }
         } catch (UntrustedIdentityException e) {
             return SendMessageResult.identityFailure(address, e.getIdentityKey());
         }
     }
 
-    private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, org.whispersystems.libsignal.UntrustedIdentityException {
-        var cipher = new SignalServiceCipher(account.getSelfAddress(),
-                account.getSignalProtocolStore(),
-                certificateValidator);
+    private SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
+        var messageSender = createMessageSender();
+
+        final var address = resolveSignalServiceAddress(recipientId);
         try {
-            return cipher.decrypt(envelope);
-        } catch (ProtocolUntrustedIdentityException e) {
-            if (e.getCause() instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
-                throw (org.whispersystems.libsignal.UntrustedIdentityException) e.getCause();
+            try {
+                return messageSender.sendNullMessage(address, unidentifiedAccessHelper.getAccessFor(recipientId));
+            } catch (UnregisteredUserException e) {
+                final var newRecipientId = refreshRegisteredUser(recipientId);
+                final var newAddress = resolveSignalServiceAddress(newRecipientId);
+                return messageSender.sendNullMessage(newAddress, unidentifiedAccessHelper.getAccessFor(newRecipientId));
             }
-            throw new AssertionError(e);
+        } catch (UntrustedIdentityException e) {
+            return SendMessageResult.identityFailure(address, e.getIdentityKey());
         }
     }
 
+    private SignalServiceContent decryptMessage(SignalServiceEnvelope envelope) throws InvalidMetadataMessageException, ProtocolInvalidMessageException, ProtocolDuplicateMessageException, ProtocolLegacyMessageException, ProtocolInvalidKeyIdException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolNoSessionException, ProtocolInvalidKeyException, SelfSendException, UnsupportedDataMessageException, ProtocolUntrustedIdentityException {
+        var cipher = new SignalServiceCipher(account.getSelfAddress(),
+                account.getSignalProtocolStore(),
+                sessionLock,
+                certificateValidator);
+        return cipher.decrypt(envelope);
+    }
+
     private void handleEndSession(RecipientId recipientId) {
         account.getSessionStore().deleteAllSessions(recipientId);
     }
@@ -1637,10 +1762,12 @@ public class Manager implements Closeable {
             if (signedGroupChange != null
                     && groupInfoV2.getGroup() != null
                     && groupInfoV2.getGroup().getRevision() + 1 == revision) {
-                group = groupHelper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(), signedGroupChange, groupMasterKey);
+                group = groupV2Helper.getUpdatedDecryptedGroup(groupInfoV2.getGroup(),
+                        signedGroupChange,
+                        groupMasterKey);
             }
             if (group == null) {
-                group = groupHelper.getDecryptedGroup(groupSecretParams);
+                group = groupV2Helper.getDecryptedGroup(groupSecretParams);
             }
             if (group != null) {
                 storeProfileKeysFromMembers(group);
@@ -1658,11 +1785,11 @@ public class Manager implements Closeable {
 
     private void storeProfileKeysFromMembers(final DecryptedGroup group) {
         for (var member : group.getMembersList()) {
-            final var address = resolveRecipient(new SignalServiceAddress(UuidUtil.parseOrThrow(member.getUuid()
-                    .toByteArray()), null));
+            final var uuid = UuidUtil.parseOrThrow(member.getUuid().toByteArray());
+            final var recipientId = account.getRecipientStore().resolveRecipient(uuid);
             try {
                 account.getProfileStore()
-                        .storeProfileKey(address, new ProfileKey(member.getProfileKey().toByteArray()));
+                        .storeProfileKey(recipientId, new ProfileKey(member.getProfileKey().toByteArray()));
             } catch (InvalidInputException ignored) {
             }
         }
@@ -1697,10 +1824,10 @@ public class Manager implements Closeable {
         if (!envelope.isReceipt()) {
             try {
                 content = decryptMessage(envelope);
-            } catch (org.whispersystems.libsignal.UntrustedIdentityException e) {
+            } catch (ProtocolUntrustedIdentityException e) {
                 if (!envelope.hasSource()) {
-                    final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) e)
-                            .getName());
+                    final var identifier = e.getSender();
+                    final var recipientId = resolveRecipient(identifier);
                     try {
                         account.getMessageCache().replaceSender(cachedMessage, recipientId);
                     } catch (IOException ioException) {
@@ -1715,7 +1842,6 @@ public class Manager implements Closeable {
             }
             actions = handleMessage(envelope, content, ignoreAttachments);
         }
-        account.save();
         handler.handleMessage(envelope, content, null);
         cachedMessage.delete();
         return actions;
@@ -1763,7 +1889,6 @@ public class Manager implements Closeable {
                                 logger.warn("Message action failed.", e);
                             }
                         }
-                        account.save();
                         queuedActions.clear();
                         queuedActions = null;
                     }
@@ -1778,6 +1903,7 @@ public class Manager implements Closeable {
 
             if (envelope.hasSource()) {
                 // Store uuid if we don't have it already
+                // address/uuid in envelope is sent by server
                 resolveRecipientTrusted(envelope.getSourceAddress());
             }
             final var notAGroupMember = isNotAGroupMember(envelope, content);
@@ -1787,7 +1913,17 @@ public class Manager implements Closeable {
                 } catch (Exception e) {
                     exception = e;
                 }
+                if (!envelope.hasSource() && content != null) {
+                    // Store uuid if we don't have it already
+                    // address/uuid is validated by unidentified sender certificate
+                    resolveRecipientTrusted(content.getSender());
+                }
                 var actions = handleMessage(envelope, content, ignoreAttachments);
+                if (exception instanceof ProtocolInvalidMessageException) {
+                    final var sender = resolveRecipient(((ProtocolInvalidMessageException) exception).getSender());
+                    logger.debug("Received invalid message, queuing renew session action.");
+                    actions.add(new RenewSessionAction(sender));
+                }
                 if (hasCaughtUpWithOldMessages) {
                     for (var action : actions) {
                         try {
@@ -1803,7 +1939,6 @@ public class Manager implements Closeable {
                     queuedActions.addAll(actions);
                 }
             }
-            account.save();
             if (isMessageBlocked(envelope, content)) {
                 logger.info("Ignoring a message from blocked user/group: {}", envelope.getTimestamp());
             } else if (notAGroupMember) {
@@ -1812,9 +1947,9 @@ public class Manager implements Closeable {
                 handler.handleMessage(envelope, content, exception);
             }
             if (cachedMessage[0] != null) {
-                if (exception instanceof org.whispersystems.libsignal.UntrustedIdentityException) {
-                    final var recipientId = resolveRecipient(((org.whispersystems.libsignal.UntrustedIdentityException) exception)
-                            .getName());
+                if (exception instanceof ProtocolUntrustedIdentityException) {
+                    final var identifier = ((ProtocolUntrustedIdentityException) exception).getSender();
+                    final var recipientId = resolveRecipient(identifier);
                     queuedActions.add(new RetrieveProfileAction(recipientId));
                     if (!envelope.hasSource()) {
                         try {
@@ -2444,7 +2579,7 @@ public class Manager implements Closeable {
         final var group = account.getGroupStore().getGroup(groupId);
         if (group instanceof GroupInfoV2 && ((GroupInfoV2) group).getGroup() == null) {
             final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(((GroupInfoV2) group).getMasterKey());
-            ((GroupInfoV2) group).setGroup(groupHelper.getDecryptedGroup(groupSecretParams), this::resolveRecipient);
+            ((GroupInfoV2) group).setGroup(groupV2Helper.getDecryptedGroup(groupSecretParams), this::resolveRecipient);
             account.getGroupStore().updateGroup(group);
         }
         return group;
@@ -2529,14 +2664,6 @@ public class Manager implements Closeable {
                 theirIdentityKey);
     }
 
-    @Deprecated
-    public SignalServiceAddress canonicalizeAndResolveSignalServiceAddress(String identifier) throws InvalidNumberException {
-        var canonicalizedNumber = UuidUtil.isUuid(identifier)
-                ? identifier
-                : PhoneNumberFormatter.formatNumber(identifier, account.getUsername());
-        return resolveSignalServiceAddress(canonicalizedNumber);
-    }
-
     @Deprecated
     public SignalServiceAddress resolveSignalServiceAddress(String identifier) {
         var address = Utils.getSignalServiceAddressFromIdentifier(identifier);