mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 07:18:25 +00:00
Implement ClosedGroupUpdateMessageSendJob
This commit is contained in:
parent
15f3942838
commit
6be509c657
@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.jobs.SmsSentJob;
|
|||||||
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
import org.thoughtcrime.securesms.jobs.TrimThreadJob;
|
||||||
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
import org.thoughtcrime.securesms.jobs.TypingSendJob;
|
||||||
import org.thoughtcrime.securesms.jobs.UpdateApkJob;
|
import org.thoughtcrime.securesms.jobs.UpdateApkJob;
|
||||||
|
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -76,6 +77,7 @@ public class WorkManagerFactoryMappings {
|
|||||||
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
|
put(PushNotificationReceiveJob.class.getName(), PushNotificationReceiveJob.KEY);
|
||||||
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
|
put(PushTextSendJob.class.getName(), PushTextSendJob.KEY);
|
||||||
put(NullMessageSendJob.class.getName(), NullMessageSendJob.KEY);
|
put(NullMessageSendJob.class.getName(), NullMessageSendJob.KEY);
|
||||||
|
put(ClosedGroupUpdateMessageSendJob.class.getName(), ClosedGroupUpdateMessageSendJob.KEY);
|
||||||
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
|
put(RefreshAttributesJob.class.getName(), RefreshAttributesJob.KEY);
|
||||||
put(RefreshPreKeysJob.class.getName(), RefreshPreKeysJob.KEY);
|
put(RefreshPreKeysJob.class.getName(), RefreshPreKeysJob.KEY);
|
||||||
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
put(RefreshUnidentifiedDeliveryAbilityJob.class.getName(), RefreshUnidentifiedDeliveryAbilityJob.KEY);
|
||||||
|
@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver;
|
|||||||
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.NetworkOrCellServiceConstraint;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraint;
|
||||||
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
import org.thoughtcrime.securesms.jobmanager.impl.SqlCipherMigrationConstraintObserver;
|
||||||
|
import org.thoughtcrime.securesms.loki.protocol.ClosedGroupUpdateMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
import org.thoughtcrime.securesms.loki.protocol.shelved.MultiDeviceOpenGroupUpdateJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.NullMessageSendJob;
|
||||||
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
import org.thoughtcrime.securesms.loki.protocol.SessionRequestMessageSendJob;
|
||||||
@ -51,7 +52,8 @@ public final class JobManagerFactories {
|
|||||||
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
put(PushMediaSendJob.KEY, new PushMediaSendJob.Factory());
|
||||||
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
put(PushNotificationReceiveJob.KEY, new PushNotificationReceiveJob.Factory());
|
||||||
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
|
put(PushTextSendJob.KEY, new PushTextSendJob.Factory());
|
||||||
put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory());
|
put(NullMessageSendJob.KEY, new NullMessageSendJob.Factory());
|
||||||
|
put(ClosedGroupUpdateMessageSendJob.KEY, new ClosedGroupUpdateMessageSendJob.Factory());
|
||||||
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
put(RefreshAttributesJob.KEY, new RefreshAttributesJob.Factory());
|
||||||
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
put(RefreshPreKeysJob.KEY, new RefreshPreKeysJob.Factory());
|
||||||
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
put(RefreshUnidentifiedDeliveryAbilityJob.KEY, new RefreshUnidentifiedDeliveryAbilityJob.Factory());
|
||||||
|
@ -22,7 +22,8 @@ class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) :
|
|||||||
private val keyIndex = "key_index"
|
private val keyIndex = "key_index"
|
||||||
private val messageKeys = "message_keys"
|
private val messageKeys = "message_keys"
|
||||||
@JvmStatic val createClosedGroupRatchetsTableCommand
|
@JvmStatic val createClosedGroupRatchetsTableCommand
|
||||||
= "CREATE TABLE $closedGroupRatchetsTable (PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey), $chainKey STRING, $keyIndex INTEGER DEFAULT 0, $messageKeys STRING);"
|
= "CREATE TABLE $closedGroupRatchetsTable ($closedGroupPublicKey STRING, $senderPublicKey STRING, $chainKey STRING, " +
|
||||||
|
"$keyIndex INTEGER DEFAULT 0, $messageKeys STRING, PRIMARY KEY ($closedGroupPublicKey, $senderPublicKey));"
|
||||||
// Private keys
|
// Private keys
|
||||||
private val closedGroupPrivateKeysTable = "closed_group_private_keys"
|
private val closedGroupPrivateKeysTable = "closed_group_private_keys"
|
||||||
private val closedGroupPrivateKey = "closed_group_private_key"
|
private val closedGroupPrivateKey = "closed_group_private_key"
|
||||||
@ -37,7 +38,7 @@ class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) :
|
|||||||
return database.get(closedGroupRatchetsTable, query, arrayOf( groupPublicKey, senderPublicKey )) { cursor ->
|
return database.get(closedGroupRatchetsTable, query, arrayOf( groupPublicKey, senderPublicKey )) { cursor ->
|
||||||
val chainKey = cursor.getString(Companion.chainKey)
|
val chainKey = cursor.getString(Companion.chainKey)
|
||||||
val keyIndex = cursor.getInt(Companion.keyIndex)
|
val keyIndex = cursor.getInt(Companion.keyIndex)
|
||||||
val messageKeys = cursor.getString(Companion.messageKeys).split("-")
|
val messageKeys = cursor.getString(Companion.messageKeys).split(" - ")
|
||||||
ClosedGroupRatchet(chainKey, keyIndex, messageKeys)
|
ClosedGroupRatchet(chainKey, keyIndex, messageKeys)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -49,7 +50,7 @@ class SharedSenderKeysDatabase(context: Context, helper: SQLCipherOpenHelper) :
|
|||||||
values.put(Companion.senderPublicKey, senderPublicKey)
|
values.put(Companion.senderPublicKey, senderPublicKey)
|
||||||
values.put(Companion.chainKey, ratchet.chainKey)
|
values.put(Companion.chainKey, ratchet.chainKey)
|
||||||
values.put(Companion.keyIndex, ratchet.keyIndex)
|
values.put(Companion.keyIndex, ratchet.keyIndex)
|
||||||
values.put(Companion.messageKeys, ratchet.messageKeys.joinToString("-"))
|
values.put(Companion.messageKeys, ratchet.messageKeys.joinToString(" - "))
|
||||||
val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?"
|
val query = "${Companion.closedGroupPublicKey} = ? AND ${Companion.senderPublicKey} = ?"
|
||||||
database.insertOrUpdate(closedGroupRatchetsTable, values, query, arrayOf( groupPublicKey, senderPublicKey ))
|
database.insertOrUpdate(closedGroupRatchetsTable, values, query, arrayOf( groupPublicKey, senderPublicKey ))
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,183 @@
|
|||||||
|
package org.thoughtcrime.securesms.loki.protocol
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString
|
||||||
|
import org.thoughtcrime.securesms.ApplicationContext
|
||||||
|
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
|
||||||
|
import org.thoughtcrime.securesms.database.Address
|
||||||
|
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.thoughtcrime.securesms.loki.utilities.recipient
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.util.Hex
|
||||||
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress
|
||||||
|
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
|
||||||
|
import org.whispersystems.signalservice.loki.protocol.closedgroups.ClosedGroupSenderKey
|
||||||
|
import org.whispersystems.signalservice.loki.protocol.meta.TTLUtilities
|
||||||
|
import org.whispersystems.signalservice.loki.utilities.toHexString
|
||||||
|
import java.io.IOException
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class ClosedGroupUpdateMessageSendJob private constructor(parameters: Parameters, private val destination: String, private val kind: Kind) : BaseJob(parameters) {
|
||||||
|
|
||||||
|
sealed class Kind {
|
||||||
|
class New(val groupPublicKey: ByteArray, val name: String, val groupPrivateKey: ByteArray, val senderKeys: Collection<ClosedGroupSenderKey>, val members: Collection<ByteArray>, val admins: Collection<ByteArray>) : Kind()
|
||||||
|
class Info(val groupPublicKey: ByteArray, val name: String, val senderKeys: Collection<ClosedGroupSenderKey>, val members: Collection<ByteArray>, val admins: Collection<ByteArray>) : Kind()
|
||||||
|
class SenderKeyRequest(val groupPublicKey: ByteArray) : Kind()
|
||||||
|
class SenderKey(val groupPublicKey: ByteArray, val senderKey: ClosedGroupSenderKey) : Kind()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val KEY = "ClosedGroupUpdateMessageSendJob"
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(destination: String, kind: Kind) : this(Parameters.Builder()
|
||||||
|
.addConstraint(NetworkConstraint.KEY)
|
||||||
|
.setQueue(KEY)
|
||||||
|
.setLifespan(TimeUnit.DAYS.toMillis(1))
|
||||||
|
.setMaxAttempts(1)
|
||||||
|
.build(),
|
||||||
|
destination,
|
||||||
|
kind)
|
||||||
|
|
||||||
|
override fun getFactoryKey(): String { return KEY }
|
||||||
|
|
||||||
|
override fun serialize(): Data {
|
||||||
|
val builder = Data.Builder()
|
||||||
|
builder.putString("destination", destination)
|
||||||
|
when (kind) {
|
||||||
|
is Kind.New -> {
|
||||||
|
builder.putString("kind", "New")
|
||||||
|
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
|
||||||
|
builder.putString("name", kind.name)
|
||||||
|
builder.putByteArray("groupPrivateKey", kind.groupPrivateKey)
|
||||||
|
val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() }
|
||||||
|
builder.putString("senderKeys", senderKeys)
|
||||||
|
val members = kind.members.joinToString(" - ") { it.toHexString() }
|
||||||
|
builder.putString("members", members)
|
||||||
|
val admins = kind.admins.joinToString(" - ") { it.toHexString() }
|
||||||
|
builder.putString("admins", admins)
|
||||||
|
}
|
||||||
|
is Kind.Info -> {
|
||||||
|
builder.putString("kind", "Info")
|
||||||
|
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
|
||||||
|
builder.putString("name", kind.name)
|
||||||
|
val senderKeys = kind.senderKeys.joinToString(" - ") { it.toJSON() }
|
||||||
|
builder.putString("senderKeys", senderKeys)
|
||||||
|
val members = kind.members.joinToString(" - ") { it.toHexString() }
|
||||||
|
builder.putString("members", members)
|
||||||
|
val admins = kind.admins.joinToString(" - ") { it.toHexString() }
|
||||||
|
builder.putString("admins", admins)
|
||||||
|
}
|
||||||
|
is Kind.SenderKeyRequest -> {
|
||||||
|
builder.putString("kind", "SenderKeyRequest")
|
||||||
|
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
|
||||||
|
}
|
||||||
|
is Kind.SenderKey -> {
|
||||||
|
builder.putString("kind", "SenderKey")
|
||||||
|
builder.putByteArray("groupPublicKey", kind.groupPublicKey)
|
||||||
|
builder.putString("senderKey", kind.senderKey.toJSON())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onRun() {
|
||||||
|
val contentMessage = SignalServiceProtos.Content.newBuilder()
|
||||||
|
val dataMessage = SignalServiceProtos.DataMessage.newBuilder()
|
||||||
|
val closedGroupUpdate = SignalServiceProtos.ClosedGroupUpdate.newBuilder()
|
||||||
|
when (kind) {
|
||||||
|
is Kind.New -> {
|
||||||
|
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.NEW
|
||||||
|
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
|
||||||
|
closedGroupUpdate.name = kind.name
|
||||||
|
closedGroupUpdate.groupPrivateKey = ByteString.copyFrom(kind.groupPrivateKey)
|
||||||
|
closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() })
|
||||||
|
closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) })
|
||||||
|
closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) })
|
||||||
|
}
|
||||||
|
is Kind.Info -> {
|
||||||
|
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.INFO
|
||||||
|
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
|
||||||
|
closedGroupUpdate.name = kind.name
|
||||||
|
closedGroupUpdate.addAllSenderKeys(kind.senderKeys.map { it.toProto() })
|
||||||
|
closedGroupUpdate.addAllMembers(kind.members.map { ByteString.copyFrom(it) })
|
||||||
|
closedGroupUpdate.addAllAdmins(kind.admins.map { ByteString.copyFrom(it) })
|
||||||
|
}
|
||||||
|
is Kind.SenderKeyRequest -> {
|
||||||
|
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST
|
||||||
|
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
|
||||||
|
}
|
||||||
|
is Kind.SenderKey -> {
|
||||||
|
closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY
|
||||||
|
closedGroupUpdate.groupPublicKey = ByteString.copyFrom(kind.groupPublicKey)
|
||||||
|
closedGroupUpdate.addAllSenderKeys(listOf( kind.senderKey.toProto() ))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataMessage.closedGroupUpdate = closedGroupUpdate.build()
|
||||||
|
contentMessage.dataMessage = dataMessage.build()
|
||||||
|
val serializedContentMessage = contentMessage.build().toByteArray()
|
||||||
|
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
|
||||||
|
val address = SignalServiceAddress(destination)
|
||||||
|
val recipient = recipient(context, destination)
|
||||||
|
val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient)
|
||||||
|
val ttl = TTLUtilities.getTTL(TTLUtilities.MessageType.ClosedGroupUpdate)
|
||||||
|
try {
|
||||||
|
// TODO: useFallbackEncryption
|
||||||
|
// TODO: isClosedGroup
|
||||||
|
messageSender.sendMessage(0, address, udAccess.get().targetUnidentifiedAccess,
|
||||||
|
Date().time, serializedContentMessage, false, ttl, false,
|
||||||
|
false, false, false)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override fun onShouldRetry(e: Exception): Boolean {
|
||||||
|
// Disable since we have our own retrying
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCanceled() { }
|
||||||
|
|
||||||
|
class Factory : Job.Factory<ClosedGroupUpdateMessageSendJob> {
|
||||||
|
|
||||||
|
override fun create(parameters: Parameters, data: Data): ClosedGroupUpdateMessageSendJob {
|
||||||
|
val destination = data.getString("destination")
|
||||||
|
val rawKind = data.getString("kind")
|
||||||
|
val groupPublicKey = data.getByteArray("groupPublicKey")
|
||||||
|
val kind: Kind
|
||||||
|
when (rawKind) {
|
||||||
|
"New" -> {
|
||||||
|
val name = data.getString("name")
|
||||||
|
val groupPrivateKey = data.getByteArray("groupPrivateKey")
|
||||||
|
val senderKeys = data.getString("senderKeys").split(" - ").map { ClosedGroupSenderKey.fromJSON(it)!! }
|
||||||
|
val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) }
|
||||||
|
val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) }
|
||||||
|
kind = Kind.New(groupPublicKey, name, groupPrivateKey, senderKeys, members, admins)
|
||||||
|
}
|
||||||
|
"Info" -> {
|
||||||
|
val name = data.getString("name")
|
||||||
|
val senderKeys = data.getString("senderKeys").split(" - ").map { ClosedGroupSenderKey.fromJSON(it)!! }
|
||||||
|
val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) }
|
||||||
|
val admins = data.getString("admins").split(" - ").map { Hex.fromStringCondensed(it) }
|
||||||
|
kind = Kind.Info(groupPublicKey, name, senderKeys, members, admins)
|
||||||
|
}
|
||||||
|
"SenderKeyRequest" -> {
|
||||||
|
kind = Kind.SenderKeyRequest(groupPublicKey)
|
||||||
|
}
|
||||||
|
"SenderKey" -> {
|
||||||
|
val senderKey = ClosedGroupSenderKey.fromJSON(data.getString("senderKey"))!!
|
||||||
|
kind = Kind.SenderKey(groupPublicKey, senderKey)
|
||||||
|
}
|
||||||
|
else -> throw Exception("Invalid closed group update message kind: $rawKind.")
|
||||||
|
}
|
||||||
|
return ClosedGroupUpdateMessageSendJob(parameters, destination, kind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user