mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-30 13:35:18 +00:00
Handle session restoration.
This commit is contained in:
parent
fd2dc678ea
commit
0caeb3a109
@ -3086,8 +3086,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
@Override
|
@Override
|
||||||
public void updateItemButtonPressed(@NonNull MessageRecord messageRecord) {
|
public void updateItemButtonPressed(@NonNull MessageRecord messageRecord) {
|
||||||
// Loki - User clicked restore session
|
// Loki - User clicked restore session
|
||||||
if (messageRecord.isNoRemoteSession() && !messageRecord.isLokiSessionRestoreSent()) {
|
Recipient recipient = messageRecord.getRecipient();
|
||||||
// TODO: Send a message with `SESSION_RESTORE` flag
|
if (!recipient.isGroupRecipient() && messageRecord.isNoRemoteSession() && !messageRecord.isLokiSessionRestoreSent()) {
|
||||||
|
MessageSender.sendRestoreSessionMessage(this, recipient.getAddress().serialize());
|
||||||
DatabaseFactory.getSmsDatabase(this).markAsLokiSessionRestoreSent(messageRecord.id);
|
DatabaseFactory.getSmsDatabase(this).markAsLokiSessionRestoreSent(messageRecord.id);
|
||||||
TextSecurePreferences.setShowingSessionRestorePrompt(this, messageRecord.getIndividualRecipient().getAddress().serialize(), false);
|
TextSecurePreferences.setShowingSessionRestorePrompt(this, messageRecord.getIndividualRecipient().getAddress().serialize(), false);
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ import org.thoughtcrime.securesms.loki.LokiPreKeyBundleDatabase;
|
|||||||
import org.thoughtcrime.securesms.loki.LokiPreKeyRecordDatabase;
|
import org.thoughtcrime.securesms.loki.LokiPreKeyRecordDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
|
import org.thoughtcrime.securesms.loki.LokiThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
import org.thoughtcrime.securesms.loki.MultiDeviceUtilities;
|
||||||
|
import org.thoughtcrime.securesms.loki.DebouncerCache;
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.mms.MmsException;
|
import org.thoughtcrime.securesms.mms.MmsException;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage;
|
||||||
@ -94,6 +95,7 @@ import org.thoughtcrime.securesms.sms.OutgoingEncryptedMessage;
|
|||||||
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
|
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage;
|
||||||
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage;
|
||||||
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
import org.thoughtcrime.securesms.stickers.StickerLocator;
|
||||||
|
import org.thoughtcrime.securesms.util.Debouncer;
|
||||||
import org.thoughtcrime.securesms.util.GroupUtil;
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
import org.thoughtcrime.securesms.util.IdentityUtil;
|
import org.thoughtcrime.securesms.util.IdentityUtil;
|
||||||
@ -337,6 +339,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
MultiDeviceUtilities.checkForRevocation(context);
|
MultiDeviceUtilities.checkForRevocation(context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Loki - We shouldn't process session restore message any further
|
||||||
|
if (message.isSessionRestore()) { return; }
|
||||||
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
if (message.isEndSession()) handleEndSessionMessage(content, smsMessageId);
|
||||||
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
else if (message.isGroupUpdate()) handleGroupMessage(content, message, smsMessageId);
|
||||||
else if (message.isExpirationUpdate())
|
else if (message.isExpirationUpdate())
|
||||||
@ -1187,7 +1191,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) {
|
||||||
if (content.lokiServiceMessage.isPresent()) {
|
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
||||||
|
if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) {
|
||||||
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
|
||||||
if (lokiMessage.getPreKeyBundleMessage() != null) {
|
if (lokiMessage.getPreKeyBundleMessage() != null) {
|
||||||
int registrationID = TextSecurePreferences.getLocalRegistrationId(context);
|
int registrationID = TextSecurePreferences.getLocalRegistrationId(context);
|
||||||
@ -1203,10 +1208,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
|
|
||||||
// If we got a friend request and we were friends with this user then we need to reset our session
|
// If we got a friend request and we were friends with this user then we need to reset our session
|
||||||
if (envelope.isFriendRequest()) {
|
if (envelope.isFriendRequest()) {
|
||||||
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
|
|
||||||
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
|
||||||
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
|
||||||
resetSession(content.getSender(), threadID);
|
resetSession(content.getSender(), threadID);
|
||||||
|
// Let our other devices know that we have reset session
|
||||||
|
MessageSender.syncContact(context, sender.getAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1398,23 +1404,43 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
|
|||||||
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
private void handleNoSessionMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||||
@NonNull Optional<Long> smsMessageId)
|
@NonNull Optional<Long> smsMessageId)
|
||||||
{
|
{
|
||||||
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
Recipient recipient = Recipient.from(context, Address.fromSerialized(sender), false);
|
||||||
|
if (recipient.isGroupRecipient()) { return; }
|
||||||
|
|
||||||
if (!smsMessageId.isPresent()) {
|
long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(recipient);
|
||||||
if (!TextSecurePreferences.isShowingSessionRestorePrompt(context, sender)) {
|
LokiThreadFriendRequestStatus friendRequestStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID);
|
||||||
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
/*
|
||||||
|
If we are friends with the user or we sent a friend request to them and we got a message back with no session then we want to try and restore the session automatically.
|
||||||
|
otherwise if we're not friends or our friend request expired then we need to prompt the user for action
|
||||||
|
*/
|
||||||
|
if (friendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_SENT) {
|
||||||
|
autoRestoreSession(sender);
|
||||||
|
} else if (friendRequestStatus == LokiThreadFriendRequestStatus.NONE || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_EXPIRED) {
|
||||||
|
SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context);
|
||||||
|
|
||||||
if (insertResult.isPresent()) {
|
if (!smsMessageId.isPresent()) {
|
||||||
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
|
if (!TextSecurePreferences.isShowingSessionRestorePrompt(context, sender)) {
|
||||||
TextSecurePreferences.setShowingSessionRestorePrompt(context, sender, true);
|
Optional<InsertResult> insertResult = insertPlaceholder(sender, senderDevice, timestamp);
|
||||||
//MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
|
||||||
|
if (insertResult.isPresent()) {
|
||||||
|
smsDatabase.markAsNoSession(insertResult.get().getMessageId());
|
||||||
|
TextSecurePreferences.setShowingSessionRestorePrompt(context, sender, true);
|
||||||
|
//MessageNotifier.updateNotification(context, insertResult.get().getThreadId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
smsDatabase.markAsNoSession(smsMessageId.get());
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
smsDatabase.markAsNoSession(smsMessageId.get());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void autoRestoreSession(@NonNull String sender) {
|
||||||
|
// We don't want to keep spamming the user for an auto restore
|
||||||
|
String key = "restore_session_" + sender;
|
||||||
|
Debouncer debouncer = DebouncerCache.getDebouncer(key, 10000);
|
||||||
|
debouncer.publish(() -> MessageSender.sendRestoreSessionMessage(context, sender));
|
||||||
|
}
|
||||||
|
|
||||||
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
|
private void handleLegacyMessage(@NonNull String sender, int senderDevice, long timestamp,
|
||||||
@NonNull Optional<Long> smsMessageId)
|
@NonNull Optional<Long> smsMessageId)
|
||||||
{
|
{
|
||||||
|
18
src/org/thoughtcrime/securesms/loki/DebouncerCache.kt
Normal file
18
src/org/thoughtcrime/securesms/loki/DebouncerCache.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.Debouncer
|
||||||
|
|
||||||
|
object DebouncerCache {
|
||||||
|
private val cache: HashMap<String, Debouncer> = hashMapOf()
|
||||||
|
@JvmStatic
|
||||||
|
fun getDebouncer(key: String, threshold: Long): Debouncer {
|
||||||
|
val throttler = cache[key] ?: Debouncer(threshold)
|
||||||
|
cache[key] = throttler
|
||||||
|
return throttler
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun remove(key: String) {
|
||||||
|
cache.remove(key)
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,15 @@ object FriendRequestHandler {
|
|||||||
ActionType.Sent -> LokiThreadFriendRequestStatus.REQUEST_SENT
|
ActionType.Sent -> LokiThreadFriendRequestStatus.REQUEST_SENT
|
||||||
}
|
}
|
||||||
DatabaseFactory.getLokiThreadDatabase(context).setFriendRequestStatus(threadId, threadFriendStatus)
|
DatabaseFactory.getLokiThreadDatabase(context).setFriendRequestStatus(threadId, threadFriendStatus)
|
||||||
|
// If we sent a friend request then we need to hide the session restore prompt
|
||||||
|
if (type == ActionType.Sent) {
|
||||||
|
val smsDatabase = DatabaseFactory.getSmsDatabase(context)
|
||||||
|
smsDatabase.getMessages(threadId)
|
||||||
|
.filter { it.isNoRemoteSession && !it.isLokiSessionRestoreSent }
|
||||||
|
.forEach {
|
||||||
|
smsDatabase.markAsLokiSessionRestoreSent(it.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update message status
|
// Update message status
|
||||||
|
@ -13,30 +13,43 @@ import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
|
|||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
import org.whispersystems.signalservice.internal.util.JsonUtil
|
import org.whispersystems.signalservice.internal.util.JsonUtil
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
import java.lang.IllegalStateException
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
data class BackgroundMessage private constructor(val recipient: String, val body: String?, val friendRequest: Boolean, val unpairingRequest: Boolean) {
|
data class BackgroundMessage private constructor(val data: Map<String, Any>) {
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun create(recipient: String) = BackgroundMessage(recipient, null, false, false)
|
fun create(recipient: String) = BackgroundMessage(mapOf("recipient" to recipient))
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createFriendRequest(recipient: String, messageBody: String) = BackgroundMessage(recipient, messageBody, true, false)
|
fun createFriendRequest(recipient: String, messageBody: String) = BackgroundMessage(mapOf(
|
||||||
|
"recipient" to recipient,
|
||||||
|
"body" to messageBody,
|
||||||
|
"friendRequest" to true
|
||||||
|
))
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun createUnpairingRequest(recipient: String) = BackgroundMessage(recipient, null, false, true)
|
fun createUnpairingRequest(recipient: String) = BackgroundMessage(mapOf(
|
||||||
|
"recipient" to recipient,
|
||||||
|
"unpairingRequest" to true
|
||||||
|
))
|
||||||
|
@JvmStatic
|
||||||
|
fun createSessionRestore(recipient: String) = BackgroundMessage(mapOf(
|
||||||
|
"recipient" to recipient,
|
||||||
|
"friendRequest" to true,
|
||||||
|
"sessionRestore" to true
|
||||||
|
))
|
||||||
|
|
||||||
internal fun parse(serialized: String): BackgroundMessage {
|
internal fun parse(serialized: String): BackgroundMessage {
|
||||||
val node = JsonUtil.fromJson(serialized)
|
val data = JsonUtil.fromJson(serialized, Map::class.java) as? Map<String, Any> ?: throw AssertionError("JSON parsing failed")
|
||||||
val recipient = node.get("recipient").asText()
|
return BackgroundMessage(data)
|
||||||
val body = if (node.hasNonNull("body")) node.get("body").asText() else null
|
|
||||||
val friendRequest = node.get("friendRequest").asBoolean(false)
|
|
||||||
val unpairingRequest = node.get("unpairingRequest").asBoolean(false)
|
|
||||||
return BackgroundMessage(recipient, body, friendRequest, unpairingRequest)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T> get(key: String, defaultValue: T): T {
|
||||||
|
return data[key] as? T ?: defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
fun serialize(): String {
|
fun serialize(): String {
|
||||||
val map = mapOf("recipient" to recipient, "body" to body, "friendRequest" to friendRequest, "unpairingRequest" to unpairingRequest)
|
return JsonUtil.toJson(data)
|
||||||
return JsonUtil.toJson(map)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,24 +84,31 @@ class PushBackgroundMessageSendJob private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
public override fun onRun() {
|
public override fun onRun() {
|
||||||
|
val recipient = message.get<String?>("recipient", null) ?: throw IllegalStateException()
|
||||||
val dataMessage = SignalServiceDataMessage.newBuilder()
|
val dataMessage = SignalServiceDataMessage.newBuilder()
|
||||||
.withTimestamp(System.currentTimeMillis())
|
.withTimestamp(System.currentTimeMillis())
|
||||||
.withBody(message.body)
|
.withBody(message.get<String?>("body", null))
|
||||||
|
|
||||||
if (message.friendRequest) {
|
if (message.get("friendRequest", false)) {
|
||||||
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(message.recipient)
|
val bundle = DatabaseFactory.getLokiPreKeyBundleDatabase(context).generatePreKeyBundle(recipient)
|
||||||
dataMessage.withPreKeyBundle(bundle)
|
dataMessage.withPreKeyBundle(bundle)
|
||||||
.asFriendRequest(true)
|
.asFriendRequest(true)
|
||||||
} else if (message.unpairingRequest) {
|
}
|
||||||
|
|
||||||
|
if (message.get("unpairingRequest", false)) {
|
||||||
dataMessage.asUnpairingRequest(true)
|
dataMessage.asUnpairingRequest(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message.get("sessionRestore", false)) {
|
||||||
|
dataMessage.asSessionRestore(true)
|
||||||
|
}
|
||||||
|
|
||||||
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
val address = SignalServiceAddress(message.recipient)
|
val address = SignalServiceAddress(recipient)
|
||||||
try {
|
try {
|
||||||
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter
|
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.d("Loki", "Failed to send background message to: ${message.recipient}.")
|
Log.d("Loki", "Failed to send background message to: ${recipient}.")
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -139,6 +139,10 @@ public class MessageSender {
|
|||||||
public static void sendUnpairRequest(Context context, String contactHexEncodedPublicKey) {
|
public static void sendUnpairRequest(Context context, String contactHexEncodedPublicKey) {
|
||||||
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createUnpairingRequest(contactHexEncodedPublicKey)));
|
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createUnpairingRequest(contactHexEncodedPublicKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void sendRestoreSessionMessage(Context context, String contactHexEncodedPublicKey) {
|
||||||
|
ApplicationContext.getInstance(context).getJobManager().add(new PushBackgroundMessageSendJob(BackgroundMessage.createSessionRestore(contactHexEncodedPublicKey)));
|
||||||
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
public static long send(final Context context,
|
public static long send(final Context context,
|
||||||
|
Loading…
Reference in New Issue
Block a user