'How to programmatically connect to Wifi with no internet Android (Xamarin)

My app needs to use a wifi network (without internet, thus android avoids it by default). And I want it to be simple to use (like Grandma doesn't have to be manually changing settings). This is just an personal IoT device so I want to use ConnectivityManager.BindProcessToNetwork(Android.Net.Network network). How do I get the Android.Net.Network associated with the currently connected wifi network so I can use BindProcessToNetwork?

//c#(Xamarin)
//my rudimentary attempt to get the connected wifi network:

var networks = ConnectivityManager.GetAllNetworks();
foreach (Network network in networks) {
                NetworkCapabilities networkCability =ConnectivityManager.GetNetworkCapabilities(network);

                if (networkCability.HasTransport(TransportType.Wifi))
                {
                    currentWifiNetwork = network;  // this is never reached
                }
}
ConnectivityManager.BindProcessToNetwork( currentWifiNetwork );

Is there not a distinct Network object for all the phones currently in use WiFi, cellular, etc... networks?

This blog got me close: https://android-developers.googleblog.com/2016/07/connecting-your-app-to-wi-fi-device.html.

A binding socket method would work too (except the wifi network isn't an available network until the use network without internet box is checked). I just need to the App to use urls that are on port 8080 via the wifi. I want to avoid having to manually telling Android to "use the network with no internet". Cheers

Update

When I run this, there are only two Networks returned by ConnectivityManager.GetAllNetworks(), and looking at them in the debugger, one is the Cellular network with internet and mms, and the other is another Cellular network without internet and mms. So no ConnectivityManager.GetAllNetworks() doesn't get the wifi network as it appears android won't even add the wifi network unless it has internet! If the phones data is disabled Android will switch and use the internet-less wifi for all traffic (without having to check use the network anyways box).So their must be a way to get the WiFi network bound to the app! or...

How does one programmatically check the use network anyways box!?

I have not seen a solution to this. Just a bunch of open questions all over the web. At this rate I might just use dnsmasq on the iot device and a spoofing web server to make android think it has internet. I also see that API 29 has NetworkBuilder and that you can specify a request for a WiFi network without internet capabilities...but I need lower API support.



Solution 1:[1]

Update: Android now supports requesting local only wifi networks (networks without the NetworkCapabilities#NET_CAPABILITY_INTERNET capability) check out : https://developer.android.com/reference/android/net/wifi/WifiNetworkSpecifier.Builder

Original Answer:

This is the solution that I came up with (for Api 28 ). It prioritizes WiFi over 4G/5G (Data) regardless of internet capability via a NetworkRequest, and then allows for both internet over 4G/5G (Data) and the app to use its local WiFi services:

public static WifiManager WifiManager { get; } = (WifiManager)Android.App.Application.Context.GetSystemService(Context.WifiService);
public static ConnectivityManager ConnectivityManager { get; set; } = (ConnectivityManager)Android.App.Application.Context.GetSystemService(Context.ConnectivityService);

 public bool ConnectToWifi(string ssid, string password, bool previouslyConnected = true)
        {

            if (!WifiManager.IsWifiEnabled)
                WifiManager.SetWifiEnabled(true); //turn on wifi if not on

            var formattedSsid = $"\"{ssid}\"";
            var formattedPassword = $"\"{password}\"";

            var wifiConfig = new WifiConfiguration
            {
                Ssid = formattedSsid,
                PreSharedKey = formattedPassword,
                Priority = 0

            };

            _NetworkId = WifiManager.AddNetwork(wifiConfig);

            WifiManager.Disconnect();
            bool enableNetwork = WifiManager.EnableNetwork(_NetworkId, true);

 
            NetworkRequest.Builder builder = new NetworkRequest.Builder(); //request that WiFi be prioritized over the 4G internet capable network.
            builder.AddTransportType(TransportType.Wifi);
            ConnectivityManager.RequestNetwork(builder.Build(), new BindNetworkCallBack ());


            return enableNetwork;

        }

This call back then binds the appropriate WIFI network to the app(process)! Allowing for the user to both simultaneously use the app with the local server over WIFI, and other apps that still require internet, as Android OS can allow other processes to access the internet via the 4G/5G data connection!

 public class BindNetworkCallBack : ConnectivityManager.NetworkCallback
    {
      public override void OnAvailable(Network network)
        {
            if (WifiManager.ConnectionInfo.BSSID == NetworkBSSID) /* 
The only way on Android (API 28+) to check if the acquired network is
the one you want is to use the BSSID (MAC address) of the network. 
You can omit the if statement if you want to presume the acquired network is correct/ 
cannot know the MAC address...
*/
            {
                try
                {
                    ConnectivityManager.BindProcessToNetwork(network);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(@"\tERROR Unable to Bind process to network {0}", ex.Message);
                }

            }

        }
    }

"bindProcessToNetwork Binds the current process to network. All Sockets created in the future (and not explicitly bound via a bound SocketFactory from Network.getSocketFactory()) will be bound to network. All host name resolutions will be limited to network as well." - Android Docs

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1