Better network connection observing

This commit is contained in:
topjohnwu 2020-09-27 21:21:38 -07:00
parent ee4db43136
commit 9bebe07d5a
7 changed files with 91 additions and 145 deletions

View File

@ -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) }
}
}
}

View File

@ -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<Network>()
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())
}
}
}

View File

@ -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<PowerManager>() ?: 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()
}
}
}
}

View File

@ -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<ConnectivityManager>()!!
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() }
}
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}
}
}

View File

@ -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<PowerManager>()!!
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))
}
}
}
}