mirror of
https://github.com/oxen-io/session-android.git
synced 2025-12-06 03:33:24 +00:00
...
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
package org.thoughtcrime.securesms.notifications
|
||||
|
||||
import android.content.Context
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
|
||||
class ExpiryManager(
|
||||
private val context: Context,
|
||||
private val interval: Int = 12 * 60 * 60 * 1000
|
||||
) {
|
||||
fun isExpired() = currentTime() > time + interval
|
||||
|
||||
fun markTime() {
|
||||
time = currentTime()
|
||||
}
|
||||
|
||||
fun clearTime() {
|
||||
time = 0
|
||||
}
|
||||
|
||||
private var time
|
||||
get() = TextSecurePreferences.getLastFCMUploadTime(context)
|
||||
set(value) = TextSecurePreferences.setLastFCMUploadTime(context, value)
|
||||
|
||||
private fun currentTime() = System.currentTimeMillis()
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.thoughtcrime.securesms.notifications
|
||||
|
||||
import android.content.Context
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
|
||||
class FcmTokenManager(
|
||||
private val context: Context,
|
||||
private val expiryManager: ExpiryManager
|
||||
) {
|
||||
val isUsingFCM get() = TextSecurePreferences.isUsingFCM(context)
|
||||
|
||||
var fcmToken
|
||||
get() = TextSecurePreferences.getFCMToken(context)
|
||||
set(value) {
|
||||
TextSecurePreferences.setFCMToken(context, value)
|
||||
if (value != null) markTime() else clearTime()
|
||||
}
|
||||
|
||||
val requiresUnregister get() = fcmToken != null
|
||||
|
||||
private fun clearTime() = expiryManager.clearTime()
|
||||
private fun markTime() = expiryManager.markTime()
|
||||
private fun isExpired() = expiryManager.isExpired()
|
||||
|
||||
fun isInvalid(): Boolean = fcmToken == null || isExpired()
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import org.session.libsignal.utilities.emptyPromise
|
||||
import org.session.libsignal.utilities.retryIfNeeded
|
||||
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
||||
import javax.inject.Inject
|
||||
|
||||
private const val TAG = "FirebasePushManager"
|
||||
|
||||
@@ -47,7 +48,7 @@ class FirebasePushManager(
|
||||
private val context: Context
|
||||
): PushManager {
|
||||
|
||||
private val pushManagerV2 = PushManagerV2(context)
|
||||
@Inject lateinit var pushManagerV2: PushManagerV2
|
||||
|
||||
companion object {
|
||||
const val maxRetryCount = 4
|
||||
@@ -132,6 +133,4 @@ class FirebasePushManager(
|
||||
} success {
|
||||
tokenManager.fcmToken = null
|
||||
}
|
||||
|
||||
fun decrypt(decode: ByteArray) = pushManagerV2.decrypt(decode)
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
||||
|
||||
private const val TAG = "PushManagerV2"
|
||||
|
||||
class PushManagerV2(private val context: Context) {
|
||||
class PushManagerV2(private val pushHandler: PushHandler) {
|
||||
private val sodium = LazySodiumAndroid(SodiumAndroid())
|
||||
|
||||
fun register(
|
||||
@@ -48,7 +48,7 @@ class PushManagerV2(private val context: Context) {
|
||||
userEd25519Key: KeyPair,
|
||||
namespaces: List<Int>
|
||||
): Promise<SubscriptionResponse, Exception> {
|
||||
val pnKey = getOrCreateNotificationKey()
|
||||
val pnKey = pushHandler.getOrCreateNotificationKey()
|
||||
|
||||
val timestamp = SnodeAPI.nowWithOffset / 1000 // get timestamp in ms -> s
|
||||
// if we want to support passing namespace list, here is the place to do it
|
||||
@@ -117,41 +117,4 @@ class PushManagerV2(private val context: Context) {
|
||||
.also { if (it.isFailure()) throw Exception("error: ${it.message}.") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateNotificationKey(): Key {
|
||||
if (IdentityKeyUtil.retrieve(context, IdentityKeyUtil.NOTIFICATION_KEY) == null) {
|
||||
// generate the key and store it
|
||||
val key = sodium.keygen(AEAD.Method.XCHACHA20_POLY1305_IETF)
|
||||
IdentityKeyUtil.save(context, IdentityKeyUtil.NOTIFICATION_KEY, key.asHexString)
|
||||
}
|
||||
return Key.fromHexString(IdentityKeyUtil.retrieve(context, IdentityKeyUtil.NOTIFICATION_KEY))
|
||||
}
|
||||
|
||||
fun decrypt(encPayload: ByteArray): ByteArray? {
|
||||
Log.d(TAG, "decrypt() called")
|
||||
|
||||
val encKey = getOrCreateNotificationKey()
|
||||
val nonce = encPayload.take(AEAD.XCHACHA20POLY1305_IETF_NPUBBYTES).toByteArray()
|
||||
val payload = encPayload.drop(AEAD.XCHACHA20POLY1305_IETF_NPUBBYTES).toByteArray()
|
||||
val padded = SodiumUtilities.decrypt(payload, encKey.asBytes, nonce)
|
||||
?: error("Failed to decrypt push notification")
|
||||
val decrypted = padded.dropLastWhile { it.toInt() == 0 }.toByteArray()
|
||||
val bencoded = Bencode.Decoder(decrypted)
|
||||
val expectedList = (bencoded.decode() as? BencodeList)?.values
|
||||
?: error("Failed to decode bencoded list from payload")
|
||||
|
||||
val metadataJson = (expectedList[0] as? BencodeString)?.value ?: error("no metadata")
|
||||
val metadata: PushNotificationMetadata = Json.decodeFromString(String(metadataJson))
|
||||
|
||||
val content: ByteArray? = if (expectedList.size >= 2) (expectedList[1] as? BencodeString)?.value else null
|
||||
// null content is valid only if we got a "data_too_long" flag
|
||||
if (content == null)
|
||||
check(metadata.data_too_long) { "missing message data, but no too-long flag" }
|
||||
else
|
||||
check(metadata.data_len == content.size) { "wrong message data size" }
|
||||
|
||||
Log.d(TAG, "Received push for ${metadata.account}/${metadata.namespace}, msg ${metadata.msg_hash}, ${metadata.data_len}B")
|
||||
|
||||
return content
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.session.libsession.messaging.jobs.BatchMessageReceiveJob
|
||||
import org.session.libsession.messaging.jobs.JobQueue
|
||||
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
||||
import org.session.libsession.messaging.sending_receiving.notifications.PushManagerV1
|
||||
import org.session.libsession.messaging.utilities.MessageWrapper
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
import org.session.libsignal.utilities.Base64
|
||||
@@ -21,6 +20,7 @@ private const val TAG = "PushNotificationService"
|
||||
class PushNotificationService : FirebaseMessagingService() {
|
||||
|
||||
@Inject lateinit var pushManager: FirebasePushManager
|
||||
@Inject lateinit var pushHandler: PushHandler
|
||||
|
||||
override fun onNewToken(token: String) {
|
||||
super.onNewToken(token)
|
||||
@@ -32,37 +32,7 @@ class PushNotificationService : FirebaseMessagingService() {
|
||||
|
||||
override fun onMessageReceived(message: RemoteMessage) {
|
||||
Log.d(TAG, "Received a push notification.")
|
||||
val data: ByteArray? = if (message.data.containsKey("spns")) {
|
||||
// this is a v2 push notification
|
||||
try {
|
||||
pushManager.decrypt(Base64.decode(message.data["enc_payload"]))
|
||||
} catch(e: Exception) {
|
||||
Log.e(TAG, "Invalid push notification: ${e.message}")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// old v1 push notification; we still need this for receiving legacy closed group notifications
|
||||
message.data?.get("ENCRYPTED_DATA")?.let(Base64::decode)
|
||||
}
|
||||
if (data != null) {
|
||||
try {
|
||||
val envelopeAsData = MessageWrapper.unwrap(data).toByteArray()
|
||||
val job = BatchMessageReceiveJob(listOf(MessageReceiveParameters(envelopeAsData)), null)
|
||||
JobQueue.shared.add(job)
|
||||
} catch (e: Exception) {
|
||||
Log.d(TAG, "Failed to unwrap data for message due to error: $e.")
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "Failed to decode data for message.")
|
||||
val builder = NotificationCompat.Builder(this, NotificationChannels.OTHER)
|
||||
.setSmallIcon(network.loki.messenger.R.drawable.ic_notification)
|
||||
.setColor(resources.getColor(network.loki.messenger.R.color.textsecure_primary))
|
||||
.setContentTitle("Session")
|
||||
.setContentText("You've got a new message.")
|
||||
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||
.setAutoCancel(true)
|
||||
NotificationManagerCompat.from(this).notify(11111, builder.build())
|
||||
}
|
||||
pushHandler.onPush(message.data)
|
||||
}
|
||||
|
||||
override fun onDeletedMessages() {
|
||||
|
||||
Reference in New Issue
Block a user