From 9d4f747c123f1edd2de74d10b1ce83ceccbf8110 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 25 Feb 2025 11:38:51 +0100 Subject: [PATCH 1/5] Upgrade UnifiedPush Connector to 3.0.4 Signed-off-by: Arnau Mora --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dad9321fc..eedc49f91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ mockk = "1.13.16" okhttp = "4.12.0" openid-appauth = "0.11.1" room = "2.6.1" -unifiedpush = "2.4.0" +unifiedpush = "3.0.4" [libraries] android-desugaring = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "android-desugaring" } From bd1f2ae1f742ebf195319c47689e1ef0eeb3556d Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 25 Feb 2025 11:39:56 +0100 Subject: [PATCH 2/5] Updated overrides Signed-off-by: Arnau Mora --- .../davdroid/push/UnifiedPushReceiver.kt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/UnifiedPushReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/UnifiedPushReceiver.kt index be94a03a5..87aa65039 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/UnifiedPushReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/UnifiedPushReceiver.kt @@ -18,7 +18,10 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import org.unifiedpush.android.connector.FailedReason import org.unifiedpush.android.connector.MessagingReceiver +import org.unifiedpush.android.connector.data.PushEndpoint +import org.unifiedpush.android.connector.data.PushMessage import java.util.logging.Level import java.util.logging.Logger import javax.inject.Inject @@ -54,7 +57,7 @@ class UnifiedPushReceiver: MessagingReceiver() { lateinit var syncWorkerManager: SyncWorkerManager - override fun onNewEndpoint(context: Context, endpoint: String, instance: String) { + override fun onNewEndpoint(context: Context, endpoint: PushEndpoint, instance: String) { // remember new endpoint preferenceRepository.unifiedPushEndpoint(endpoint) @@ -62,14 +65,24 @@ class UnifiedPushReceiver: MessagingReceiver() { pushRegistrationWorkerManager.updatePeriodicWorker() } + override fun onRegistrationFailed(context: Context, reason: FailedReason, instance: String) { + logger.warning("Unified Push registration failed: $reason") + // reset known endpoint to make sure nothing is stored when not registered + preferenceRepository.unifiedPushEndpoint(null) + } + override fun onUnregistered(context: Context, instance: String) { // reset known endpoint preferenceRepository.unifiedPushEndpoint(null) } - override fun onMessage(context: Context, message: ByteArray, instance: String) { + override fun onMessage(context: Context, message: PushMessage, instance: String) { CoroutineScope(Dispatchers.Default).launch { - val messageXml = message.toString(Charsets.UTF_8) + if (!message.decrypted) { + logger.severe("Received a push message that could not be decrypted.") + return@launch + } + val messageXml = message.content.toString(Charsets.UTF_8) logger.log(Level.INFO, "Received push message", messageXml) // parse push notification From 24a31a3a092cd237ae478a3da18031b86f2aa5ef Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 25 Feb 2025 11:40:34 +0100 Subject: [PATCH 3/5] Added storing keys and auths Signed-off-by: Arnau Mora --- .../davdroid/push/PushRegistrationWorker.kt | 16 +++++++++++-- .../repository/PreferenceRepository.kt | 23 ++++++++++++++----- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt index 06d311a83..816e76da7 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/push/PushRegistrationWorker.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.runInterruptible import okhttp3.HttpUrl import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.RequestBody.Companion.toRequestBody +import org.unifiedpush.android.connector.data.PushEndpoint import java.io.IOException import java.io.StringWriter import java.time.Duration @@ -67,7 +68,7 @@ class PushRegistrationWorker @AssistedInject constructor( return Result.success() } - private suspend fun registerPushSubscription(collection: Collection, account: Account, endpoint: String) { + private suspend fun registerPushSubscription(collection: Collection, account: Account, endpoint: PushEndpoint) { httpClientBuilder.get() .fromAccount(account) .inForeground(true) @@ -88,7 +89,18 @@ class PushRegistrationWorker @AssistedInject constructor( // subscription URL serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "web-push-subscription")) { serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "push-resource")) { - text(endpoint) + text(endpoint.url) + } + endpoint.pubKeySet?.let { pubKeySet -> + // Right now only p256dh is supported, but more can be added in + // the future. + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "client-public-key")) { + attribute(NS_WEBDAV_PUSH, "type", "p256dh") + text(pubKeySet.pubKey) + } + serializer.insertTag(Property.Name(NS_WEBDAV_PUSH, "auth-secret")) { + text(pubKeySet.auth) + } } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/PreferenceRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/PreferenceRepository.kt index cb26149c4..0314a14a4 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/PreferenceRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/PreferenceRepository.kt @@ -11,6 +11,8 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow +import org.unifiedpush.android.connector.data.PublicKeySet +import org.unifiedpush.android.connector.data.PushEndpoint import javax.inject.Inject /** @@ -24,7 +26,9 @@ class PreferenceRepository @Inject constructor( companion object { const val LOG_TO_FILE = "log_to_file" - const val UNIFIED_PUSH_ENDPOINT = "unified_push_endpoint" + const val UNIFIED_PUSH_ENDPOINT_URL = "unified_push_endpoint_url" + const val UNIFIED_PUSH_ENDPOINT_KEY = "unified_push_endpoint_key" + const val UNIFIED_PUSH_ENDPOINT_AUTH = "unified_push_endpoint_auth" } private val preferences = PreferenceManager.getDefaultSharedPreferences(context) @@ -54,17 +58,24 @@ class PreferenceRepository @Inject constructor( } - fun unifiedPushEndpoint() = - preferences.getString(UNIFIED_PUSH_ENDPOINT, null) + fun unifiedPushEndpoint(): PushEndpoint? { + val url = preferences.getString(UNIFIED_PUSH_ENDPOINT_URL, null) ?: return null + val key = preferences.getString(UNIFIED_PUSH_ENDPOINT_KEY, null) + val auth = preferences.getString(UNIFIED_PUSH_ENDPOINT_AUTH, null) + val publicKeySet = if (key != null && auth != null) PublicKeySet(key, auth) else null + return PushEndpoint(url, publicKeySet) + } - fun unifiedPushEndpointFlow() = observeAsFlow(UNIFIED_PUSH_ENDPOINT) { + fun unifiedPushEndpointFlow() = observeAsFlow(UNIFIED_PUSH_ENDPOINT_URL) { unifiedPushEndpoint() } - fun unifiedPushEndpoint(endpoint: String?) { + fun unifiedPushEndpoint(endpoint: PushEndpoint?) { preferences .edit() - .putString(UNIFIED_PUSH_ENDPOINT, endpoint) + .putString(UNIFIED_PUSH_ENDPOINT_URL, endpoint?.url) + .putString(UNIFIED_PUSH_ENDPOINT_KEY, endpoint?.pubKeySet?.pubKey) + .putString(UNIFIED_PUSH_ENDPOINT_AUTH, endpoint?.pubKeySet?.auth) .apply() } From bab946361b74c30d8b61911958df174be8b449dd Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 25 Feb 2025 11:58:29 +0100 Subject: [PATCH 4/5] Excluded tink Signed-off-by: Arnau Mora --- app/build.gradle.kts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b3e75600d..23ae11cdf 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -195,7 +195,12 @@ dependencies { implementation(libs.okhttp.brotli) implementation(libs.okhttp.logging) implementation(libs.openid.appauth) - implementation(libs.unifiedpush) + implementation(libs.unifiedpush) { + // UnifiedPush connector seems to be using a workaround by importing this library. + // Will be removed after https://github.com/tink-crypto/tink-java-apps/pull/5 is merged. + // See: https://codeberg.org/UnifiedPush/android-connector/src/commit/28cb0d622ed0a972996041ab9cc85b701abc48c6/connector/build.gradle#L56-L59 + exclude(group = "com.google.crypto.tink", module = "tink") + } // for tests androidTestImplementation(libs.androidx.arch.core.testing) From f961096543f7e5e6af583887f5a021689d73a214 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Tue, 25 Feb 2025 11:58:40 +0100 Subject: [PATCH 5/5] Fixed deprecations and calls Signed-off-by: Arnau Mora --- .../main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt index 73551c2e6..e2c7916a4 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AppSettingsModel.kt @@ -160,12 +160,12 @@ class AppSettingsModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { if (pushDistributor == null) { // Disable UnifiedPush if the distributor given is null - UnifiedPush.safeRemoveDistributor(context) - UnifiedPush.unregisterApp(context) + UnifiedPush.removeDistributor(context) + UnifiedPush.unregister(context) } else { // If a distributor was passed, store it and register the app UnifiedPush.saveDistributor(context, pushDistributor) - UnifiedPush.registerApp(context) + UnifiedPush.register(context) } _pushDistributor.value = pushDistributor }