Skip to content

Commit 9c6b5a0

Browse files
TheNodikuamanet
authored andcommitted
Android 12: Fix retrieval without using WiFiManager.getConnectionInfo
1 parent 38b77d3 commit 9c6b5a0

File tree

4 files changed

+45
-30
lines changed

4 files changed

+45
-30
lines changed

src/main/java/net/kuama/wifiMonitor/WifiMonitor.kt

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ class WifiMonitor private constructor(
2121
private var listener: WifiListener? = null
2222
fun listener(listener: WifiListener) = apply { this.listener = listener }
2323

24-
private fun listenerBuilder(): WifiListener =
24+
private fun listenerBuilder(wifiManager: WifiManager): WifiListener =
2525
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
26-
AndroidQWifiListener()
26+
AndroidQWifiListener(wifiManager)
2727
} else {
28-
BeforeAndroidQWifiListener()
28+
BeforeAndroidQWifiListener(wifiManager)
2929
}
3030

3131
private var wifiManager: WifiManager? = null
@@ -38,12 +38,15 @@ class WifiMonitor private constructor(
3838

3939
private fun permissionCheckerBuilder(context: Context): PermissionChecker = PermissionChecker.Builder().context(context).build()
4040

41-
fun build(context: Context): WifiMonitor = WifiMonitor(
42-
context = context,
43-
listener = listener ?: listenerBuilder(),
44-
wifiManager = wifiManager ?: wifiManagerBuilder(context),
45-
permissionChecker = permissionChecker ?: permissionCheckerBuilder(context),
46-
)
41+
fun build(context: Context): WifiMonitor {
42+
val wifiManager = wifiManager ?: wifiManagerBuilder(context)
43+
return WifiMonitor(
44+
context = context,
45+
listener = listener ?: listenerBuilder(wifiManager),
46+
wifiManager = wifiManager,
47+
permissionChecker = permissionChecker ?: permissionCheckerBuilder(context),
48+
)
49+
}
4750
}
4851

4952
/**
@@ -62,7 +65,8 @@ class WifiMonitor private constructor(
6265
State.DISCONNECTED
6366
)
6467
WifiManager.WIFI_STATE_ENABLED -> {
65-
val connectionInfo = wifiInfo ?: @Suppress("DEPRECATION") wifiManager.connectionInfo
68+
val connectionInfo = wifiInfo ?: return@map WifiStatus(State.UNKNOWN)
69+
6670
WifiStatus(
6771
state = if (isFineLocationAccessGranted) State.CONNECTED else State.CONNECTED_MISSING_FINE_LOCATION_PERMISSION,
6872
ssid = connectionInfo.ssid,

src/main/java/net/kuama/wifiMonitor/implementation/AndroidQWifiListener.kt

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import android.content.Context
55
import android.net.ConnectivityManager
66
import android.net.Network
77
import android.net.NetworkCapabilities
8-
import android.net.NetworkRequest
98
import android.net.wifi.WifiInfo
9+
import android.net.wifi.WifiManager
1010
import android.os.Build
1111
import kotlinx.coroutines.ExperimentalCoroutinesApi
1212
import kotlinx.coroutines.channels.awaitClose
@@ -24,31 +24,41 @@ import net.kuama.wifiMonitor.WifiListener
2424
* From now on, to observe connectivity changes we should register a [ConnectivityManager.NetworkCallback] implementation.
2525
*/
2626
@TargetApi(Build.VERSION_CODES.Q)
27-
internal class AndroidQWifiListener : WifiListener {
27+
internal class AndroidQWifiListener(private val wifiManager: WifiManager) : WifiListener {
2828
@OptIn(ExperimentalCoroutinesApi::class)
2929
override fun listen(context: Context): Flow<WifiInfo?> = callbackFlow {
30-
val callback = object : ConnectivityManager.NetworkCallback() {
31-
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
32-
super.onCapabilitiesChanged(network, networkCapabilities)
30+
// Android 12 requires a new flag to receive SSID data, but flag-based constructor has been introduced only in SDK 31.
31+
val networkCallback = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
32+
object : ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
33+
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
34+
super.onCapabilitiesChanged(network, networkCapabilities)
3335

34-
val wifiInfo = networkCapabilities.transportInfo as WifiInfo
35-
@Suppress("BlockingMethodInNonBlockingContext")
36-
trySendBlocking(wifiInfo)
37-
}
38-
}
36+
trySendBlocking(networkCapabilities.transportInfo as? WifiInfo?)
37+
}
38+
} else
39+
object : ConnectivityManager.NetworkCallback() {
40+
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
41+
super.onCapabilitiesChanged(network, networkCapabilities)
3942

40-
val request = NetworkRequest.Builder()
41-
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
42-
.build()
43+
// There's a weird behavior in SDK 30 where transportInfo is null even when connected to the WiFi.
44+
// Fallback to soon-to-deprecate connectionInfo if we're connected to a WiFi network.
45+
@Suppress("DEPRECATION")
46+
val wifiInfo =
47+
(networkCapabilities.transportInfo as? WifiInfo?)
48+
?: if (wifiManager.wifiState == WifiManager.WIFI_STATE_ENABLED) wifiManager.connectionInfo else null
49+
50+
trySendBlocking(wifiInfo)
51+
}
52+
}
4353

