Commit ef42becb authored by Jonathan Almeida's avatar Jonathan Almeida Committed by Jonathan Almeida
Browse files

For #9059: Add WebPush engine integration

parent 667ebe31
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.metrics.MetricServiceType
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.push.PushFxaIntegration
import org.mozilla.fenix.push.WebPushEngineIntegration
import org.mozilla.fenix.session.NotificationSessionObserver
import org.mozilla.fenix.session.PerformanceActivityLifecycleCallbacks
import org.mozilla.fenix.session.VisibilityLifecycleCallback
@@ -182,6 +183,9 @@ open class FenixApplication : LocaleAwareApplication() {
            // Install the AutoPush singleton to receive messages.
            PushProcessor.install(it)

            // WebPush integration to observe and deliver push messages to engine.
            WebPushEngineIntegration(components.core.engine, it).start()

            // Perform a one-time initialization of the account manager if a message is received.
            PushFxaIntegration(it, lazy { components.backgroundServices.accountManager }).launch()

+119 −0
Original line number Diff line number Diff line
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.fenix.push

import android.util.Base64
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.concept.engine.Engine
import mozilla.components.concept.engine.webpush.WebPushDelegate
import mozilla.components.concept.engine.webpush.WebPushHandler
import mozilla.components.concept.engine.webpush.WebPushSubscription
import mozilla.components.feature.push.AutoPushFeature
import mozilla.components.feature.push.AutoPushSubscription
import mozilla.components.feature.push.PushScope
import mozilla.components.support.base.log.logger.Logger

/**
 * Engine integration with the push feature to enable WebPush support.
 */
class WebPushEngineIntegration(
    private val engine: Engine,
    private val pushFeature: AutoPushFeature
) : AutoPushFeature.Observer {

    private var handler: WebPushHandler? = null
    private val delegate = WebPushEngineDelegate(pushFeature)

    fun start() {
        handler = engine.registerWebPushDelegate(delegate)

        pushFeature.register(this)
    }

    fun stop() {
        pushFeature.unregister(this)
    }

    override fun onMessageReceived(scope: PushScope, message: ByteArray?) {
        CoroutineScope(Dispatchers.Main).launch {
            handler?.onPushMessage(scope, message)
        }
    }

    override fun onSubscriptionChanged(scope: PushScope) {
        CoroutineScope(Dispatchers.Main).launch {
            handler?.onSubscriptionChanged(scope)
        }
    }
}

internal class WebPushEngineDelegate(
    private val pushFeature: AutoPushFeature
) : WebPushDelegate {
    private val logger = Logger("WebPushEngineDelegate")

    override fun onGetSubscription(scope: String, onSubscription: (WebPushSubscription?) -> Unit) {
        // We don't have the appServerKey unless an app is creating a new subscription so we
        // allow the key to be null since it won't be overridden from a previous subscription.
        pushFeature.subscribe(
            scope = scope,
            onSubscribeError = {
                logger.error("Error on push onGetSubscription.")
                onSubscription(null)
            },
            onSubscribe = { subscription ->
                onSubscription(subscription.toEnginePushSubscription())
            })
    }

    override fun onSubscribe(
        scope: String,
        serverKey: ByteArray?,
        onSubscribe: (WebPushSubscription?) -> Unit
    ) {
        pushFeature.subscribe(
            scope = scope,
            // See the full note at the implementation of `toEnginePushSubscription`.
            // Issue: https://github.com/mozilla/application-services/issues/2698
            appServerKey = null,
            onSubscribeError = {
                logger.error("Error on push onSubscribe.")
                onSubscribe(null)
            },
            onSubscribe = { subscription ->
                onSubscribe(subscription.toEnginePushSubscription())
            })
    }

    override fun onUnsubscribe(scope: String, onUnsubscribe: (Boolean) -> Unit) {
        pushFeature.unsubscribe(
            scope = scope,
            onUnsubscribeError = {
                logger.error("Error on push onUnsubscribe.")
                onUnsubscribe(false)
            },
            onUnsubscribe = { result ->
                onUnsubscribe(result)
            })
    }
}

internal fun AutoPushSubscription.toEnginePushSubscription() = WebPushSubscription(
    scope = this.scope,
    publicKey = this.publicKey.toDecodedByteArray(),
    endpoint = this.endpoint,
    authSecret = this.authKey.toDecodedByteArray(),
    // We don't send the `serverKey` because the code path from that will query
    // the push database for this key, which leads to an exception thrown.
    // Our workaround for now is to not put the server key in to begin with (which
    // will probably break a lot of sites).
    // See: https://github.com/mozilla/application-services/issues/2698
    appServerKey = null
)

private fun String.toDecodedByteArray() =
    Base64.decode(this.toByteArray(), Base64.URL_SAFE or Base64.NO_PADDING or Base64.NO_WRAP)