diff --git a/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiPushService.kt b/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiPushService.kt index 2c3512acc7..fb4e262560 100644 --- a/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiPushService.kt +++ b/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiPushService.kt @@ -14,11 +14,6 @@ private val TAG = HuaweiPushService::class.java.simpleName @AndroidEntryPoint class HuaweiPushService: HmsMessageService() { - - init { - Log.d(TAG, "init Huawei Service") - } - @Inject lateinit var pushRegistry: PushRegistry @Inject lateinit var pushReceiver: PushReceiver @@ -32,33 +27,13 @@ class HuaweiPushService: HmsMessageService() { pushReceiver.onPush(message?.data?.let(Base64::decode)) } - override fun onMessageSent(p0: String?) { - Log.d(TAG, "onMessageSent() called with: p0 = $p0") - super.onMessageSent(p0) - } - - override fun onSendError(p0: String?, p1: Exception?) { - Log.d(TAG, "onSendError() called with: p0 = $p0, p1 = $p1") - super.onSendError(p0, p1) - } - - override fun onMessageDelivered(p0: String?, p1: Exception?) { - Log.d(TAG, "onMessageDelivered") - super.onMessageDelivered(p0, p1) - } - - - override fun onNewToken(p0: String?) { - Log.d(TAG, "onNewToken") - super.onNewToken(p0) + override fun onNewToken(token: String?) { + pushRegistry.register(token) } override fun onNewToken(token: String?, bundle: Bundle?) { Log.d(TAG, "New HCM token: $token.") - - TextSecurePreferences.setPushToken(this, token) - - pushRegistry.refresh(token, true) + onNewToken(token) } override fun onDeletedMessages() { diff --git a/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiTokenFetcher.kt b/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiTokenFetcher.kt index e3b6afe4cc..9d9b61ce9a 100644 --- a/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiTokenFetcher.kt +++ b/app/src/huawei/kotlin/org/thoughtcrime/securesms/notifications/HuaweiTokenFetcher.kt @@ -2,26 +2,28 @@ package org.thoughtcrime.securesms.notifications import android.content.Context import com.huawei.hms.aaid.HmsInstanceId +import dagger.Lazy import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.session.libsignal.utilities.Log import javax.inject.Inject import javax.inject.Singleton +private const val APP_ID = "107205081" +private const val TOKEN_SCOPE = "HCM" + @Singleton class HuaweiTokenFetcher @Inject constructor( - @ApplicationContext private val context: Context + @ApplicationContext private val context: Context, + private val pushRegistry: Lazy, ): TokenFetcher { - override fun fetch(): Job { - val hmsInstanceId = HmsInstanceId.getInstance(context) - - return MainScope().launch(Dispatchers.IO) { - val appId = "107205081" - val tokenScope = "HCM" - // getToken returns an empty string, but triggers the service to initialize. - hmsInstanceId.getToken(appId, tokenScope) - } + override suspend fun fetch(): String? = HmsInstanceId.getInstance(context).run { + // https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/push-basic-capability#h2-1576218800370 + // getToken may return an empty string, if so HuaweiPushService#onNewToken will be called. + withContext(Dispatchers.IO) { getToken(APP_ID, TOKEN_SCOPE) } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index 73b0af55f7..75622c7bb6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -427,11 +427,6 @@ public class ApplicationContext extends Application implements DefaultLifecycleO } private static class ProviderInitializationException extends RuntimeException { } - - public void registerForPnIfNeeded(final Boolean force) { - pushRegistry.refresh(force); - } - private void setUpPollingIfNeeded() { String userPublicKey = TextSecurePreferences.getLocalNumber(this); if (userPublicKey == null) return; diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt index 56871ba64e..1d097c7059 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushReceiver.kt @@ -75,7 +75,7 @@ class PushReceiver @Inject constructor(@ApplicationContext val context: Context) else -> this["ENCRYPTED_DATA"]?.let(Base64::decode) } - fun decrypt(encPayload: ByteArray): ByteArray? { + private fun decrypt(encPayload: ByteArray): ByteArray? { Log.d(TAG, "decrypt() called") val encKey = getOrCreateNotificationKey() diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistry.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistry.kt index 9a625f133c..dc50ad8bdd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistry.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushRegistry.kt @@ -4,6 +4,8 @@ import android.content.Context import com.goterl.lazysodium.utils.KeyPair import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Job +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.launch import nl.komponents.kovenant.Promise import nl.komponents.kovenant.combine.and import org.session.libsession.messaging.sending_receiving.notifications.PushRegistryV1 @@ -22,8 +24,10 @@ private val TAG = PushRegistry::class.java.name class PushRegistry @Inject constructor( @ApplicationContext private val context: Context, private val device: Device, - private val tokenManager: PushTokenManager, + private val tokenManager: TokenManager, private val pushRegistryV2: PushRegistryV2, + private val prefs: TextSecurePreferences, + private val tokenFetcher: TokenFetcher, ) { private var pushRegistrationJob: Job? = null @@ -32,42 +36,33 @@ class PushRegistry @Inject constructor( Log.d(TAG, "refresh() called with: force = $force") pushRegistrationJob?.apply { - if (force) cancel() else if (isActive) return + if (force) cancel() else if (isActive || !tokenManager.hasValidRegistration) return } - pushRegistrationJob = tokenManager.fetchToken() + pushRegistrationJob = MainScope().launch { + register(tokenFetcher.fetch()) fail { + Log.e(TAG, "register failed", it) + } + } } - fun refresh(token: String?, force: Boolean): Promise<*, Exception> { - Log.d(TAG, "refresh($token, $force) called") + fun register(token: String?): Promise<*, Exception> { + Log.d(TAG, "refresh($token) called") - token ?: return emptyPromise() - val userPublicKey = TextSecurePreferences.getLocalNumber(context) ?: return emptyPromise() + if (token?.isNotEmpty() != true) return emptyPromise() + + prefs.setPushToken(token) + + val userPublicKey = prefs.getLocalNumber() ?: return emptyPromise() val userEdKey = KeyPairUtilities.getUserED25519KeyPair(context) ?: return emptyPromise() return when { - tokenManager.isPushEnabled -> register(force, token, userPublicKey, userEdKey) - tokenManager.requiresUnregister -> unregister(token, userPublicKey, userEdKey) + prefs.isPushEnabled() -> register(token, userPublicKey, userEdKey) + tokenManager.isRegistered -> unregister(token, userPublicKey, userEdKey) else -> emptyPromise() } } - /** - * Register for push notifications if: - * force is true - * there is no FCM Token - * FCM Token has expired - */ - private fun register( - force: Boolean, - token: String, - publicKey: String, - userEd25519Key: KeyPair, - namespaces: List = listOf(Namespace.DEFAULT) - ): Promise<*, Exception> = if (force || tokenManager.isInvalid()) { - register(token, publicKey, userEd25519Key, namespaces) - } else emptyPromise() - /** * Register for push notifications. */ @@ -77,7 +72,7 @@ class PushRegistry @Inject constructor( userEd25519Key: KeyPair, namespaces: List = listOf(Namespace.DEFAULT) ): Promise<*, Exception> { - android.util.Log.d( + Log.d( TAG, "register() called with: token = $token, publicKey = $publicKey, userEd25519Key = $userEd25519Key, namespaces = $namespaces" ) @@ -97,8 +92,8 @@ class PushRegistry @Inject constructor( } return v1 and v2 success { - Log.d(TAG, "registerBoth success... saving token!!") - tokenManager.fcmToken = token + Log.d(TAG, "register v1 & v2 success") + tokenManager.register() } } @@ -111,6 +106,6 @@ class PushRegistry @Inject constructor( ) fail { Log.e(TAG, "unregisterBoth failed", it) } success { - tokenManager.fcmToken = null + tokenManager.unregister() } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushTokenManager.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/PushTokenManager.kt deleted file mode 100644 index 45469a3dd9..0000000000 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/PushTokenManager.kt +++ /dev/null @@ -1,34 +0,0 @@ -package org.thoughtcrime.securesms.notifications - -import android.content.Context -import dagger.hilt.android.qualifiers.ApplicationContext -import kotlinx.coroutines.Job -import org.session.libsession.utilities.TextSecurePreferences -import javax.inject.Inject -import javax.inject.Singleton - -@Singleton -class PushTokenManager @Inject constructor( - @ApplicationContext private val context: Context, - private val tokenFetcher: TokenFetcher -) { - private val expiryManager = ExpiryManager(context) - - val isPushEnabled get() = TextSecurePreferences.isPushEnabled(context) - - var fcmToken - get() = TextSecurePreferences.getPushToken(context) - set(value) { - TextSecurePreferences.setPushToken(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() - fun fetchToken(): Job = tokenFetcher.fetch() -} diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenFetcher.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenFetcher.kt index d8657e95d3..5bd9ce0d8d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenFetcher.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenFetcher.kt @@ -1,7 +1,5 @@ package org.thoughtcrime.securesms.notifications -import kotlinx.coroutines.Job - interface TokenFetcher { - fun fetch(): Job + suspend fun fetch(): String? } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/ExpiryManager.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenManager.kt similarity index 59% rename from app/src/main/java/org/thoughtcrime/securesms/notifications/ExpiryManager.kt rename to app/src/main/java/org/thoughtcrime/securesms/notifications/TokenManager.kt index 2b3f0aa860..b3db642b81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/ExpiryManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/TokenManager.kt @@ -6,17 +6,21 @@ import org.session.libsession.utilities.TextSecurePreferences import javax.inject.Inject import javax.inject.Singleton -class ExpiryManager( - private val context: Context, - private val interval: Int = 12 * 60 * 60 * 1000 -) { - fun isExpired() = currentTime() > time + interval +private const val INTERVAL: Int = 12 * 60 * 60 * 1000 - fun markTime() { +@Singleton +class TokenManager @Inject constructor( + @ApplicationContext private val context: Context, +) { + val hasValidRegistration get() = isRegistered && !isExpired + val isRegistered get() = time > 0 + private val isExpired get() = currentTime() > time + INTERVAL + + fun register() { time = currentTime() } - fun clearTime() { + fun unregister() { time = 0 } diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushService.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushService.kt index 1ebf405a28..2a3b054a58 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushService.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebasePushService.kt @@ -18,7 +18,7 @@ class FirebasePushService : FirebaseMessagingService() { override fun onNewToken(token: String) { if (token == prefs.getPushToken()) return - pushRegistry.refresh(token, true) + pushRegistry.register(token) } override fun onMessageReceived(message: RemoteMessage) { diff --git a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt index 70f3fb4522..d40f160d0b 100644 --- a/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt +++ b/app/src/play/kotlin/org/thoughtcrime/securesms/notifications/FirebaseTokenFetcher.kt @@ -1,43 +1,19 @@ package org.thoughtcrime.securesms.notifications -import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.Tasks import com.google.firebase.iid.FirebaseInstanceId -import com.google.firebase.iid.InstanceIdResult -import dagger.Lazy import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.MainScope import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import org.session.libsignal.utilities.Log +import kotlinx.coroutines.withContext import javax.inject.Inject import javax.inject.Singleton -private val TAG = FirebaseTokenFetcher::class.java.name - @Singleton -class FirebaseTokenFetcher @Inject constructor( - private val pushRegistry: Lazy, -): TokenFetcher { - override fun fetch(): Job = MainScope().launch(Dispatchers.IO) { +class FirebaseTokenFetcher @Inject constructor(): TokenFetcher { + override suspend fun fetch() = withContext(Dispatchers.IO) { FirebaseInstanceId.getInstance().instanceId .also(Tasks::await) - .also { if (!isActive) return@launch } // don't 'complete' task if we were canceled - .process() + .takeIf { isActive } // don't 'complete' task if we were canceled + ?.run { result?.token ?: throw exception!! } } - - private fun Task.process() = when { - isSuccessful -> try { - result?.token?.let { - pushRegistry.get().refresh(it, force = true).get() - } - } catch (e: Exception) { - onFail(e) - } - else -> exception?.let(::onFail) - } - - private fun onFail(e: Exception) = Log.e(TAG, "fetch failed", e) } -