org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage Java Examples

The following examples show how to use org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage. 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: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 6 votes vote down vote up
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
    throws IOException, UntrustedIdentityException
{
  byte[] nullMessageBody = DataMessage.newBuilder()
                                      .setBody(Base64.encodeBytes(Util.getRandomLengthBytes(140)))
                                      .build()
                                      .toByteArray();

  NullMessage nullMessage = NullMessage.newBuilder()
                                       .setPadding(ByteString.copyFrom(nullMessageBody))
                                       .build();

  byte[] content          = Content.newBuilder()
                                   .setNullMessage(nullMessage)
                                   .build()
                                   .toByteArray();

  SendMessageResult result = sendMessage(message.getDestination(), getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, false);

  if (result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray());
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), message.getTimestamp(), syncMessage, false);
  }
}
 
Example #2
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
private void sendMessage(VerifiedMessage message, Optional<UnidentifiedAccessPair> unidentifiedAccess)
    throws IOException, UntrustedIdentityException
{
  byte[] nullMessageBody = DataMessage.newBuilder()
                                      .setBody(Base64.encodeBytes(Util.getRandomLengthBytes(140)))
                                      .build()
                                      .toByteArray();

  NullMessage nullMessage = NullMessage.newBuilder()
                                       .setPadding(ByteString.copyFrom(nullMessageBody))
                                       .build();

  byte[] content          = Content.newBuilder()
                                   .setNullMessage(nullMessage)
                                   .build()
                                   .toByteArray();

  SendMessageResult result = sendMessage(message.getDestination(), getTargetUnidentifiedAccess(unidentifiedAccess), message.getTimestamp(), content, false);

  if (result.getSuccess().isNeedsSync()) {
    byte[] syncMessage = createMultiDeviceVerifiedContent(message, nullMessage.toByteArray());
    sendMessage(localAddress, Optional.<UnidentifiedAccess>absent(), message.getTimestamp(), syncMessage, false);
  }
}
 
Example #3
Source File: SignalServiceCipher.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
private SignalServiceDataMessage.Quote createQuote(DataMessage content) {
  if (!content.hasQuote()) return null;

  List<SignalServiceDataMessage.Quote.QuotedAttachment> attachments = new LinkedList<>();

  for (DataMessage.Quote.QuotedAttachment attachment : content.getQuote().getAttachmentsList()) {
    attachments.add(new SignalServiceDataMessage.Quote.QuotedAttachment(attachment.getContentType(),
                                                                        attachment.getFileName(),
                                                                        attachment.hasThumbnail() ? createAttachmentPointer(attachment.getThumbnail()) : null));
  }

  if (SignalServiceAddress.isValidAddress(content.getQuote().getAuthorUuid(), content.getQuote().getAuthorE164())) {
    SignalServiceAddress address = new SignalServiceAddress(UuidUtil.parseOrNull(content.getQuote().getAuthorUuid()), content.getQuote().getAuthorE164());

    return new SignalServiceDataMessage.Quote(content.getQuote().getId(),
                                              address,
                                              content.getQuote().getText(),
                                              attachments);
  } else {
    Log.w(TAG, "Quote was missing an author! Returning null.");
    return null;
  }
}
 
Example #4
Source File: SignalServiceCipher.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
private List<Preview> createPreviews(DataMessage content) {
  if (content.getPreviewCount() <= 0) return null;

  List<Preview> results = new LinkedList<>();

  for (DataMessage.Preview preview : content.getPreviewList()) {
    SignalServiceAttachment attachment = null;

    if (preview.hasImage()) {
      attachment = createAttachmentPointer(preview.getImage());
    }

    results.add(new Preview(preview.getUrl(),
                            preview.getTitle(),
                            Optional.fromNullable(attachment)));
  }

  return results;
}
 
Example #5
Source File: SignalServiceCipher.java    From libsignal-service-java with GNU General Public License v3.0 6 votes vote down vote up
private Sticker createSticker(DataMessage content) {
  if (!content.hasSticker()                ||
      !content.getSticker().hasPackId()    ||
      !content.getSticker().hasPackKey()   ||
      !content.getSticker().hasStickerId() ||
      !content.getSticker().hasData())
  {
    return null;
  }

  DataMessage.Sticker sticker = content.getSticker();

  return new Sticker(sticker.getPackId().toByteArray(),
                     sticker.getPackKey().toByteArray(),
                     sticker.getStickerId(),
                     createAttachmentPointer(sticker.getData()));
}
 
Example #6
Source File: SignalServiceCipher.java    From bcm-android with GNU General Public License v3.0 5 votes vote down vote up
private SignalServiceDataMessage createSignalServiceMessage(SignalServiceProtos.Envelope envelope, DataMessage content) throws InvalidMessageException {
  SignalServiceGroup            groupInfo        = createGroupInfo(envelope, content);
  List<SignalServiceAttachment> attachments      = new LinkedList<>();
  boolean                       endSession       = ((content.getFlags() & DataMessage.Flags.END_SESSION_VALUE) != 0);
  boolean                       expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0);
  boolean                       profileKeyUpdate = ((content.getFlags() & DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE) != 0);
    boolean newGroupShare = ((content.getFlags() & 8) != 0);//添加 newGroupShare 类型

  for (AttachmentPointer pointer : content.getAttachmentsList()) {
    attachments.add(new SignalServiceAttachmentPointer(pointer.getId(),
                                                       pointer.getContentType(),
                                                       pointer.getKey().toByteArray(),
                                                       envelope.getRelay(),
                                                       pointer.hasSize() ? Optional.of(pointer.getSize()) : Optional.<Integer>absent(),
                                                       pointer.hasThumbnail() ? Optional.of(pointer.getThumbnail().toByteArray()): Optional.<byte[]>absent(),
                                                       pointer.hasDigest() ? Optional.of(pointer.getDigest().toByteArray()) : Optional.<byte[]>absent(),
                                                       pointer.hasFileName() ? Optional.of(pointer.getFileName()) : Optional.<String>absent(),
                                                       (pointer.getFlags() & AttachmentPointer.Flags.VOICE_MESSAGE_VALUE) != 0,
                                                       pointer.hasUrl() ? Optional.of(pointer.getUrl()) : Optional.<String>absent()));
  }

  if (content.hasTimestamp() && content.getTimestamp() != envelope.getTimestamp()) {
    throw new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + envelope.getTimestamp());
  }

  return new SignalServiceDataMessage(envelope.getTimestamp(), groupInfo, attachments,
                                      content.getBody(), endSession, content.getExpireTimer(),
                                      expirationUpdate, content.hasProfileKey() ? content.getProfileKey().toByteArray() : null,
          profileKeyUpdate, newGroupShare);
}
 
Example #7
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 4 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional<SignalServiceAddress> recipient,
                                                      long timestamp, List<SendMessageResult> sendMessageResults,
                                                      boolean isRecipientUpdate)
{
  try {
    Content.Builder          container   = Content.newBuilder();
    SyncMessage.Builder      syncMessage = createSyncMessageBuilder();
    SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder();
    DataMessage              dataMessage = Content.parseFrom(content).getDataMessage();

    sentMessage.setTimestamp(timestamp);
    sentMessage.setMessage(dataMessage);

    for (SendMessageResult result : sendMessageResults) {
      if (result.getSuccess() != null) {
        SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builder = SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder();

        if (result.getAddress().getUuid().isPresent()) {
          builder = builder.setDestinationUuid(result.getAddress().getUuid().get().toString());
        }

        if (result.getAddress().getNumber().isPresent()) {
          builder = builder.setDestinationE164(result.getAddress().getNumber().get());
        }

        builder.setUnidentified(result.getSuccess().isUnidentified());

        sentMessage.addUnidentifiedStatus(builder.build());
      }
    }

    if (recipient.isPresent()) {
      if (recipient.get().getUuid().isPresent())   sentMessage.setDestinationUuid(recipient.get().getUuid().get().toString());
      if (recipient.get().getNumber().isPresent()) sentMessage.setDestinationE164(recipient.get().getNumber().get());
    }

    if (dataMessage.getExpireTimer() > 0) {
      sentMessage.setExpirationStartTimestamp(System.currentTimeMillis());
    }

    if (dataMessage.getIsViewOnce()) {
      dataMessage = dataMessage.toBuilder().clearAttachments().build();
      sentMessage.setMessage(dataMessage);
    }

    sentMessage.setIsRecipientUpdate(isRecipientUpdate);

    return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray();
  } catch (InvalidProtocolBufferException e) {
    throw new AssertionError(e);
  }
}
 
Example #8
Source File: SignalServiceMessageSender.java    From mollyim-android with GNU General Public License v3.0 4 votes vote down vote up
private List<DataMessage.Contact> createSharedContactContent(List<SharedContact> contacts) throws IOException {
  List<DataMessage.Contact> results = new LinkedList<>();

  for (SharedContact contact : contacts) {
    DataMessage.Contact.Name.Builder nameBuilder    = DataMessage.Contact.Name.newBuilder();

    if (contact.getName().getFamily().isPresent())  nameBuilder.setFamilyName(contact.getName().getFamily().get());
    if (contact.getName().getGiven().isPresent())   nameBuilder.setGivenName(contact.getName().getGiven().get());
    if (contact.getName().getMiddle().isPresent())  nameBuilder.setMiddleName(contact.getName().getMiddle().get());
    if (contact.getName().getPrefix().isPresent())  nameBuilder.setPrefix(contact.getName().getPrefix().get());
    if (contact.getName().getSuffix().isPresent())  nameBuilder.setSuffix(contact.getName().getSuffix().get());
    if (contact.getName().getDisplay().isPresent()) nameBuilder.setDisplayName(contact.getName().getDisplay().get());

    DataMessage.Contact.Builder contactBuilder = DataMessage.Contact.newBuilder()
                                                                    .setName(nameBuilder);

    if (contact.getAddress().isPresent()) {
      for (SharedContact.PostalAddress address : contact.getAddress().get()) {
        DataMessage.Contact.PostalAddress.Builder addressBuilder = DataMessage.Contact.PostalAddress.newBuilder();

        switch (address.getType()) {
          case HOME:   addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.HOME); break;
          case WORK:   addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.WORK); break;
          case CUSTOM: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + address.getType());
        }

        if (address.getCity().isPresent())         addressBuilder.setCity(address.getCity().get());
        if (address.getCountry().isPresent())      addressBuilder.setCountry(address.getCountry().get());
        if (address.getLabel().isPresent())        addressBuilder.setLabel(address.getLabel().get());
        if (address.getNeighborhood().isPresent()) addressBuilder.setNeighborhood(address.getNeighborhood().get());
        if (address.getPobox().isPresent())        addressBuilder.setPobox(address.getPobox().get());
        if (address.getPostcode().isPresent())     addressBuilder.setPostcode(address.getPostcode().get());
        if (address.getRegion().isPresent())       addressBuilder.setRegion(address.getRegion().get());
        if (address.getStreet().isPresent())       addressBuilder.setStreet(address.getStreet().get());

        contactBuilder.addAddress(addressBuilder);
      }
    }

    if (contact.getEmail().isPresent()) {
      for (SharedContact.Email email : contact.getEmail().get()) {
        DataMessage.Contact.Email.Builder emailBuilder = DataMessage.Contact.Email.newBuilder()
                                                                                  .setValue(email.getValue());

        switch (email.getType()) {
          case HOME:   emailBuilder.setType(DataMessage.Contact.Email.Type.HOME);   break;
          case WORK:   emailBuilder.setType(DataMessage.Contact.Email.Type.WORK);   break;
          case MOBILE: emailBuilder.setType(DataMessage.Contact.Email.Type.MOBILE); break;
          case CUSTOM: emailBuilder.setType(DataMessage.Contact.Email.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + email.getType());
        }

        if (email.getLabel().isPresent()) emailBuilder.setLabel(email.getLabel().get());

        contactBuilder.addEmail(emailBuilder);
      }
    }

    if (contact.getPhone().isPresent()) {
      for (SharedContact.Phone phone : contact.getPhone().get()) {
        DataMessage.Contact.Phone.Builder phoneBuilder = DataMessage.Contact.Phone.newBuilder()
                                                                                  .setValue(phone.getValue());

        switch (phone.getType()) {
          case HOME:   phoneBuilder.setType(DataMessage.Contact.Phone.Type.HOME);   break;
          case WORK:   phoneBuilder.setType(DataMessage.Contact.Phone.Type.WORK);   break;
          case MOBILE: phoneBuilder.setType(DataMessage.Contact.Phone.Type.MOBILE); break;
          case CUSTOM: phoneBuilder.setType(DataMessage.Contact.Phone.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + phone.getType());
        }

        if (phone.getLabel().isPresent()) phoneBuilder.setLabel(phone.getLabel().get());

        contactBuilder.addNumber(phoneBuilder);
      }
    }

    if (contact.getAvatar().isPresent()) {
      AttachmentPointer pointer = contact.getAvatar().get().getAttachment().isStream() ? createAttachmentPointer(contact.getAvatar().get().getAttachment().asStream())
                                                                                       : createAttachmentPointer(contact.getAvatar().get().getAttachment().asPointer());
      contactBuilder.setAvatar(DataMessage.Contact.Avatar.newBuilder()
                                                         .setAvatar(pointer)
                                                         .setIsProfile(contact.getAvatar().get().isProfile()));
    }

    if (contact.getOrganization().isPresent()) {
      contactBuilder.setOrganization(contact.getOrganization().get());
    }

    results.add(contactBuilder.build());
  }

  return results;
}
 
Example #9
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 4 votes vote down vote up
private byte[] createMultiDeviceSentTranscriptContent(byte[] content, Optional<SignalServiceAddress> recipient,
                                                      long timestamp, List<SendMessageResult> sendMessageResults,
                                                      boolean isRecipientUpdate)
{
  try {
    Content.Builder          container   = Content.newBuilder();
    SyncMessage.Builder      syncMessage = createSyncMessageBuilder();
    SyncMessage.Sent.Builder sentMessage = SyncMessage.Sent.newBuilder();
    DataMessage              dataMessage = Content.parseFrom(content).getDataMessage();

    sentMessage.setTimestamp(timestamp);
    sentMessage.setMessage(dataMessage);

    for (SendMessageResult result : sendMessageResults) {
      if (result.getSuccess() != null) {
        SyncMessage.Sent.UnidentifiedDeliveryStatus.Builder builder = SyncMessage.Sent.UnidentifiedDeliveryStatus.newBuilder();

        if (result.getAddress().getUuid().isPresent()) {
          builder = builder.setDestinationUuid(result.getAddress().getUuid().get().toString());
        }

        if (result.getAddress().getNumber().isPresent()) {
          builder = builder.setDestinationE164(result.getAddress().getNumber().get());
        }

        builder.setUnidentified(result.getSuccess().isUnidentified());

        sentMessage.addUnidentifiedStatus(builder.build());
      }
    }

    if (recipient.isPresent()) {
      if (recipient.get().getUuid().isPresent())   sentMessage.setDestinationUuid(recipient.get().getUuid().get().toString());
      if (recipient.get().getNumber().isPresent()) sentMessage.setDestinationE164(recipient.get().getNumber().get());
    }

    if (dataMessage.getExpireTimer() > 0) {
      sentMessage.setExpirationStartTimestamp(System.currentTimeMillis());
    }

    if (dataMessage.getIsViewOnce()) {
      dataMessage = dataMessage.toBuilder().clearAttachments().build();
      sentMessage.setMessage(dataMessage);
    }

    sentMessage.setIsRecipientUpdate(isRecipientUpdate);

    return container.setSyncMessage(syncMessage.setSent(sentMessage)).build().toByteArray();
  } catch (InvalidProtocolBufferException e) {
    throw new AssertionError(e);
  }
}
 
