diff --git a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java index 75d79edbd3..00425988ec 100644 --- a/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java +++ b/src/org/thoughtcrime/securesms/jobs/MultiDeviceContactUpdateJob.java @@ -37,6 +37,7 @@ import org.whispersystems.signalservice.api.messages.multidevice.DeviceContact; import org.whispersystems.signalservice.api.messages.multidevice.DeviceContactsOutputStream; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; +import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; @@ -62,41 +63,57 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy private static final long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6); private static final String KEY_ADDRESS = "address"; + private static final String KEY_RECIPIENT = "recipient"; private static final String KEY_FORCE_SYNC = "force_sync"; @Inject SignalServiceMessageSender messageSender; private @Nullable String address; + // The recipient of this sync message. If null then we send to all devices + private @Nullable String recipient; + private boolean forceSync; + /** + * Create a full contact sync job which syncs across to all other devices + */ public MultiDeviceContactUpdateJob(@NonNull Context context) { this(context, false); } + public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { this(context, null, forceSync); } - public MultiDeviceContactUpdateJob(@NonNull Context context, boolean forceSync) { - this(context, null, forceSync); + /** + * Create a full contact sync job which only gets sent to `recipient` + */ + public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, boolean forceSync) { + this(context, recipient, null, forceSync); } + /** + * Create a single contact sync job which syncs across `address` to the all other devices + */ public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address) { - this(context, address, true); + this(context, null, address, true); } - public MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address address, boolean forceSync) { + private MultiDeviceContactUpdateJob(@NonNull Context context, @Nullable Address recipient, @Nullable Address address, boolean forceSync) { this(new Job.Parameters.Builder() .addConstraint(NetworkConstraint.KEY) .setQueue("MultiDeviceContactUpdateJob") .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(3) + .setMaxAttempts(1) .build(), + recipient, address, forceSync); } - private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address address, boolean forceSync) { + private MultiDeviceContactUpdateJob(@NonNull Job.Parameters parameters, @Nullable Address recipient, @Nullable Address address, boolean forceSync) { super(parameters); this.forceSync = forceSync; + this.recipient = (recipient != null) ? recipient.serialize() : null; if (address != null) this.address = address.serialize(); else this.address = null; @@ -106,6 +123,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy public @NonNull Data serialize() { return new Data.Builder().putString(KEY_ADDRESS, address) .putBoolean(KEY_FORCE_SYNC, forceSync) + .putString(KEY_RECIPIENT, recipient) .build(); } @@ -124,7 +142,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy } if (address == null) generateFullContactUpdate(); - else generateSingleContactUpdate(Address.fromSerialized(address)); + else if (address != TextSecurePreferences.getMasterHexEncodedPublicKey(context)) generateSingleContactUpdate(Address.fromSerialized(address)); } private void generateSingleContactUpdate(@NonNull Address address) @@ -245,7 +263,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy @Override public boolean onShouldRetry(@NonNull Exception exception) { - if (exception instanceof PushNetworkException) return true; + // Loki - Disabled because we have our own retrying + // if (exception instanceof PushNetworkException) return true; return false; } @@ -265,9 +284,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy .withLength(contactsFile.length()) .build(); + SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null; + try { - messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), - UnidentifiedAccessUtil.getAccessForSync(context)); + messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), messageRecipient); } catch (IOException ioe) { throw new NetworkException(ioe); } @@ -375,7 +395,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy String serialized = data.getString(KEY_ADDRESS); Address address = serialized != null ? Address.fromSerialized(serialized) : null; - return new MultiDeviceContactUpdateJob(parameters, address, data.getBoolean(KEY_FORCE_SYNC)); + String recipientSerialized = data.getString(KEY_RECIPIENT); + Address recipient = recipientSerialized != null ? Address.fromSerialized(recipientSerialized) : null; + + return new MultiDeviceContactUpdateJob(parameters, recipient, address, data.getBoolean(KEY_FORCE_SYNC)); } } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index b7b92a2ced..4107e58dc0 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -648,6 +648,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { private void handleSynchronizeContactMessage(@NonNull ContactsMessage contactsMessage) { if (contactsMessage.getContactsStream().isStream()) { + Log.d("Loki", "Received contact sync message"); try { DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(contactsMessage.getContactsStream().asStream().getInputStream()); DeviceContact deviceContact = contactsInputStream.read(); @@ -665,9 +666,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType { LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId); if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) { MessageSender.sendBackgroundFriendRequest(context, deviceContact.getNumber(), "This is an automated friend request. Still under testing!"); + Log.d("Loki", "Sent friend request to " + deviceContact.getNumber()); } else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { // Accept the incoming friend request becomeFriendsWithContact(deviceContact.getNumber(), false); + Log.d("Loki", "Became friends with " + deviceContact.getNumber()); } // TODO: Handle blocked - If user is not blocked then we should do the friend request logic otherwise add them to our block list @@ -679,6 +682,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } catch (IOException e) { // Exception is thrown when we don't have any more contacts to read from + return; } catch (Exception e) { Log.d("Loki", "Failed to sync contact: " + e.getMessage()); } @@ -1113,6 +1117,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { setDisplayName(envelope.getSource(), content.senderDisplayName.get()); } + + // Contact sync + if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) { + handleSynchronizeContactMessage(content.getSyncMessage().get().getContacts().get()); + } } private void setDisplayName(String hexEncodedPublicKey, String profileName) { diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt index 13e61721ac..db84dcf440 100644 --- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt @@ -123,10 +123,12 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation) TextSecurePreferences.setMultiDevice(context, true) + val address = Address.fromSerialized(pairingAuthorisation.secondaryDevicePublicKey); + val sendPromise = retryIfNeeded(8) { - sendPairingAuthorisationMessage(context, pairingAuthorisation.secondaryDevicePublicKey, signedPairingAuthorisation) + sendPairingAuthorisationMessage(context, address.serialize(), signedPairingAuthorisation) }.fail { - Log.d("Loki", "Failed to send pairing authorization message to ${pairingAuthorisation.secondaryDevicePublicKey}.") + Log.d("Loki", "Failed to send pairing authorization message to ${address.serialize()}.") } val updatePromise = LokiStorageAPI.shared.updateUserDeviceMappings().fail { @@ -138,7 +140,7 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio Log.d("Loki", "Successfully pairing with a secondary device! Syncing contacts.") // Send out sync contact after a delay Timer().schedule(3000) { - MessageSender.syncAllContacts(context) + MessageSender.syncAllContacts(context, address) } } } diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 1ec057723e..2ee0a3a5a4 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -81,8 +81,8 @@ public class MessageSender { private enum MessageType { TEXT, MEDIA } - public static void syncAllContacts(Context context) { - ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, true)); + public static void syncAllContacts(Context context, Address recipient) { + ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, recipient, true)); } /**