Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After connecting to WiFi programmatically, disconnects a few seconds later

Tags:

android

I am having an issue with switching wifi networks on the users behalf. We have an IoT device that we need to connect to in order to set it up. Using the WifiManager and ConnectivityManager, I can make the connection and even make a REST call to it, but it changes back after about 10 seconds. I don’t understand why. There are a couple of strange log lines from the device that I am sure are related but I don’t know how to fix, mainly:

02-12 15:36:13.441 E/WifiConfigManager: UID 10356 does not have permission to update configuration "REDACTED"NONE
02-12 15:36:13.442 I/WifiStateMachine: connectToUserSelectNetwork Allowing uid 10356 with insufficient permissions to connect=90
...
02-12 15:36:13.470 E/WifiVendorHal: stopRssiMonitoring(l.2156) failed {.code = ERROR_NOT_AVAILABLE, .description = }
02-12 15:36:13.470 W/WifiConfigManager: Looking up network with invalid networkId -1
...
02-12 15:36:16.882 D/WifiStateMachine: NETWORK_STATUS_UNWANTED_VALIDATION_FAILED
...
02-12 15:36:17.041 D/WifiPermissionsUtil: Denied: no location permission

There is a lot about permissions in there, but I think I am asking for them all:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

And I ask for the RuntimePermission for location since I am testing this on a device with SDK 27. What’s more is that it does let me connect, but just changes it’s mind few seconds later.

like image 765
Joel Shea Avatar asked Feb 12 '18 15:02

Joel Shea


2 Answers

The issue is caused when you try to scan for networks with WifiManager.startScan() since Android has changed its permission model. You require to check for the runtime permission to access the coarse/fine location and you also have to check if the location provider (GPS) is switched ON. Otherwise you won't get any location updates. In your case, the location updates contain also wifi SSIDs, which you have scanned for.

Also note, that you have to check, if the user has NOT connected to that device previously via system settings, because it is not permitted by an app to connect to user defined access points from within an app. To allow the app to connect again in behalf of the user, the user must delete/forget that network in the system settings. If you don't check that, you will get an supplicant authentication error from the subsystem.

Another notes to IoT Wifi devices: they are mostly "headless" access points, i.e. have no internet. Many Android devices have the issue, that they require a valid captive portal. See this issue: https://android.stackexchange.com/questions/130265/stay-connected-to-specific-wifi-which-has-no-internet

To solve this issue, you must connect with a static IP address. However, Android does not offer an API to set this programmatically. So you must use reflection as stated here: Set static IP and gateway programmatically in Android 6.x (Marshmallow)

like image 131
Denis Loh Avatar answered Nov 04 '22 06:11

Denis Loh


I've had a similar experience when trying to connect to an existing WiFi configuration, and I found Denis' answer very helpful. The following represents my findings on a Nexus 5x running Android 8.1.0.

When things work:

In order to maintain a persistent connection to a WiFi network which doesn't have internet access, you either need to call:

  • (a) bindToProcess() on that network, which tells your app to route all of its traffic over this network despite the lack of internet connection, or

  • (b) you can configure your HTTP client to use the desired network's socket factory.

If you do either of these with a WiFi configuration which your app created, the system doesn't disconnect you from the network due to lack of internet access.

When they don't:

If the WiFi configuration you are trying to connect to was not created by your app (based on the app's UID), you are still able to connect, and you can perform (a) or (b), but the system will disconnect you from that network after a short period of time. This seems like an Android bug to me. The system allows you to connect to a configuration you didn't create, but will report the following:

UID xxxxx does not have permission to update configuration

connectToUserSelectNetwork Allowing uid xxxxx with insufficient permissions to connect=16

Shortly after, it will disconnect you from your configured network, the exact same way it does if you don't perform (a) or (b).. This seems like a bug in the implementation of whatever part of the Android system kicks you off a network for not having internet access. Either you shouldn't be able to connect to that network in the first place, or it should allow you to persist a connection using (a) or (b).

The cases where a WiFi configuration is not considered to have been created by your app are both when the user manually configures that network via the system WiFi settings, or when you re-run the app in Android Studio. So if you're debugging, and you configure a network, then re-run the app, you won't be able to maintain a persistent connection to that previously configured network, because your UID will have changed.

Solution:

The actual solution is quite simple. You can just remove the previous WiFi configuration (even though you apparently don't have 'sufficient' permission to properly connect to it!)

// Find an existing network configuration with the SSID you're trying to connect to:
val configuration = wifiManager.configuredNetworks.firstOrNull { network -> network.SSID == "\"YOUR_SSID\"" }

// Remove the configuration
if (configuration != null) {
    wifiManager.removeNetwork(configuration.networkId)
}

Now you can create a new configuration, connect to it, and perform (a) or (b) and maintain a persistent network connection.

I've filed a bug report on the Android Issue Tracker: https://issuetracker.google.com/issues/123896447

like image 23
Tim Malseed Avatar answered Nov 04 '22 06:11

Tim Malseed