From 9bebe07d5a29761f37b11d583cf23af2fc230808 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Sun, 27 Sep 2020 21:21:38 -0700 Subject: [PATCH] Better network connection observing --- .../java/com/topjohnwu/magisk/core/Info.kt | 4 +- .../utils/net/LollipopNetworkObserver.kt | 19 ++++-- .../utils/net/MarshmallowNetworkObserver.kt | 53 ++++++++++++++++ .../{ => core}/utils/net/NetworkObserver.kt | 26 +++----- .../utils/net/PreLollipopNetworkObserver.kt | 22 ++++--- .../magisk/utils/net/Connectivity.kt | 49 --------------- .../utils/net/MarshmallowNetworkObserver.kt | 63 ------------------- 7 files changed, 91 insertions(+), 145 deletions(-) rename app/src/main/java/com/topjohnwu/magisk/{ => core}/utils/net/LollipopNetworkObserver.kt (66%) create mode 100644 app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt rename app/src/main/java/com/topjohnwu/magisk/{ => core}/utils/net/NetworkObserver.kt (51%) rename app/src/main/java/com/topjohnwu/magisk/{ => core}/utils/net/PreLollipopNetworkObserver.kt (51%) delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/net/Connectivity.kt delete mode 100644 app/src/main/java/com/topjohnwu/magisk/utils/net/MarshmallowNetworkObserver.kt diff --git a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt index bda2969d9..9bc2913d1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/core/Info.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/Info.kt @@ -3,9 +3,9 @@ package com.topjohnwu.magisk.core import androidx.databinding.ObservableBoolean import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.core.model.UpdateInfo +import com.topjohnwu.magisk.core.utils.net.NetworkObserver import com.topjohnwu.magisk.ktx.get import com.topjohnwu.magisk.utils.CachedValue -import com.topjohnwu.magisk.utils.net.NetworkObserver import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.ShellUtils.fastCmd import com.topjohnwu.superuser.internal.UiThreadHandler @@ -38,7 +38,7 @@ object Info { val isConnected by lazy { ObservableBoolean(false).also { field -> NetworkObserver.observe(get()) { - UiThreadHandler.run { field.set(it.isAvailable) } + UiThreadHandler.run { field.set(it) } } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/net/LollipopNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/LollipopNetworkObserver.kt similarity index 66% rename from app/src/main/java/com/topjohnwu/magisk/utils/net/LollipopNetworkObserver.kt rename to app/src/main/java/com/topjohnwu/magisk/core/utils/net/LollipopNetworkObserver.kt index 112ded03b..161bde696 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/net/LollipopNetworkObserver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/LollipopNetworkObserver.kt @@ -1,4 +1,4 @@ -package com.topjohnwu.magisk.utils.net +package com.topjohnwu.magisk.core.utils.net import android.annotation.TargetApi import android.content.Context @@ -6,11 +6,12 @@ import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest +import androidx.collection.ArraySet @TargetApi(21) open class LollipopNetworkObserver( context: Context, - callback: NetInfoCallback + callback: ConnectionCallback ): NetworkObserver(context, callback) { private val networkCallback = NetCallback() @@ -22,17 +23,27 @@ open class LollipopNetworkObserver( manager.registerNetworkCallback(request, networkCallback) } + @Suppress("DEPRECATION") + override fun getCurrentState() { + callback(manager.activeNetworkInfo?.isConnected ?: false) + } + override fun stopObserving() { manager.unregisterNetworkCallback(networkCallback) } private inner class NetCallback : ConnectivityManager.NetworkCallback() { + + private val activeList = ArraySet() + override fun onAvailable(network: Network) { - emit(Connectivity.create(manager, network)) + activeList.add(network) + callback(true) } override fun onLost(network: Network) { - emit(Connectivity.create(manager, network)) + activeList.remove(network) + callback(!activeList.isEmpty()) } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt new file mode 100644 index 000000000..d688ba3bf --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/MarshmallowNetworkObserver.kt @@ -0,0 +1,53 @@ +@file:Suppress("DEPRECATION") + +package com.topjohnwu.magisk.core.utils.net + +import android.annotation.TargetApi +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.net.NetworkCapabilities +import android.os.PowerManager +import androidx.core.content.getSystemService + +@TargetApi(23) +class MarshmallowNetworkObserver( + context: Context, + callback: ConnectionCallback +): LollipopNetworkObserver(context, callback) { + + private val receiver = IdleBroadcastReceiver() + + init { + val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED) + app.registerReceiver(receiver, filter) + } + + override fun stopObserving() { + super.stopObserving() + app.unregisterReceiver(receiver) + } + + override fun getCurrentState() { + callback(manager.getNetworkCapabilities(manager.activeNetwork) + ?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false) + } + + private inner class IdleBroadcastReceiver: BroadcastReceiver() { + + private fun Context.isIdleMode(): Boolean { + val pwm = getSystemService() ?: return true + val isIgnoringOptimizations = pwm.isIgnoringBatteryOptimizations(packageName) + return pwm.isDeviceIdleMode && !isIgnoringOptimizations + } + + override fun onReceive(context: Context, intent: Intent) { + if (context.isIdleMode()) { + callback(false) + } else { + getCurrentState() + } + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/net/NetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/NetworkObserver.kt similarity index 51% rename from app/src/main/java/com/topjohnwu/magisk/utils/net/NetworkObserver.kt rename to app/src/main/java/com/topjohnwu/magisk/core/utils/net/NetworkObserver.kt index 68152ff3a..ce0c9bad0 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/net/NetworkObserver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/NetworkObserver.kt @@ -1,40 +1,30 @@ -package com.topjohnwu.magisk.utils.net +package com.topjohnwu.magisk.core.utils.net import android.content.Context import android.net.ConnectivityManager import android.os.Build import androidx.core.content.getSystemService -typealias NetInfoCallback = (Connectivity) -> Unit +typealias ConnectionCallback = (Boolean) -> Unit -abstract class NetworkObserver protected constructor( +abstract class NetworkObserver( context: Context, - private val callback: NetInfoCallback + protected val callback: ConnectionCallback ) { - protected val context = context.applicationContext + protected val app: Context = context.applicationContext protected val manager = context.getSystemService()!! - protected var last = Connectivity.create(manager) - - init { - callback(last) - } - - protected open fun emit(current: Connectivity) { - if (last != current) - callback(current) - last = current - } protected abstract fun stopObserving() + protected abstract fun getCurrentState() companion object { - fun observe(context: Context, callback: NetInfoCallback): NetworkObserver { + fun observe(context: Context, callback: ConnectionCallback): NetworkObserver { return when (Build.VERSION.SDK_INT) { in 23 until Int.MAX_VALUE -> MarshmallowNetworkObserver(context, callback) in 21 until 23 -> LollipopNetworkObserver(context, callback) else -> PreLollipopNetworkObserver(context, callback) - } + }.apply { getCurrentState() } } } } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/net/PreLollipopNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/PreLollipopNetworkObserver.kt similarity index 51% rename from app/src/main/java/com/topjohnwu/magisk/utils/net/PreLollipopNetworkObserver.kt rename to app/src/main/java/com/topjohnwu/magisk/core/utils/net/PreLollipopNetworkObserver.kt index 754f19931..36f90e500 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/net/PreLollipopNetworkObserver.kt +++ b/app/src/main/java/com/topjohnwu/magisk/core/utils/net/PreLollipopNetworkObserver.kt @@ -1,34 +1,38 @@ @file:Suppress("DEPRECATION") -package com.topjohnwu.magisk.utils.net +package com.topjohnwu.magisk.core.utils.net import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.net.ConnectivityManager +import androidx.core.net.ConnectivityManagerCompat class PreLollipopNetworkObserver( context: Context, - callback: NetInfoCallback + callback: ConnectionCallback ): NetworkObserver(context, callback) { private val receiver = ConnectivityBroadcastReceiver() init { - val filter = IntentFilter() - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION) - context.registerReceiver(receiver, filter) + val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) + app.registerReceiver(receiver, filter) } override fun stopObserving() { - context.unregisterReceiver(receiver) + app.unregisterReceiver(receiver) + } + + override fun getCurrentState() { + callback(manager.activeNetworkInfo?.isConnected ?: false) } private inner class ConnectivityBroadcastReceiver: BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - emit(Connectivity.create(manager)) + override fun onReceive(context: Context?, intent: Intent) { + val info = ConnectivityManagerCompat.getNetworkInfoFromBroadcast(manager, intent) + callback(info?.isConnected ?: false) } } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/net/Connectivity.kt b/app/src/main/java/com/topjohnwu/magisk/utils/net/Connectivity.kt deleted file mode 100644 index 8d0a05d82..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/net/Connectivity.kt +++ /dev/null @@ -1,49 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.topjohnwu.magisk.utils.net - -import android.net.ConnectivityManager -import android.net.Network -import android.net.NetworkInfo -import android.net.NetworkInfo.DetailedState -import androidx.annotation.RequiresApi - -// Recreate NetworkInfo with Kotlin data class -data class Connectivity( - val state : NetworkInfo.State = NetworkInfo.State.DISCONNECTED, - val detailedState : DetailedState = DetailedState.IDLE, - val type : Int = -1, - val subType : Int = -1, - val isAvailable : Boolean = false, - val isFailover : Boolean = false, - val isRoaming : Boolean = false, - val typeName : String = "NONE", - val subTypeName : String = "NONE", - val reason : String? = null, - val extraInfo : String? = null -) { - private constructor(info: NetworkInfo) : this( - info.state, - info.detailedState, - info.type, - info.subtype, - info.isAvailable, - info.isFailover, - info.isRoaming, - info.typeName, - info.subtypeName, - info.reason, - info.extraInfo - ) - - companion object { - fun create(manager: ConnectivityManager): Connectivity { - return manager.activeNetworkInfo?.let { Connectivity(it) } ?: Connectivity() - } - - @RequiresApi(21) - fun create(manager: ConnectivityManager, network: Network): Connectivity { - return manager.getNetworkInfo(network)?.let { Connectivity(it) } ?: Connectivity() - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/net/MarshmallowNetworkObserver.kt b/app/src/main/java/com/topjohnwu/magisk/utils/net/MarshmallowNetworkObserver.kt deleted file mode 100644 index 8dba6d010..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/net/MarshmallowNetworkObserver.kt +++ /dev/null @@ -1,63 +0,0 @@ -@file:Suppress("DEPRECATION") - -package com.topjohnwu.magisk.utils.net - -import android.annotation.TargetApi -import android.content.BroadcastReceiver -import android.content.Context -import android.content.Intent -import android.content.IntentFilter -import android.net.NetworkInfo -import android.os.PowerManager -import androidx.core.content.getSystemService - -@TargetApi(23) -class MarshmallowNetworkObserver( - context: Context, - callback: NetInfoCallback -): LollipopNetworkObserver(context, callback) { - - private val idleReceiver = IdleBroadcastReceiver() - - init { - val filter = IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED) - context.registerReceiver(idleReceiver, filter) - } - - override fun stopObserving() { - super.stopObserving() - context.unregisterReceiver(idleReceiver) - } - - override fun emit(current: Connectivity) { - val typeChanged = last.type != current.type - val wasConnected = last.state == NetworkInfo.State.CONNECTED - val isDisconnected = current.state == NetworkInfo.State.DISCONNECTED - val isNotIdle = current.detailedState != NetworkInfo.DetailedState.IDLE - if (typeChanged && wasConnected && isDisconnected && isNotIdle) { - super.emit(current) - super.emit(last) - last = current - } else { - super.emit(current) - } - } - - private inner class IdleBroadcastReceiver: BroadcastReceiver() { - - private fun isIdleMode(context: Context): Boolean { - val packageName = context.packageName - val manager = context.getSystemService()!! - val isIgnoringOptimizations = manager.isIgnoringBatteryOptimizations(packageName) - return manager.isDeviceIdleMode && !isIgnoringOptimizations - } - - override fun onReceive(context: Context, intent: Intent) { - if (isIdleMode(context)) { - emit(Connectivity()) - } else { - emit(Connectivity.create(manager)) - } - } - } -}