Cleanup FetchPushForegroundService (#6577)

* Rename `PushHandlingWakeLock` to `FetchPushForegroundServiceManager`. Move the start/stop logic from `FetchPushForegroundService.Companion` to it.

* Add more tests using Robolectric.

* Remove `FeatureFlags.SyncNotificationsWithWorkManager` and associated code: this should have been removed in one of the previous refactors, since we don't have the 2 ways to sync notifications anymore, everything uses the `WorkManager`

---------

Co-authored-by: Benoit Marty <benoit@matrix.org>
This commit is contained in:
Jorge Martin Espinosa 2026-04-20 16:03:12 +02:00 committed by GitHub
parent 8853f160e2
commit f80a140cf9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 329 additions and 215 deletions

View file

@ -15,7 +15,7 @@ import dev.zacsweers.metro.Inject
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
import io.element.android.libraries.push.api.push.FetchPushForegroundServiceManager
import io.element.android.libraries.pushproviders.api.PushHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -27,7 +27,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
@Inject lateinit var firebaseNewTokenHandler: FirebaseNewTokenHandler
@Inject lateinit var pushParser: FirebasePushParser
@Inject lateinit var pushHandler: PushHandler
@Inject lateinit var pushHandlingWakeLock: PushHandlingWakeLock
@Inject lateinit var fetchPushForegroundServiceManager: FetchPushForegroundServiceManager
@AppCoroutineScope
@Inject lateinit var coroutineScope: CoroutineScope
@ -49,7 +49,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
val isHighPriority = message.priority == PRIORITY_HIGH
if (isHighPriority) {
// Acquire wakelock to ensure the device stays awake while we handle the push and schedule and run the work
pushHandlingWakeLock.lock()
fetchPushForegroundServiceManager.start()
}
coroutineScope.launch {
@ -63,7 +63,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
},
)
if (isHighPriority) {
pushHandlingWakeLock.unlock()
fetchPushForegroundServiceManager.stop()
}
} else {
val handled = pushHandler.handle(
@ -73,7 +73,7 @@ class VectorFirebaseMessagingService : FirebaseMessagingService() {
// If we failed to handle the push, we should release the wakelock early to avoid keeping the device awake for too long.
if (!handled && isHighPriority) {
pushHandlingWakeLock.unlock()
fetchPushForegroundServiceManager.stop()
}
}
}

View file

@ -15,7 +15,7 @@ import com.google.firebase.messaging.RemoteMessage
import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SECRET
import io.element.android.libraries.push.test.push.FakePushHandlingWakeLock
import io.element.android.libraries.push.test.push.FakeFetchPushForegroundServiceManager
import io.element.android.libraries.push.test.test.FakePushHandler
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.libraries.pushproviders.api.PushHandler
@ -29,7 +29,6 @@ import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.RobolectricTestRunner
import kotlin.time.Duration
@RunWith(RobolectricTestRunner::class)
class VectorFirebaseMessagingServiceTest {
@ -81,11 +80,11 @@ class VectorFirebaseMessagingServiceTest {
@Test
fun `test pushHandler returning true locks and does not unlock the wakelock so it continues running`() = runTest {
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
val unlockLambda = lambdaRecorder<Unit> { }
val lockLambda = lambdaRecorder<Boolean> { true }
val unlockLambda = lambdaRecorder<Boolean> { true }
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
pushHandler = FakePushHandler(handleResult = { _, _ -> true }),
pushHandlingWakeLock = FakePushHandlingWakeLock(
pushHandlingWakeLock = FakeFetchPushForegroundServiceManager(
lock = lockLambda,
unlock = unlockLambda
)
@ -113,11 +112,11 @@ class VectorFirebaseMessagingServiceTest {
@Test
fun `test pushHandler returning false locks and unlocks the wakelock early`() = runTest {
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
val unlockLambda = lambdaRecorder<Unit> { }
val lockLambda = lambdaRecorder<Boolean> { true }
val unlockLambda = lambdaRecorder<Boolean> { true }
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
pushHandler = FakePushHandler(handleResult = { _, _ -> false }),
pushHandlingWakeLock = FakePushHandlingWakeLock(
pushHandlingWakeLock = FakeFetchPushForegroundServiceManager(
lock = lockLambda,
unlock = unlockLambda
)
@ -145,11 +144,11 @@ class VectorFirebaseMessagingServiceTest {
@Test
fun `test pushHandler with a remote message with normal priority won't lock the wakelock`() = runTest {
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
val unlockLambda = lambdaRecorder<Unit> { }
val lockLambda = lambdaRecorder<Boolean> { true }
val unlockLambda = lambdaRecorder<Boolean> { true }
val vectorFirebaseMessagingService = createVectorFirebaseMessagingService(
pushHandler = FakePushHandler(handleResult = { _, _ -> false }),
pushHandlingWakeLock = FakePushHandlingWakeLock(
pushHandlingWakeLock = FakeFetchPushForegroundServiceManager(
lock = lockLambda,
unlock = unlockLambda
)
@ -186,14 +185,14 @@ class VectorFirebaseMessagingServiceTest {
private fun TestScope.createVectorFirebaseMessagingService(
firebaseNewTokenHandler: FirebaseNewTokenHandler = FakeFirebaseNewTokenHandler(),
pushHandler: PushHandler = FakePushHandler(),
pushHandlingWakeLock: FakePushHandlingWakeLock = FakePushHandlingWakeLock(),
pushHandlingWakeLock: FakeFetchPushForegroundServiceManager = FakeFetchPushForegroundServiceManager(),
): VectorFirebaseMessagingService {
return VectorFirebaseMessagingService().apply {
this.firebaseNewTokenHandler = firebaseNewTokenHandler
this.pushParser = FirebasePushParser()
this.pushHandler = pushHandler
this.coroutineScope = this@createVectorFirebaseMessagingService
this.pushHandlingWakeLock = pushHandlingWakeLock
this.fetchPushForegroundServiceManager = pushHandlingWakeLock
}
}
}

View file

@ -14,7 +14,7 @@ import dev.zacsweers.metro.Inject
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.di.annotations.AppCoroutineScope
import io.element.android.libraries.push.api.push.PushHandlingWakeLock
import io.element.android.libraries.push.api.push.FetchPushForegroundServiceManager
import io.element.android.libraries.pushproviders.api.PushHandler
import io.element.android.libraries.pushproviders.unifiedpush.registration.EndpointRegistrationHandler
import io.element.android.libraries.pushproviders.unifiedpush.registration.RegistrationResult
@ -38,7 +38,7 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
@Inject lateinit var newGatewayHandler: UnifiedPushNewGatewayHandler
@Inject lateinit var removedGatewayHandler: UnifiedPushRemovedGatewayHandler
@Inject lateinit var endpointRegistrationHandler: EndpointRegistrationHandler
@Inject lateinit var pushHandlingWakeLock: PushHandlingWakeLock
@Inject lateinit var fetchPushForegroundServiceManager: FetchPushForegroundServiceManager
@AppCoroutineScope
@Inject lateinit var coroutineScope: CoroutineScope
@ -59,8 +59,8 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
* @param instance connection, for multi-account
*/
override fun onMessage(context: Context, message: PushMessage, instance: String) {
// Acquire wakelock to ensure the device stays awake while we handle the push and schedule and run the work
pushHandlingWakeLock.lock()
// Start the foreground service to ensure the device stays awake while we handle the push and schedule and run the work.
fetchPushForegroundServiceManager.start()
Timber.tag(loggerTag.value).d("New message, decrypted: ${message.decrypted}")
coroutineScope.launch {
@ -71,16 +71,16 @@ class VectorUnifiedPushMessagingReceiver : MessagingReceiver() {
providerInfo = "${UnifiedPushConfig.NAME} - $instance",
data = String(message.content),
)
pushHandlingWakeLock.unlock()
fetchPushForegroundServiceManager.stop()
} else {
val handled = pushHandler.handle(
pushData = pushData,
providerInfo = "${UnifiedPushConfig.NAME} - $instance",
)
// If we failed to handle the push, we should release the wakelock early to avoid keeping the device awake for too long.
// If we failed to handle the push, we should stop the foreground service early to avoid keeping the device awake for too long.
if (!handled) {
pushHandlingWakeLock.unlock()
fetchPushForegroundServiceManager.stop()
}
}
}

View file

@ -18,7 +18,7 @@ import io.element.android.libraries.matrix.test.AN_EVENT_ID
import io.element.android.libraries.matrix.test.AN_EXCEPTION
import io.element.android.libraries.matrix.test.A_ROOM_ID
import io.element.android.libraries.matrix.test.A_SECRET
import io.element.android.libraries.push.test.push.FakePushHandlingWakeLock
import io.element.android.libraries.push.test.push.FakeFetchPushForegroundServiceManager
import io.element.android.libraries.push.test.test.FakePushHandler
import io.element.android.libraries.pushproviders.api.PushData
import io.element.android.libraries.pushproviders.api.PushHandler
@ -39,7 +39,6 @@ import org.unifiedpush.android.connector.FailedReason
import org.unifiedpush.android.connector.data.PublicKeySet
import org.unifiedpush.android.connector.data.PushEndpoint
import org.unifiedpush.android.connector.data.PushMessage
import kotlin.time.Duration
@RunWith(RobolectricTestRunner::class)
class VectorUnifiedPushMessagingReceiverTest {
@ -106,13 +105,13 @@ class VectorUnifiedPushMessagingReceiverTest {
fun `pushHandler returning true locks the wake lock but does not unlock it so it continues to run`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val pushHandlerResult = lambdaRecorder<PushData, String, Boolean> { _, _ -> true }
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
val unlockLambda = lambdaRecorder<Unit> { }
val lockLambda = lambdaRecorder<Boolean> { true }
val unlockLambda = lambdaRecorder<Boolean> { true }
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver(
pushHandler = FakePushHandler(
handleResult = pushHandlerResult
),
pushHandlingWakeLock = FakePushHandlingWakeLock(
pushHandlingWakeLock = FakeFetchPushForegroundServiceManager(
lock = lockLambda,
unlock = unlockLambda,
),
@ -133,13 +132,13 @@ class VectorUnifiedPushMessagingReceiverTest {
fun `pushHandler returning false locks and unlocks the wakelock early`() = runTest {
val context = InstrumentationRegistry.getInstrumentation().context
val pushHandlerResult = lambdaRecorder<PushData, String, Boolean> { _, _ -> false }
val lockLambda = lambdaRecorder<Duration, Unit> { _ -> }
val unlockLambda = lambdaRecorder<Unit> { }
val lockLambda = lambdaRecorder<Boolean> { true }
val unlockLambda = lambdaRecorder<Boolean> { true }
val vectorUnifiedPushMessagingReceiver = createVectorUnifiedPushMessagingReceiver(
pushHandler = FakePushHandler(
handleResult = pushHandlerResult
),
pushHandlingWakeLock = FakePushHandlingWakeLock(
pushHandlingWakeLock = FakeFetchPushForegroundServiceManager(
lock = lockLambda,
unlock = unlockLambda,
),
@ -264,7 +263,7 @@ class VectorUnifiedPushMessagingReceiverTest {
unifiedPushNewGatewayHandler: UnifiedPushNewGatewayHandler = FakeUnifiedPushNewGatewayHandler(),
endpointRegistrationHandler: EndpointRegistrationHandler = EndpointRegistrationHandler(),
removedGatewayHandler: UnifiedPushRemovedGatewayHandler = UnifiedPushRemovedGatewayHandler { lambdaError() },
pushHandlingWakeLock: FakePushHandlingWakeLock = FakePushHandlingWakeLock(),
pushHandlingWakeLock: FakeFetchPushForegroundServiceManager = FakeFetchPushForegroundServiceManager(),
): VectorUnifiedPushMessagingReceiver {
return VectorUnifiedPushMessagingReceiver().apply {
this.pushParser = unifiedPushParser
@ -277,7 +276,7 @@ class VectorUnifiedPushMessagingReceiverTest {
this.removedGatewayHandler = removedGatewayHandler
this.endpointRegistrationHandler = endpointRegistrationHandler
this.coroutineScope = this@createVectorUnifiedPushMessagingReceiver
this.pushHandlingWakeLock = pushHandlingWakeLock
this.fetchPushForegroundServiceManager = pushHandlingWakeLock
}
}
}