Java Code Examples for org.whispersystems.signalservice.api.SignalServiceMessageSender#sendMessage()

The following examples show how to use org.whispersystems.signalservice.api.SignalServiceMessageSender#sendMessage() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: MultiDeviceStickerPackSyncJob.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
@Override
protected void onRun() throws Exception {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  List<StickerPackOperationMessage> operations = new LinkedList<>();

  try (StickerPackRecordReader reader = new StickerPackRecordReader(DatabaseFactory.getStickerDatabase(context).getInstalledStickerPacks())) {
    StickerPackRecord pack;
    while ((pack = reader.getNext()) != null) {
      byte[] packIdBytes  = Hex.fromStringCondensed(pack.getPackId());
      byte[] packKeyBytes = Hex.fromStringCondensed(pack.getPackKey());

      operations.add(new StickerPackOperationMessage(packIdBytes, packKeyBytes, StickerPackOperationMessage.Type.INSTALL));
    }
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(operations),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 2
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 6 votes vote down vote up
private SendMessageResult sendSelfMessage(SignalServiceDataMessage message) throws IOException {
    SignalServiceMessageSender messageSender = getMessageSender();

    SignalServiceAddress recipient = account.getSelfAddress();

    final Optional<UnidentifiedAccessPair> unidentifiedAccess = getAccessFor(recipient);
    SentTranscriptMessage transcript = new SentTranscriptMessage(Optional.of(recipient),
            message.getTimestamp(),
            message,
            message.getExpiresInSeconds(),
            Collections.singletonMap(recipient, unidentifiedAccess.isPresent()),
            false);
    SignalServiceSyncMessage syncMessage = SignalServiceSyncMessage.forSentTranscript(transcript);

    try {
        messageSender.sendMessage(syncMessage, unidentifiedAccess);
        return SendMessageResult.success(recipient, unidentifiedAccess.isPresent(), false);
    } catch (UntrustedIdentityException e) {
        account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
        return SendMessageResult.identityFailure(recipient, e.getIdentityKey());
    }
}
 
Example 3
Source File: MultiDeviceReadUpdateJob.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device...");
    return;
  }

  List<ReadMessage> readMessages = new LinkedList<>();

  for (SerializableSyncMessageId messageId : messageIds) {
    Recipient recipient = Recipient.resolved(RecipientId.from(messageId.recipientId));
    readMessages.add(new ReadMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp));
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  messageSender.sendMessage(SignalServiceSyncMessage.forRead(readMessages), UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 4
Source File: MultiDeviceStickerPackOperationJob.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
@Override
protected void onRun() throws Exception {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  byte[] packIdBytes  = Hex.fromStringCondensed(packId);
  byte[] packKeyBytes = Hex.fromStringCondensed(packKey);

  StickerPackOperationMessage.Type remoteType;

  switch (type) {
    case INSTALL: remoteType = StickerPackOperationMessage.Type.INSTALL; break;
    case REMOVE:  remoteType = StickerPackOperationMessage.Type.REMOVE; break;
    default:      throw new AssertionError("No matching type?");
  }

  SignalServiceMessageSender  messageSender        = ApplicationDependencies.getSignalServiceMessageSender();
  StickerPackOperationMessage stickerPackOperation = new StickerPackOperationMessage(packIdBytes, packKeyBytes, remoteType);

  messageSender.sendMessage(SignalServiceSyncMessage.forStickerPackOperations(Collections.singletonList(stickerPackOperation)),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 5
Source File: MultiDeviceMessageRequestResponseJob.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  Recipient                  recipient     = Recipient.resolved(threadRecipient);


  MessageRequestResponseMessage response;

  if (recipient.isGroup()) {
    response = MessageRequestResponseMessage.forGroup(recipient.getGroupId().get().getDecodedId(), localToRemoteType(type));
  } else {
    response = MessageRequestResponseMessage.forIndividual(RecipientUtil.toSignalServiceAddress(context, recipient), localToRemoteType(type));
  }

  messageSender.sendMessage(SignalServiceSyncMessage.forMessageRequestResponse(response),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 6
Source File: RequestGroupInfoJob.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  SignalServiceGroup       group   = SignalServiceGroup.newBuilder(Type.REQUEST_INFO)
                                                       .withId(groupId.getDecodedId())
                                                       .build();

  SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder()
                                                             .asGroupMessage(group)
                                                             .withTimestamp(System.currentTimeMillis())
                                                             .build();

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  Recipient                  recipient     = Recipient.resolved(source);

  messageSender.sendMessage(RecipientUtil.toSignalServiceAddress(context, recipient),
                            UnidentifiedAccessUtil.getAccessFor(context, recipient),
                            message);
}
 
Example 7
Source File: MultiDeviceBlockedUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
public void onRun()
    throws IOException, UntrustedIdentityException
{
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  RecipientDatabase database = DatabaseFactory.getRecipientDatabase(context);

  try (RecipientReader reader = database.readerForBlocked(database.getBlocked())) {
    List<SignalServiceAddress> blockedIndividuals = new LinkedList<>();
    List<byte[]>               blockedGroups      = new LinkedList<>();

    Recipient recipient;

    while ((recipient = reader.getNext()) != null) {
      if (recipient.isPushGroup()) {
        blockedGroups.add(recipient.requireGroupId().getDecodedId());
      } else if (recipient.hasServiceIdentifier()) {
        blockedIndividuals.add(RecipientUtil.toSignalServiceAddress(context, recipient));
      }
    }

    SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
    messageSender.sendMessage(SignalServiceSyncMessage.forBlocked(new BlockedListMessage(blockedIndividuals, blockedGroups)),
                              UnidentifiedAccessUtil.getAccessForSync(context));
  }
}
 
Example 8
Source File: Manager.java    From signald with GNU General Public License v3.0 5 votes vote down vote up
private void sendSyncMessage(SignalServiceSyncMessage message)
        throws IOException, UntrustedIdentityException {
    SignalServiceMessageSender messageSender = new SignalServiceMessageSender(serviceConfiguration, accountData.username, accountData.password,
            accountData.deviceId, accountData.axolotlStore, USER_AGENT, true, Optional.fromNullable(messagePipe), Optional.fromNullable(unidentifiedMessagePipe), Optional.<SignalServiceMessageSender.EventListener>absent());
    try {
        messageSender.sendMessage(message, Optional.<UnidentifiedAccessPair>absent());
    } catch (UntrustedIdentityException e) {
        accountData.axolotlStore.identityKeyStore.saveIdentity(e.getE164Number(), e.getIdentityKey(), TrustLevel.UNTRUSTED);
        throw e;
    }
}
 
Example 9
Source File: MultiDeviceKeysUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  SignalServiceMessageSender messageSender     = ApplicationDependencies.getSignalServiceMessageSender();
  StorageKey                 storageServiceKey = SignalStore.storageServiceValues().getOrCreateStorageKey();

  messageSender.sendMessage(SignalServiceSyncMessage.forKeys(new KeysMessage(Optional.fromNullable(storageServiceKey))),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 10
Source File: RemoteDeleteSendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private @NonNull List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations, long targetSentTimestamp)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withRemoteDelete(new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp));

  if (conversationRecipient.isGroup()) {
    GroupUtil.setDataMessageGroupContext(context, dataMessage, conversationRecipient.requireGroupId().requirePush());
  }

  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));

  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example 11
Source File: ReactionSendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private @NonNull List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations, @NonNull Recipient targetAuthor, long targetSentTimestamp)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withReaction(buildReaction(context, reaction, remove, targetAuthor, targetSentTimestamp));

  if (conversationRecipient.isGroup()) {
    GroupUtil.setDataMessageGroupContext(context, dataMessage, conversationRecipient.requireGroupId().requirePush());
  }


  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example 12
Source File: MultiDeviceViewOnceOpenJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device...");
    return;
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  Recipient                  recipient     = Recipient.resolved(RecipientId.from(messageId.recipientId));
  ViewOnceOpenMessage        openMessage   = new ViewOnceOpenMessage(RecipientUtil.toSignalServiceAddress(context, recipient), messageId.timestamp);

  messageSender.sendMessage(SignalServiceSyncMessage.forViewOnceOpen(openMessage), UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 13
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 5 votes vote down vote up
private SendMessageResult sendMessage(SignalServiceAddress address, SignalServiceDataMessage message) throws IOException {
    SignalServiceMessageSender messageSender = getMessageSender();

    try {
        return messageSender.sendMessage(address, getAccessFor(address), message);
    } catch (UntrustedIdentityException e) {
        account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
        return SendMessageResult.identityFailure(address, e.getIdentityKey());
    }
}
 
Example 14
Source File: ProfileKeySendJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private List<Recipient> deliver(@NonNull Recipient conversationRecipient, @NonNull List<Recipient> destinations) throws IOException, UntrustedIdentityException {
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .asProfileKeyUpdate(true)
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .withProfileKey(Recipient.self().resolve().getProfileKey());

  if (conversationRecipient.isGroup()) {
    dataMessage.asGroupMessage(new SignalServiceGroup(conversationRecipient.requireGroupId().getDecodedId()));
  }

  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example 15
Source File: MultiDeviceProfileContentUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
protected void onRun() throws Exception {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();

  messageSender.sendMessage(SignalServiceSyncMessage.forFetchLatest(SignalServiceSyncMessage.FetchType.LOCAL_PROFILE),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 16
Source File: MultiDeviceConfigurationUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device, aborting...");
    return;
  }

  SignalServiceMessageSender messageSender = ApplicationDependencies.getSignalServiceMessageSender();
  messageSender.sendMessage(SignalServiceSyncMessage.forConfiguration(new ConfigurationMessage(Optional.of(readReceiptsEnabled),
                                                                                               Optional.of(unidentifiedDeliveryIndicatorsEnabled),
                                                                                               Optional.of(typingIndicatorsEnabled),
                                                                                               Optional.of(linkPreviewsEnabled))),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 17
Source File: MultiDeviceProfileKeyUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
@Override
public void onRun() throws IOException, UntrustedIdentityException {
  if (!TextSecurePreferences.isMultiDevice(context)) {
    Log.i(TAG, "Not multi device...");
    return;
  }

  Optional<ProfileKey>       profileKey = Optional.of(ProfileKeyUtil.getSelfProfileKey());
  ByteArrayOutputStream      baos       = new ByteArrayOutputStream();
  DeviceContactsOutputStream out        = new DeviceContactsOutputStream(baos);

  out.write(new DeviceContact(RecipientUtil.toSignalServiceAddress(context, Recipient.self()),
                              Optional.absent(),
                              Optional.absent(),
                              Optional.absent(),
                              Optional.absent(),
                              profileKey,
                              false,
                              Optional.absent(),
                              Optional.absent(),
                              false));

  out.close();

  SignalServiceMessageSender    messageSender    = ApplicationDependencies.getSignalServiceMessageSender();
  SignalServiceAttachmentStream attachmentStream = SignalServiceAttachment.newStreamBuilder()
                                                                          .withStream(new ByteArrayInputStream(baos.toByteArray()))
                                                                          .withContentType("application/octet-stream")
                                                                          .withLength(baos.toByteArray().length)
                                                                          .build();

  SignalServiceSyncMessage      syncMessage      = SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, false));

  messageSender.sendMessage(syncMessage, UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 18
Source File: MultiDeviceGroupUpdateJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private void sendUpdate(SignalServiceMessageSender messageSender, File contactsFile)
    throws IOException, UntrustedIdentityException
{
  FileInputStream               contactsFileStream = new FileInputStream(contactsFile);
  SignalServiceAttachmentStream attachmentStream   = SignalServiceAttachment.newStreamBuilder()
                                                                            .withStream(contactsFileStream)
                                                                            .withContentType("application/octet-stream")
                                                                            .withLength(contactsFile.length())
                                                                            .build();

  messageSender.sendMessage(SignalServiceSyncMessage.forGroups(attachmentStream),
                            UnidentifiedAccessUtil.getAccessForSync(context));
}
 
Example 19
Source File: LeaveGroupJob.java    From mollyim-android with GNU General Public License v3.0 5 votes vote down vote up
private static @NonNull List<Recipient> deliver(@NonNull Context context,
                                                @NonNull GroupId.Push groupId,
                                                @NonNull String name,
                                                @NonNull List<RecipientId> members,
                                                @NonNull List<RecipientId> destinations)
    throws IOException, UntrustedIdentityException
{
  SignalServiceMessageSender             messageSender      = ApplicationDependencies.getSignalServiceMessageSender();
  List<SignalServiceAddress>             addresses          = Stream.of(destinations).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<SignalServiceAddress>             memberAddresses    = Stream.of(members).map(Recipient::resolved).map(t -> RecipientUtil.toSignalServiceAddress(context, t)).toList();
  List<Optional<UnidentifiedAccessPair>> unidentifiedAccess = Stream.of(destinations).map(Recipient::resolved).map(recipient -> UnidentifiedAccessUtil.getAccessFor(context, recipient)).toList();
  SignalServiceGroup                     serviceGroup       = new SignalServiceGroup(SignalServiceGroup.Type.QUIT, groupId.getDecodedId(), name, memberAddresses, null);
  SignalServiceDataMessage.Builder       dataMessage        = SignalServiceDataMessage.newBuilder()
                                                                                      .withTimestamp(System.currentTimeMillis())
                                                                                      .asGroupMessage(serviceGroup);


  List<SendMessageResult> results = messageSender.sendMessage(addresses, unidentifiedAccess, false, dataMessage.build());

  Stream.of(results)
        .filter(r -> r.getIdentityFailure() != null)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Identity failure for " + r.getId()));

  Stream.of(results)
        .filter(SendMessageResult::isUnregisteredFailure)
        .map(SendMessageResult::getAddress)
        .map(a -> Recipient.externalPush(context, a))
        .forEach(r -> Log.w(TAG, "Unregistered failure for " + r.getId()));


  return Stream.of(results)
               .filter(r -> r.getSuccess() != null || r.getIdentityFailure() != null || r.isUnregisteredFailure())
               .map(SendMessageResult::getAddress)
               .map(a -> Recipient.externalPush(context, a))
               .toList();
}
 
Example 20
Source File: Manager.java    From signal-cli with GNU General Public License v3.0 4 votes vote down vote up
private List<SendMessageResult> sendMessage(SignalServiceDataMessage.Builder messageBuilder, Collection<SignalServiceAddress> recipients)
        throws IOException {
    if (messagePipe == null) {
        messagePipe = getMessageReceiver().createMessagePipe();
    }
    if (unidentifiedMessagePipe == null) {
        unidentifiedMessagePipe = getMessageReceiver().createUnidentifiedMessagePipe();
    }
    SignalServiceDataMessage message = null;
    try {
        message = messageBuilder.build();
        if (message.getGroupContext().isPresent()) {
            try {
                SignalServiceMessageSender messageSender = getMessageSender();
                final boolean isRecipientUpdate = false;
                List<SendMessageResult> result = messageSender.sendMessage(new ArrayList<>(recipients), getAccessFor(recipients), isRecipientUpdate, message);
                for (SendMessageResult r : result) {
                    if (r.getIdentityFailure() != null) {
                        account.getSignalProtocolStore().saveIdentity(r.getAddress(), r.getIdentityFailure().getIdentityKey(), TrustLevel.UNTRUSTED);
                    }
                }
                return result;
            } catch (UntrustedIdentityException e) {
                account.getSignalProtocolStore().saveIdentity(resolveSignalServiceAddress(e.getIdentifier()), e.getIdentityKey(), TrustLevel.UNTRUSTED);
                return Collections.emptyList();
            }
        } else {
            // Send to all individually, so sync messages are sent correctly
            List<SendMessageResult> results = new ArrayList<>(recipients.size());
            for (SignalServiceAddress address : recipients) {
                ContactInfo contact = account.getContactStore().getContact(address);
                if (contact != null) {
                    messageBuilder.withExpiration(contact.messageExpirationTime);
                    messageBuilder.withProfileKey(account.getProfileKey().serialize());
                } else {
                    messageBuilder.withExpiration(0);
                    messageBuilder.withProfileKey(null);
                }
                message = messageBuilder.build();
                if (address.matches(account.getSelfAddress())) {
                    results.add(sendSelfMessage(message));
                } else {
                    results.add(sendMessage(address, message));
                }
            }
            return results;
        }
    } finally {
        if (message != null && message.isEndSession()) {
            for (SignalServiceAddress recipient : recipients) {
                handleEndSession(recipient);
            }
        }
        account.save();
    }
}