3

I'm developing a Raspberry Pi-based IoT device which hosts its own WiFi AP (no internet) and have successfully followed this Android Developers blog post from 2016 to connect to it and route traffic to it from my Android app, even when mobile data is enabled (i.e. via Network#getSocketFactory). (i.e. I'm NOT having the issue reported in this question: Send request over WiFi (without connection) even if Mobile data is ON (with connection) on Android M)

The problem now is that my Android 10 device (a Google Pixel) automatically disconnects from the network and switches to my home WiFi network (with internet) after a few minutes. This is with the app in the foreground, with an active web socket connection to a server-side app running on the Pi.

I could work around the problem by listening for network state changes in the app and forcibly reconnecting to my IoT network via WifiManager#enableNetwork, but this seems like a hacky solution and the connection would still be interrupted, leading to a poor user experience.

Another thought I had was to use WifiManager#disableNetwork to disable all other configured WiFi networks, thus preventing the phone from connecting to them. However the doc states that disabling networks created by other apps is not allowed.

Maintaining a connection to an IoT WiFi network with no net access seems to be a reasonable use case for an Android app that Google are (or were) aware of, but I'm struggling to piece together the current best practices for how to do this in 2020. I'm wondering if it's achievable via the newer WiFi suggestion APIs. However these sound like are even more restrictive to app developers and don't offer any guarantees about the WiFi network that will actually be joined.

user1405990
  • 806
  • 1
  • 8
  • 19

2 Answers2

0

You can bind Android to the WiFi Access Point it is connected to.

This can be done using ConnectivityManager's bindProcessToNetwork(network):

final ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);

NetworkRequest.Builder builder = new NetworkRequest.Builder();

//set the transport type to WIFI
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);

try {
    manager.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() {

                @Override
                public void onAvailable(Network network) {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        manager.bindProcessToNetwork(network);
                    } else {
                        ConnectivityManager.setProcessDefaultNetwork(network);
                    }
                    manager.unregisterNetworkCallback(this);
                }
            });

} catch (SecurityException e) {
    Log.e(TAG, e.getMessage());
}

This will also prevent Android from switching current connexion from WiFi AP without internet to Mobile data

matdev
  • 4,115
  • 6
  • 35
  • 56
  • I switched to calling `bindProcessToNetwork` rather than using the network's socket factory as suggested. Unfortunately the results are the same - it connects OK even with mobile data on but Android still switches off it and back to the home wifi network after less than a minute – user1405990 Jun 09 '20 at 22:25
  • well you've posted an answer based on using bindProcessToNetwork(), so I guess my solution helped you ? – matdev Jun 11 '20 at 08:33
0

Got to the bottom of it. The solution that worked for me for Android 10 was this one: https://stackoverflow.com/a/58770341/1405990

I.e. using WifiNetworkSpecifier and ConnectivityManager#requestNetwork:

        val builder: NetworkRequest.Builder = NetworkRequest.Builder()
        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        builder.setNetworkSpecifier(
            WifiNetworkSpecifier.Builder().apply {
                setSsid("IOTWifi")
                setWpa2Passphrase("iotwifipassword")
            }.build()
        )

        try {
            connectivityManager.requestNetwork(builder.build(), object : NetworkCallback() {
                override fun onAvailable(network: Network) {
                    connectivityManager.bindProcessToNetwork(network)
                }
            })
        } catch (e: SecurityException) {
            Timber.e(e)
        }

This will show a system dialog from which the user can (eventually) select the IoT wifi network to connect to it.

The key to have Android maintain a connection to the network is to not unregister the network callback from ConnectivityManager. Android 10 will not automatically switch back to one with internet access until you do so.

The user experience isn't great as the dialog title isn't too clear and because of the inexplicable delay in showing the matching WiFi network (which is already visible in old scan results). But at least a persistent connection to the IoT device on Android 10 is now possible.

user1405990
  • 806
  • 1
  • 8
  • 19