Example #10
Source File: SignalServiceMessageSender.java    From libsignal-service-java with GNU General Public License v3.0 4 votes vote down vote up
private List<DataMessage.Contact> createSharedContactContent(List<SharedContact> contacts) throws IOException {
  List<DataMessage.Contact> results = new LinkedList<>();

  for (SharedContact contact : contacts) {
    DataMessage.Contact.Name.Builder nameBuilder    = DataMessage.Contact.Name.newBuilder();

    if (contact.getName().getFamily().isPresent())  nameBuilder.setFamilyName(contact.getName().getFamily().get());
    if (contact.getName().getGiven().isPresent())   nameBuilder.setGivenName(contact.getName().getGiven().get());
    if (contact.getName().getMiddle().isPresent())  nameBuilder.setMiddleName(contact.getName().getMiddle().get());
    if (contact.getName().getPrefix().isPresent())  nameBuilder.setPrefix(contact.getName().getPrefix().get());
    if (contact.getName().getSuffix().isPresent())  nameBuilder.setSuffix(contact.getName().getSuffix().get());
    if (contact.getName().getDisplay().isPresent()) nameBuilder.setDisplayName(contact.getName().getDisplay().get());

    DataMessage.Contact.Builder contactBuilder = DataMessage.Contact.newBuilder()
                                                                    .setName(nameBuilder);

    if (contact.getAddress().isPresent()) {
      for (SharedContact.PostalAddress address : contact.getAddress().get()) {
        DataMessage.Contact.PostalAddress.Builder addressBuilder = DataMessage.Contact.PostalAddress.newBuilder();

        switch (address.getType()) {
          case HOME:   addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.HOME); break;
          case WORK:   addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.WORK); break;
          case CUSTOM: addressBuilder.setType(DataMessage.Contact.PostalAddress.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + address.getType());
        }

        if (address.getCity().isPresent())         addressBuilder.setCity(address.getCity().get());
        if (address.getCountry().isPresent())      addressBuilder.setCountry(address.getCountry().get());
        if (address.getLabel().isPresent())        addressBuilder.setLabel(address.getLabel().get());
        if (address.getNeighborhood().isPresent()) addressBuilder.setNeighborhood(address.getNeighborhood().get());
        if (address.getPobox().isPresent())        addressBuilder.setPobox(address.getPobox().get());
        if (address.getPostcode().isPresent())     addressBuilder.setPostcode(address.getPostcode().get());
        if (address.getRegion().isPresent())       addressBuilder.setRegion(address.getRegion().get());
        if (address.getStreet().isPresent())       addressBuilder.setStreet(address.getStreet().get());

        contactBuilder.addAddress(addressBuilder);
      }
    }

    if (contact.getEmail().isPresent()) {
      for (SharedContact.Email email : contact.getEmail().get()) {
        DataMessage.Contact.Email.Builder emailBuilder = DataMessage.Contact.Email.newBuilder()
                                                                                  .setValue(email.getValue());

        switch (email.getType()) {
          case HOME:   emailBuilder.setType(DataMessage.Contact.Email.Type.HOME);   break;
          case WORK:   emailBuilder.setType(DataMessage.Contact.Email.Type.WORK);   break;
          case MOBILE: emailBuilder.setType(DataMessage.Contact.Email.Type.MOBILE); break;
          case CUSTOM: emailBuilder.setType(DataMessage.Contact.Email.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + email.getType());
        }

        if (email.getLabel().isPresent()) emailBuilder.setLabel(email.getLabel().get());

        contactBuilder.addEmail(emailBuilder);
      }
    }

    if (contact.getPhone().isPresent()) {
      for (SharedContact.Phone phone : contact.getPhone().get()) {
        DataMessage.Contact.Phone.Builder phoneBuilder = DataMessage.Contact.Phone.newBuilder()
                                                                                  .setValue(phone.getValue());

        switch (phone.getType()) {
          case HOME:   phoneBuilder.setType(DataMessage.Contact.Phone.Type.HOME);   break;
          case WORK:   phoneBuilder.setType(DataMessage.Contact.Phone.Type.WORK);   break;
          case MOBILE: phoneBuilder.setType(DataMessage.Contact.Phone.Type.MOBILE); break;
          case CUSTOM: phoneBuilder.setType(DataMessage.Contact.Phone.Type.CUSTOM); break;
          default:     throw new AssertionError("Unknown type: " + phone.getType());
        }

        if (phone.getLabel().isPresent()) phoneBuilder.setLabel(phone.getLabel().get());

        contactBuilder.addNumber(phoneBuilder);
      }
    }

    if (contact.getAvatar().isPresent()) {
      AttachmentPointer pointer = contact.getAvatar().get().getAttachment().isStream() ? createAttachmentPointer(contact.getAvatar().get().getAttachment().asStream())
                                                                                       : createAttachmentPointer(contact.getAvatar().get().getAttachment().asPointer());
      contactBuilder.setAvatar(DataMessage.Contact.Avatar.newBuilder()
                                                         .setAvatar(pointer)
                                                         .setIsProfile(contact.getAvatar().get().isProfile()));
    }

    if (contact.getOrganization().isPresent()) {
      contactBuilder.setOrganization(contact.getOrganization().get());
    }

    results.add(contactBuilder.build());
  }

  return results;
}
 
