From 85b23795103cbb86df88adc14f31a778b49901df Mon Sep 17 00:00:00 2001 From: Mikunj Date: Wed, 26 Feb 2020 11:53:28 +1100 Subject: [PATCH] Fix race condition that occurs on device link authorisation. Upon authorisation, we upload our mapping to the server. At the same time we also get a contact sync message and send out background friend request messages. There was a race condition between those 2 functions where to correctly establish multi-device communication, you need your mapping on the server so that the other party knows that it's a secondary device and not a regular user. --- .../thoughtcrime/securesms/ApplicationContext.java | 3 +++ .../securesms/jobs/PushDecryptJob.java | 14 +++++++++++++- .../securesms/loki/MultiDeviceUtilities.kt | 6 ++++++ .../loki/redesign/activities/LandingActivity.kt | 1 - 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index f8eed2a13d..1661754ebb 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -194,6 +194,9 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc if (userHexEncodedPublicKey != null) { if (TextSecurePreferences.getNeedsIsRevokedSlaveDeviceCheck(this)) { MultiDeviceUtilities.checkIsRevokedSlaveDevice(this); + } else { + // We always update our current device links onto the server in case we failed to do so upon linking + MultiDeviceUtilities.updateDeviceLinksOnServer(this); } } } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 6d7595b9b8..d85fe7b720 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -138,6 +138,7 @@ import org.whispersystems.signalservice.loki.api.DeviceLink; import org.whispersystems.signalservice.loki.api.DeviceLinkingSession; import org.whispersystems.signalservice.loki.api.LokiAPI; import org.whispersystems.signalservice.loki.api.LokiDeviceLinkUtilities; +import org.whispersystems.signalservice.loki.api.LokiFileServerAPI; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; @@ -704,6 +705,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); LokiThreadFriendRequestStatus status = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID); if (status == LokiThreadFriendRequestStatus.NONE || status == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) { + // TODO: We should ensure that our mapping has been uploaded to the server before sending out this message MessageSender.sendBackgroundFriendRequest(context, hexEncodedPublicKey, "Please accept to enable messages to be synced across devices"); Log.d("Loki", "Sent friend request to " + hexEncodedPublicKey); } else if (status == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { @@ -1206,6 +1208,17 @@ public class PushDecryptJob extends BaseJob implements InjectableType { TextSecurePreferences.setMultiDevice(context, true); // Send a background message to the master device MessageSender.sendBackgroundMessage(context, deviceLink.getMasterHexEncodedPublicKey()); + /* + Update device link on the file server. + We put this here because after receiving the authorisation message, we will also receive all sync messages. + If these sync messages are contact syncs then we need to send them friend requests so that we can establish multi-device communication. + If our device mapping is not stored on the server before the other party receives our message, they will think that they got a friend request from a non-multi-device user. + */ + try { + PromiseUtil.timeout(LokiFileServerAPI.shared.addDeviceLink(deviceLink), 8000).get(); + } catch (Exception e) { + Log.w("Loki", "Failed to upload device links to the file server! " + e); + } // Update display name if needed if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { TextSecurePreferences.setProfileName(context, content.senderDisplayName.get()); @@ -1218,7 +1231,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (content.getSyncMessage().isPresent() && content.getSyncMessage().get().getContacts().isPresent()) { handleContactSyncMessage(content.getSyncMessage().get().getContacts().get()); } - // The device link is propagated to the file server in LandingActivity.onDeviceLinkAuthorized because we can handle the error there } 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 6afb77f165..8cbf546901 100644 --- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt @@ -42,6 +42,12 @@ fun checkIsRevokedSlaveDevice(context: Context) { } } +fun updateDeviceLinksOnServer(context: Context) { + val hexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context) + val deviceLinks = DatabaseFactory.getLokiAPIDatabase(context).getDeviceLinks(hexEncodedPublicKey) + LokiFileServerAPI.shared.setDeviceLinks(deviceLinks) +} + fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise, Exception> { val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) return LokiDeviceLinkUtilities.getAllLinkedDeviceHexEncodedPublicKeys(hexEncodedPublicKey).map { keys -> diff --git a/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt b/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt index f8031a22b7..130646d4fa 100644 --- a/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/redesign/activities/LandingActivity.kt @@ -114,7 +114,6 @@ class LandingActivity : BaseActionBarActivity(), LinkDeviceSlaveModeDialogDelega } override fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { - LokiFileServerAPI.shared.addDeviceLink(deviceLink) TextSecurePreferences.setMasterHexEncodedPublicKey(this, deviceLink.masterHexEncodedPublicKey) val intent = Intent(this, HomeActivity::class.java) show(intent)