Add a job for sending a background message.

Send a friend request background message to other linked devices upon accepting a friend request.
This commit is contained in:
Mikunj 2019-11-08 10:20:11 +11:00
parent 9541588383
commit 55ecd3cae4
6 changed files with 121 additions and 28 deletions

View File

@ -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());
}};
}

View File

@ -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

View File

@ -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<Map<String, LokiThreadFriendRequestStatus>, Exception> {
val lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context)
@ -128,9 +131,12 @@ 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.")
// Send out sync contact after a delay
Timer().schedule(3000) {
MessageSender.syncAllContacts(context)
}
}
}
fun isOneOfOurDevices(context: Context, address: Address): Promise<Boolean, Exception> {
if (address.isGroup || address.isEmail || address.isMmsGroup) {

View File

@ -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<UnidentifiedAccessPair>(), 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<PushBackgroundMessageSendJob> {
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)
}
}
}
}

View File

@ -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)

View File

@ -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,