Is the Android Wifi-API really so broken on Android 10+?

Viewed 256

I'm working on a Wifi auto connect feature and I am shocked how broken that API is.

I'm using now 5 different APIs and I still don't get it in a way the user would expect it.

I have a setting to enable wifi auto connection on Android 10+ I'll try this:

  1. Check if I hold the ACCESS_WIFI_STATE permission with:
    appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
    
    When I own the permission I go on with 2. if not I continue with 6.
  2. When I hold the permission I'll check on Android 11+ if from a previous run my wifi suggestion was saved wifiManager.networkSuggestions.isNotEmpty() if that is true I check which Wifi I'm currently connected with see step x. On lower levels I skip this and go to step 3
  3. I use the wifi suggestion API with the WifiNetworkSuggestion.Builder() and suggest the user my wifi with
    val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
    // Strage bug: On first usage the return value is always SUCCESS
    val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
    
  4. When the user accepts my suggestion I'm fine so far. I cannot verify on Android 10 if my suggestion was accepted. On Android 11 this is possible (see step 2). However I still don't know if the device actually connected with the wifi. So I go to step 7
  5. When the user rejected my suggestion I'm almost out of the game I can never ask the user again (well technically there is a well hidden feature where you can switch the permission, but no user will ever find it). On Android 10 I can just check if I'm connected with the right wifi, well at least theoretically (see step 7).
  6. On Android 11+ I can fallback to the Settings.ACTION_WIFI_ADD_NETWORKS intent which can always add Wifis (with the downside that the user can share the wifi password)
  7. I need to verify if the steps before worked and the user is actually connected to the desired wifi. To do so I already own the permissions ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. However the ConnectivityManager API don't return the actual SSID, that drives me crazy.
  8. If the user is not connected to the right wifi I would try to use the Settings.Panel.ACTION_WIFI action to let the user to connect to the right wifi

To summarize that:

  • I use the WifiNetworkSuggestion API
  • I fallback to Settings.ACTION_WIFI_ADD_NETWORKS where possible
  • I try to verify if the user is currently connected to the right wifi
  • I try to assist the user to conenct to the right wifi (or even turn wifi on) with the Settings.Panel.ACTION_WIFI action

Is that really such a mess or is there a easier way?


I'm currently accessing the SSID like this:

private val currentSsid: String?
    get() =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val wifiInfo = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.transportInfo as? WifiInfo
            wifiInfo?.ssid
        } else {
            wifiManager.connectionInfo?.ssid
        }

Based on Unable to get WIFI SSID using onCapabilitiesChanged in Android 12 I think that way I access the value is not supported. I get currently the value "<unknown ssid>".

1 Answers

Well just a half answer, but it might help anyway. Here is how I get the current SSID of the user (you need to hold the location permission):

fun updateSsid(networkCapabilities: NetworkCapabilities) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        currentSsid = (networkCapabilities.transportInfo as? WifiInfo)?.ssid?.trim('"')
    }
}

wifiCallback = when {
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
        object: ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
            override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
                updateSsid(networkCapabilities)
            }
        }
    }
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
        object: ConnectivityManager.NetworkCallback() {
            override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
                updateSsid(networkCapabilities)
            }
        }
    }
    else -> null
}

wifiCallback?.let {
    fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
        override fun onResume(owner: LifecycleOwner) {
            connectivityManager.registerNetworkCallback(request, it)
        }

        override fun onPause(owner: LifecycleOwner) {
            connectivityManager.unregisterNetworkCallback(it)
        }
    })
}

So basically aspects of your code need to check apis for the levels <10, 10, 11 and 12. What a mess.

This might be quiet handy if you want the user to pick the configured wifi:

fun showWifiDialog() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        try {
            fragment.startActivity(Intent(android.provider.Settings.Panel.ACTION_WIFI))
        } catch (e: Exception) {
            // ignored
        }
    }
}
Related