17

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.

rolandl
  • 1,769
  • 1
  • 25
  • 48

3 Answers3

8

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.

Tassadar
  • 1,861
  • 1
  • 15
  • 12
  • 1
    I would agree with your suggestion to target Android 9, but... "From 2 November 2020, app updates must target Android 10 (API level 29) or higher." (from https://developer.android.com/distribute/best-practices/develop/target-sdk) – Attila Tanyi Sep 20 '20 at 14:41
  • 1
    Yes. We are now stuck with an inferior user experience that's even broken on some phones. – Tassadar Sep 21 '20 at 10:26
  • Im in the exact same position - Only thing to add here is that for some reason if I grant `Display over other apps` permission in the app screen the api seems to work with the deprecated methods ([addNetwork()](https://developer.android.com/reference/android/net/wifi/WifiManager#addNetwork(android.net.wifi.WifiConfiguration)) and etc) and the whole flow connects – Blue Bot Jan 03 '21 at 15:00
  • Is this still open? @BlueBot I am unable to connect using AddNetwork as well :/ – Singhal2 Jan 29 '21 at 07:51
  • I think I implemented the new mechanism and it worked, honestly don't remember :/ @Singhal2 – Blue Bot Feb 02 '21 at 09:00
  • Hmm, I think https://stackoverflow.com/questions/63124728/connect-to-wifi-in-android-q-programmatically/65327716#65327716 is the problem. It's only on certain devices. – Singhal2 Feb 02 '21 at 10:08
3

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.

MungoRae
  • 1,912
  • 1
  • 16
  • 25
  • 3
    Thank you for your answer. I found this code on the internet but it does not work when I try it. Unfortunately, when I call the requestNetwork method, I can see a dialog saying "device to use with my application" with a progress bar. After some time, another dialog appears with the content "no devices found. Make sure that devices are turned on and available to connect". I never reach the onAvailable method. – rolandl Nov 08 '19 at 16:33
  • Yeh you are right. I tried it on my own setup. I got it to work from seeing an example in https://developer.android.com/reference/android/net/wifi/WifiNetworkSpecifier.Builder#build() where they specifically request `removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)`. I tried that and got it to reach the onAvailable method. However this was just using an emulator as I don't have a Q device. I'll update the answer – MungoRae Nov 08 '19 at 17:17
  • 5
    Device popup is taking too much time. Is there any way to reduce that ? – Khushbu Shah Jun 09 '20 at 09:54
  • 2
    This is very fragile code as it won't run with API 30 which is already out. Change == to >= – Matt Wolfe Oct 09 '20 at 05:43
1

I try lots of codes, including WifiManager.addNetworkSuggestions and ConnectivityManager.requestNetwork. Finally i found that, just call ConnectivityManager.unregisterNetworkCallback to restore user wifi connection. The only place that this code makes me dissatisfied, if the user saved WiFi A and WiFi B, then the system will automatically select a connection, not the one I specified.

leimenghao
  • 226
  • 1
  • 10