Add network constraints for fetching notifications with WorkManager (#6305)
* Add `isNetworkBlocked` and `isInAirGappedEnvironment` to `NetworkMonitor`. * Improve the DI of `SyncPendingNotificationsRequestBuilder` to simplify its usage. * Only update `isInAirGappedEnvironment` in `DefaultNetworkManager` if the current build is an enterprise one. * Add network constraints to `DefaultSyncPendingNotificationsRequestBuilder` based on the air-gapped status. * Add a feature flag to disable the new check, in case it doesn't work as expected.
This commit is contained in:
parent
1e85c02a59
commit
912b9168fd
15 changed files with 298 additions and 100 deletions
|
|
@ -13,18 +13,21 @@ package io.element.android.features.networkmonitor.impl
|
|||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.ContributesBinding
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import io.element.android.features.networkmonitor.api.NetworkMonitor
|
||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||
import io.element.android.libraries.core.meta.BuildMeta
|
||||
import io.element.android.libraries.di.annotations.AppCoroutineScope
|
||||
import io.element.android.libraries.di.annotations.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
|
|
@ -39,13 +42,13 @@ import java.util.concurrent.atomic.AtomicInteger
|
|||
@SingleIn(AppScope::class)
|
||||
class DefaultNetworkMonitor(
|
||||
@ApplicationContext context: Context,
|
||||
@AppCoroutineScope
|
||||
appCoroutineScope: CoroutineScope,
|
||||
@AppCoroutineScope appCoroutineScope: CoroutineScope,
|
||||
private val buildMeta: BuildMeta,
|
||||
) : NetworkMonitor {
|
||||
private val connectivityManager: ConnectivityManager = context.getSystemService(ConnectivityManager::class.java)
|
||||
private val blockedNetworkBlockedChecker = NetworkBlockedChecker(connectivityManager)
|
||||
|
||||
override fun isNetworkBlocked(): Boolean = blockedNetworkBlockedChecker.isNetworkBlocked()
|
||||
override val isNetworkBlocked = MutableStateFlow(NetworkBlockedChecker(connectivityManager).isNetworkBlocked())
|
||||
override val isInAirGappedEnvironment = MutableStateFlow(false)
|
||||
|
||||
override val connectivity: StateFlow<NetworkStatus> = callbackFlow {
|
||||
|
||||
|
|
@ -63,6 +66,27 @@ class DefaultNetworkMonitor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onBlockedStatusChanged(network: Network, blocked: Boolean) {
|
||||
Timber.d("Network ${network.networkHandle} blocked status changed: $blocked.")
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network is blocked, it means that Doze is preventing the app from using the network, even if it's available.
|
||||
isNetworkBlocked.value = blocked
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||
if (!buildMeta.isEnterpriseBuild) {
|
||||
// The air-gapped environment detection is only relevant for the enterprise build.
|
||||
return
|
||||
}
|
||||
|
||||
if (network.networkHandle == connectivityManager.activeNetwork?.networkHandle) {
|
||||
// If the network doesn't have the NET_CAPABILITY_VALIDATED capability, it means that the network is not able to reach the internet
|
||||
// (according to Google), which is a common case in air-gapped environments.
|
||||
isInAirGappedEnvironment.value = !networkCapabilities.capabilities.contains(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
if (activeNetworksCount.incrementAndGet() > 0) {
|
||||
trySendBlocking(NetworkStatus.Connected)
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import android.net.ConnectivityManager
|
|||
import android.net.NetworkInfo
|
||||
|
||||
/**
|
||||
* Helper to check if the active network in [ConnectivityManager] is blocked.
|
||||
* Helper to synchronously check if the active network in [ConnectivityManager] is blocked.
|
||||
*
|
||||
* This is extracted to its own class because it uses deprecated APIs (but the only ones that are reliable)
|
||||
* and we don't want to suppress deprecations everywhere.
|
||||
* and we don't want to suppress deprecations everywhere in the file this would be called.
|
||||
*/
|
||||
class NetworkBlockedChecker(
|
||||
private val connectivityManager: ConnectivityManager,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue