Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 10 / API 29 : how to connect the phone to a configured network?

I am working on an application in which one the user has to follow these steps :

  1. connect the phone to wifi ;
  2. connect the phone to a dedicated hotspot from a connected object.

When the user is connected to the dedicated hotspot of the connected object, the application does some HTTP requests in order to configure it. Then, I would like to reconnect automatically the application to the global wifi of step 1.

From API 21 to API 28 this feature is very easy to implement because I know the SSID I want to reconnect the phone too. It can be done with a few line of code:

private fun changeCurrentWifiNetworkLegacy(ssidToConnect: String) {
    val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager

    var ssidIdentifier: Int? = null

    wifiManager.configuredNetworks?.forEach { config ->
        Log.d("SSID", config.SSID)

        if (config.SSID == "\"${ssidToConnect}\"") {
            ssidIdentifier = config.networkId
        }
    }

    ssidIdentifier?.let { id ->
        wifiManager.enableNetwork(id, true)
    }
}

On API 29 this simple code does not work anymore according to this article: https://developer.android.com/about/versions/10/privacy/changes#configure-wifi

According to the article, now, I should use 2 classes: WifiNetworkSpecifier and/or WifiNetworkSuggestion.

Unfortunately, I cannot find a working example using these classes in order to connect the user to a previous configured SSID.

Does someone already achieve that?

Thank you in advance for your help.

like image 292
rolandl Avatar asked Nov 08 '19 15:11

rolandl


2 Answers

Just in case any poor soul encounters this, it's completely possible the API is just broken on your device. On my OnePlus 5, Android 10, Build 200513, this happens:

  1. Call requestNetwork. Doesn't matter whether I use the Listener or PendingIntent version
  2. The OS finds the network, connects, onAvailable and friends are called
  3. OS immediately disconnects. I can see "App released request, cancelling NetworkRequest" in logcat
  4. This is however, not true - the request was not cancelled, which Android relizes, and starts the process of connecting to the network again. Go to 2. and repeats forever.

Created an Android bug for this: https://issuetracker.google.com/issues/158344328

Additionally, you can get the OS into state when it will no longer show the "Device to use with " dialog if you don't terminate your app and the dialogs in the correct order, and only reboot helps.

Just save yourself the trouble, target Android 9 and use the WifiManager APIs (that are helpfully broken if you target 10). It even has better user experience than the new requestNetwork APIs.

like image 155
Tassadar Avatar answered Sep 24 '22 23:09

Tassadar


You can set which network to connect to with the following code:

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
    val cm = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkRequest = NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .setNetworkSpecifier(
            WifiNetworkSpecifier.Builder()
                .setSsid("My ssid")
                .build()
        )
        .build()
    cm.requestNetwork(networkRequest, object: ConnectivityManager.NetworkCallback() {
        override fun onUnavailable() {
            Log.d("TEST", "Network unavailable")
        }

        override fun onAvailable(network: Network) {
            Log.d("TEST", "Network available")
        }
    })
}

This uses the ConnectivityManager's networkRequest method to request a network with a specific SSID.

This method requires the caller to hold either the Manifest.permission.CHANGE_NETWORK_STATE permission or the ability to modify system settings as determined by Settings.System.canWrite(Context).

See the NetworkCallback class for more documentation about what info you can get.

(edit: Missed adding Transport type.)

Further edit: I needed to use .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) to get it to work properly. Because in WifiNetworkSpecifier

can only be used to request a local wifi network (i.e no internet capability)

According to the docs

This gives me a request for devices popup, but then eventually shows me the Wifi network I asked for.

like image 27
MungoRae Avatar answered Sep 20 '22 23:09

MungoRae