4454
val connectivityManager = context.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
4555

4656
// Register the callback on network changes.
47-
connectivityManager.registerNetworkCallback(request, callback)
57+
connectivityManager.registerDefaultNetworkCallback(networkCallback)
4858

4959
// Wait for flow cancellation, then unregister the callback.
5060
awaitClose {
51-
connectivityManager.unregisterNetworkCallback(callback)
61+
connectivityManager.unregisterNetworkCallback(networkCallback)
5262
}
5363
}
5464
}

src/main/java/net/kuama/wifiMonitor/implementation/BeforeAndroidQWifiListener.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import android.content.Context
55
import android.content.Intent
66
import android.content.IntentFilter
77
import android.net.wifi.WifiInfo
8+
import android.net.wifi.WifiManager
89
import kotlinx.coroutines.ExperimentalCoroutinesApi
910
import kotlinx.coroutines.channels.awaitClose
1011
import kotlinx.coroutines.channels.trySendBlocking
@@ -17,13 +18,13 @@ import net.kuama.wifiMonitor.WifiListener
1718
*
1819
* Before Android Q we can register an intent receiver to observe wifi state changes.
1920
*/
20-
internal class BeforeAndroidQWifiListener : WifiListener {
21+
internal class BeforeAndroidQWifiListener(private val wifiManager: WifiManager) : WifiListener {
2122
@OptIn(ExperimentalCoroutinesApi::class)
2223
override fun listen(context: Context): Flow<WifiInfo?> = callbackFlow {
2324
val receiver = object : BroadcastReceiver() {
2425
override fun onReceive(context: Context?, intent: Intent?) {
25-
@Suppress("BlockingMethodInNonBlockingContext")
26-
trySendBlocking(null)
26+
@Suppress("DEPRECATION")
27+
trySendBlocking(if (wifiManager.wifiState == WifiManager.WIFI_STATE_ENABLED) wifiManager.connectionInfo else null)
2728
}
2829
}
2930

src/test/java/net/kuama/wifiMonitor/WifiMonitorTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ import io.mockk.mockk
77
import kotlinx.coroutines.ExperimentalCoroutinesApi
88
import kotlinx.coroutines.flow.first
99
import kotlinx.coroutines.flow.flowOf
10-
import kotlinx.coroutines.test.runBlockingTest
10+
import kotlinx.coroutines.test.runTest
1111
import net.kuama.wifiMonitor.data.WifiStatus
1212
import org.junit.Test
1313

1414
@OptIn(ExperimentalCoroutinesApi::class)
1515
class WifiMonitorTest {
1616
@Test
1717
fun `it sends a wifi status disconnected in the flow when wifi manager is receiving a disabled state`() =
18-
runBlockingTest {
18+
runTest {
1919
// Arrange
2020
val context = mockk<Context>(relaxed = true)
2121
val wifiManager = mockk<WifiManager>(relaxed = true)

0 commit comments

Comments
 (0)