diff --git a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java index 7309716f9c..cfa9aa7beb 100644 --- a/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java +++ b/src/org/thoughtcrime/securesms/jobs/JobManagerFactories.java @@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint; import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver; +import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import java.util.Arrays; @@ -72,6 +73,7 @@ public final class JobManagerFactories { put(TypingSendJob.KEY, new TypingSendJob.Factory()); put(UpdateApkJob.KEY, new UpdateApkJob.Factory()); put(PushMessageSyncSendJob.KEY, new PushMessageSyncSendJob.Factory()); + put(PushBackgroundMessageSendJob.KEY, new PushBackgroundMessageSendJob.Factory()); }}; } diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index df0d474136..c1a43646c1 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -652,9 +652,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType { DeviceContactsInputStream contactsInputStream = new DeviceContactsInputStream(contactsMessage.getContactsStream().asStream().getInputStream()); DeviceContact deviceContact = contactsInputStream.read(); while (deviceContact != null) { - // Check if we have the contact as a friend + // Check if we have the contact as a friend and that we're not trying to sync our own device Address address = Address.fromSerialized(deviceContact.getNumber()); - if (!address.isPhone()) { continue; } + if (!address.isPhone() || address.toPhoneString().equalsIgnoreCase(TextSecurePreferences.getLocalNumber(context))) { continue; } /* If we're not friends with the contact we received or our friend request expired then we should send them a friend request diff --git a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt index 5b471a42bb..fffbeecd80 100644 --- a/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt +++ b/src/org/thoughtcrime/securesms/loki/MultiDeviceUtilities.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki import android.content.Context +import android.os.Handler import nl.komponents.kovenant.* import nl.komponents.kovenant.functional.bind import nl.komponents.kovenant.functional.map @@ -22,6 +23,8 @@ import org.whispersystems.signalservice.loki.api.PairingAuthorisation import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus import org.whispersystems.signalservice.loki.utilities.recover import org.whispersystems.signalservice.loki.utilities.retryIfNeeded +import java.util.* +import kotlin.concurrent.schedule fun getAllDeviceFriendRequestStatuses(context: Context, hexEncodedPublicKey: String): Promise, Exception> { val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context) @@ -128,7 +131,10 @@ fun signAndSendPairingAuthorisationMessage(context: Context, pairingAuthorisatio // If both promises complete successfully then we should sync our contacts all(listOf(sendPromise, updatePromise), cancelOthersOnError = false).success { Log.d("Loki", "Successfully pairing with a secondary device! Syncing contacts.") - MessageSender.syncAllContacts(context) + // Send out sync contact after a delay + Timer().schedule(3000) { + MessageSender.syncAllContacts(context) + } } } diff --git a/src/org/thoughtcrime/securesms/loki/PushBackgroundMessageSendJob.kt b/src/org/thoughtcrime/securesms/loki/PushBackgroundMessageSendJob.kt new file mode 100644 index 0000000000..cca9031003 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/PushBackgroundMessageSendJob.kt @@ -0,0 +1,98 @@ +package org.thoughtcrime.securesms.loki + +import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.dependencies.InjectableType +import org.thoughtcrime.securesms.jobmanager.Data +import org.thoughtcrime.securesms.jobmanager.Job +import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint +import org.thoughtcrime.securesms.jobs.BaseJob +import org.thoughtcrime.securesms.logging.Log +import org.whispersystems.libsignal.util.guava.Optional +import org.whispersystems.signalservice.api.SignalServiceMessageSender +import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair +import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException +import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage +import org.whispersystems.signalservice.api.push.SignalServiceAddress +import java.io.IOException +import java.util.concurrent.TimeUnit +import javax.inject.Inject + +class PushBackgroundMessageSendJob private constructor( + parameters: Parameters, + private val recipient: String, + private val messageBody: String?, + private val friendRequest: Boolean +) : BaseJob(parameters) { + companion object { + const val KEY = "PushBackgroundMessageSendJob" + + private val TAG = PushBackgroundMessageSendJob::class.java.simpleName + + private val KEY_RECIPIENT = "recipient" + private val KEY_MESSAGE_BODY = "message_body" + private val KEY_FRIEND_REQUEST = "asFriendRequest" + } + + constructor(recipient: String): this(recipient, null, false) + constructor(recipient: String, messageBody: String?, friendRequest: Boolean) : this(Parameters.Builder() + .addConstraint(NetworkConstraint.KEY) + .setQueue(KEY) + .setLifespan(TimeUnit.DAYS.toMillis(1)) + .setMaxAttempts(1) + .build(), + recipient, messageBody, friendRequest) + + override fun serialize(): Data { + return Data.Builder() + .putString(KEY_RECIPIENT, recipient) + .putString(KEY_MESSAGE_BODY, messageBody) + .putBoolean(KEY_FRIEND_REQUEST, friendRequest) + .build() + } + + override fun getFactoryKey(): String { + return KEY + } + + public override fun onRun() { + val message = SignalServiceDataMessage.newBuilder() + .withTimestamp(System.currentTimeMillis()) + .withBody(messageBody) + + if (friendRequest) { + val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient) + message.withPreKeyBundle(bundle) + .asFriendRequest(true) + } + + val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() + val address = SignalServiceAddress(recipient) + try { + messageSender.sendMessage(-1, address, Optional.absent(), message.build()) // The message ID doesn't matter + } catch (e: Exception) { + Log.d("Loki", "Failed to send background message to: $recipient.") + throw e + } + } + + public override fun onShouldRetry(e: Exception): Boolean { + // Loki - Disable since we have our own retrying when sending messages + return false + } + + override fun onCanceled() {} + + class Factory : Job.Factory { + override fun create(parameters: Parameters, data: Data): PushBackgroundMessageSendJob { + try { + val recipient = data.getString(KEY_RECIPIENT) + val messageBody = if (data.hasString(KEY_MESSAGE_BODY)) data.getString(KEY_MESSAGE_BODY) else null + val friendRequest = data.getBooleanOrDefault(KEY_FRIEND_REQUEST, false) + return PushBackgroundMessageSendJob(parameters, recipient, messageBody, friendRequest) + } catch (e: IOException) { + throw AssertionError(e) + } + } + } +} diff --git a/src/org/thoughtcrime/securesms/loki/PushMessageSyncSendJob.kt b/src/org/thoughtcrime/securesms/loki/PushMessageSyncSendJob.kt index f5848cbb07..6d451c2bc4 100644 --- a/src/org/thoughtcrime/securesms/loki/PushMessageSyncSendJob.kt +++ b/src/org/thoughtcrime/securesms/loki/PushMessageSyncSendJob.kt @@ -41,7 +41,7 @@ class PushMessageSyncSendJob private constructor( .addConstraint(NetworkConstraint.KEY) .setQueue(KEY) .setLifespan(TimeUnit.DAYS.toMillis(1)) - .setMaxAttempts(3) + .setMaxAttempts(1) .build(), messageID, recipient, timestamp, message, ttl) diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 64653eb88d..1ec057723e 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -17,6 +17,7 @@ package org.thoughtcrime.securesms.sms; import android.content.Context; +import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -47,6 +48,7 @@ import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.GeneralUtilitiesKt; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; +import org.thoughtcrime.securesms.loki.PushBackgroundMessageSendJob; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingMediaMessage; @@ -110,9 +112,10 @@ public class MessageSender { long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient); if (threadID < 0) { continue; } LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID); - // TODO: Do we want to send a bg message regardless of FR status? OR do we want to send a custom FR to those we are not friends with if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED) { sendBackgroundMessage(context, device); + } else if (friendRequestStatus == LokiThreadFriendRequestStatus.NONE || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) { + sendBackgroundFriendRequest(context, device, "This is a friend request from android! please replace this message in the future"); } } }); @@ -120,34 +123,18 @@ public class MessageSender { }); } + // region Background message + + // We don't call the message sender here directly and instead we just opt to create a specific job for the send + // This is because calling message sender directly would cause the application to freeze in some cases as it was blocking the thread when waiting for a response from the send public static void sendBackgroundMessage(Context context, String contactHexEncodedPublicKey) { - SignalServiceDataMessage message = new SignalServiceDataMessage(System.currentTimeMillis(), null); - sendMessage(context, contactHexEncodedPublicKey, message); + ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey)); } public static void sendBackgroundFriendRequest(Context context, String contactHexEncodedPublicKey, String messageBody) { - PreKeyBundle bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(contactHexEncodedPublicKey); - SignalServiceDataMessage message = SignalServiceDataMessage.newBuilder() - .withTimestamp(System.currentTimeMillis()) - .withBody(messageBody) - .asFriendRequest(true) - .withPreKeyBundle(bundle) - .build(); - sendMessage(context, contactHexEncodedPublicKey, message); - } - - private static void sendMessage(Context context, String contactHexEncodedPublicKey, SignalServiceDataMessage message) { - Util.runOnMain(() -> { - SignalServiceMessageSender messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender(); - SignalServiceAddress address = new SignalServiceAddress(contactHexEncodedPublicKey); - try { - // Try send to the original person - messageSender.sendMessage(-1, address, Optional.absent(), message); // The message ID doesn't matter - } catch (Exception e) { - Log.d("Loki", "Failed to send background message to: " + contactHexEncodedPublicKey + "."); - } - }); + ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(contactHexEncodedPublicKey, messageBody, true)); } + // endregion public static long send(final Context context, final OutgoingTextMessage message,