Example #11
Source File: SignalServiceCipher.java    From libsignal-service-java with GNU General Public License v3.0 4 votes vote down vote up
private SignalServiceDataMessage createSignalServiceMessage(Metadata metadata, DataMessage content)
    throws ProtocolInvalidMessageException, UnsupportedDataMessageException
{
  SignalServiceGroup             groupInfo        = createGroupInfo(content);
  List<SignalServiceAttachment>  attachments      = new LinkedList<>();
  boolean                        endSession       = ((content.getFlags() & DataMessage.Flags.END_SESSION_VALUE            ) != 0);
  boolean                        expirationUpdate = ((content.getFlags() & DataMessage.Flags.EXPIRATION_TIMER_UPDATE_VALUE) != 0);
  boolean                        profileKeyUpdate = ((content.getFlags() & DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE     ) != 0);
  SignalServiceDataMessage.Quote quote            = createQuote(content);
  List<SharedContact>            sharedContacts   = createSharedContacts(content);
  List<Preview>                  previews         = createPreviews(content);
  Sticker                        sticker          = createSticker(content);

  if (content.getRequiredProtocolVersion() > DataMessage.ProtocolVersion.CURRENT.getNumber()) {
    throw new UnsupportedDataMessageException(DataMessage.ProtocolVersion.CURRENT.getNumber(),
                                              content.getRequiredProtocolVersion(),
                                              metadata.getSender().getIdentifier(),
                                              metadata.getSenderDevice(),
                                              Optional.fromNullable(groupInfo));
  }

  for (AttachmentPointer pointer : content.getAttachmentsList()) {
    attachments.add(createAttachmentPointer(pointer));
  }

  if (content.hasTimestamp() && content.getTimestamp() != metadata.getTimestamp()) {
    throw new ProtocolInvalidMessageException(new InvalidMessageException("Timestamps don't match: " + content.getTimestamp() + " vs " + metadata.getTimestamp()),
                                                                          metadata.getSender().getIdentifier(),
                                                                          metadata.getSenderDevice());
  }

  return new SignalServiceDataMessage(metadata.getTimestamp(),
                                      groupInfo,
                                      attachments,
                                      content.getBody(),
                                      endSession,
                                      content.getExpireTimer(),
                                      expirationUpdate,
                                      content.hasProfileKey() ? content.getProfileKey().toByteArray() : null,
                                      profileKeyUpdate,
                                      quote,
                                      sharedContacts,
                                      previews,
                                      sticker,
                                      content.getIsViewOnce());
}