Sync all contacts with only a specific device.

This may fix the issue where the sync message sending queue gets blocked because of sending full contact syncs to all devices.
This commit is contained in:
Mikunj Varsani 2019-11-11 12:47:43 +11:00
parent aea686c856
commit 12639b491f
4 changed files with 50 additions and 16 deletions

View File

@ -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.DeviceContactsOutputStream;
import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage; import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; 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.push.exceptions.PushNetworkException;
import org.whispersystems.signalservice.api.util.InvalidNumberException; import org.whispersystems.signalservice.api.util.InvalidNumberException;
import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; 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 long FULL_SYNC_TIME = TimeUnit.HOURS.toMillis(6);
private static final String KEY_ADDRESS = "address"; private static final String KEY_ADDRESS = "address";
private static final String KEY_RECIPIENT = "recipient";
private static final String KEY_FORCE_SYNC = "force_sync"; private static final String KEY_FORCE_SYNC = "force_sync";
@Inject SignalServiceMessageSender messageSender; @Inject SignalServiceMessageSender messageSender;
private @Nullable String address; 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; private boolean forceSync;
/**
* Create a full contact sync job which syncs across to all other devices
*/
public MultiDeviceContactUpdateJob(@NonNull Context context) { public MultiDeviceContactUpdateJob(@NonNull Context context) {
this(context, false); 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) { 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() this(new Job.Parameters.Builder()
.addConstraint(NetworkConstraint.KEY) .addConstraint(NetworkConstraint.KEY)
.setQueue("MultiDeviceContactUpdateJob") .setQueue("MultiDeviceContactUpdateJob")
.setLifespan(TimeUnit.DAYS.toMillis(1)) .setLifespan(TimeUnit.DAYS.toMillis(1))
.setMaxAttempts(3) .setMaxAttempts(1)
.build(), .build(),
recipient,
address, address,
forceSync); 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); super(parameters);
this.forceSync = forceSync; this.forceSync = forceSync;
this.recipient = (recipient != null) ? recipient.serialize() : null;
if (address != null) this.address = address.serialize(); if (address != null) this.address = address.serialize();
else this.address = null; else this.address = null;
@ -106,6 +123,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
public @NonNull Data serialize() { public @NonNull Data serialize() {
return new Data.Builder().putString(KEY_ADDRESS, address) return new Data.Builder().putString(KEY_ADDRESS, address)
.putBoolean(KEY_FORCE_SYNC, forceSync) .putBoolean(KEY_FORCE_SYNC, forceSync)
.putString(KEY_RECIPIENT, recipient)
.build(); .build();
} }
@ -124,7 +142,7 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
} }
if (address == null) generateFullContactUpdate(); 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) private void generateSingleContactUpdate(@NonNull Address address)
@ -245,7 +263,8 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
@Override @Override
public boolean onShouldRetry(@NonNull Exception exception) { 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; return false;
} }
@ -265,9 +284,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
.withLength(contactsFile.length()) .withLength(contactsFile.length())
.build(); .build();
SignalServiceAddress messageRecipient = recipient != null ? new SignalServiceAddress(recipient) : null;
try { try {
messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), messageSender.sendMessage(0, SignalServiceSyncMessage.forContacts(new ContactsMessage(attachmentStream, complete)), messageRecipient);
UnidentifiedAccessUtil.getAccessForSync(context));
} catch (IOException ioe) { } catch (IOException ioe) {
throw new NetworkException(ioe); throw new NetworkException(ioe);
} }
@ -375,7 +395,10 @@ public class MultiDeviceContactUpdateJob extends BaseJob implements InjectableTy
String serialized = data.getString(KEY_ADDRESS); String serialized = data.getString(KEY_ADDRESS);
Address address = serialized != null ? Address.fromSerialized(serialized) : null; 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));
} }
} }
} }

View File

@ -648,6 +648,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
private void handleSynchronizeContactMessage(@NonNull ContactsMessage contactsMessage) { private void handleSynchronizeContactMessage(@NonNull ContactsMessage contactsMessage) {
if (contactsMessage.getContactsStream().isStream()) { if (contactsMessage.getContactsStream().isStream()) {
Log.d("Loki", "Received contact sync message");
try { try {
DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(contactsMessage.getContactsStream().asStream().getInputStream()); DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(contactsMessage.getContactsStream().asStream().getInputStream());
DeviceContact deviceContact = contactsInputStream.read(); DeviceContact deviceContact = contactsInputStream.read();
@ -665,9 +666,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId); LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadId);
if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) { if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
MessageSender.sendBackgroundFriendRequest(context, deviceContact.getNumber(), "This is an automated friend request. Still under testing!"); 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) { } else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) {
// Accept the incoming friend request // Accept the incoming friend request
becomeFriendsWithContact(deviceContact.getNumber(), false); 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 // 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) { } catch (IOException e) {
// Exception is thrown when we don't have any more contacts to read from // Exception is thrown when we don't have any more contacts to read from
return;
} catch (Exception e) { } catch (Exception e) {
Log.d("Loki", "Failed to sync contact: " + e.getMessage()); 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) { if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) {
setDisplayName(envelope.getSource(), content.senderDisplayName.get()); 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) { private void setDisplayName(String hexEncodedPublicKey, String profileName) {

View File

@ -123,10 +123,12 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio
DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation) DatabaseFactory.getLokiAPIDatabase(context).insertOrUpdatePairingAuthorisation(signedPairingAuthorisation)
TextSecurePreferences.setMultiDevice(context, true) TextSecurePreferences.setMultiDevice(context, true)
val address = Address.fromSerialized(pairingAuthorisation.secondaryDevicePublicKey);
val sendPromise = retryIfNeeded(8) { val sendPromise = retryIfNeeded(8) {
sendPairingAuthorisationMessage(context, pairingAuthorisation.secondaryDevicePublicKey, signedPairingAuthorisation) sendPairingAuthorisationMessage(context, address.serialize(), signedPairingAuthorisation)
}.fail { }.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 { 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.") Log.d("Loki", "Successfully pairing with a secondary device! Syncing contacts.")
// Send out sync contact after a delay // Send out sync contact after a delay
Timer().schedule(3000) { Timer().schedule(3000) {
MessageSender.syncAllContacts(context) MessageSender.syncAllContacts(context, address)
} }
} }
} }

View File

@ -81,8 +81,8 @@ public class MessageSender {
private enum MessageType { TEXT, MEDIA } private enum MessageType { TEXT, MEDIA }
public static void syncAllContacts(Context context) { public static void syncAllContacts(Context context, Address recipient) {
ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, true)); ApplicationContext.getInstance(context).getJobManager().add(new MultiDeviceContactUpdateJob(context, recipient, true));
} }
/** /**