0

I am currently working on a project in which I have an Android app that needs to connect to an IoT device and send it some information. The IoT device uses a SAMD21 microcontroller with an ATWINC1500 wifi module. The ATWINC1500 is capable of creating a wifi access point that I should be able to connect to from the Android device. It is not capable of wifi-direct (p2p) connections, and does not have any bluetooth capabilities. Currently, on the IoT device, I have it creating an access point and then spinning up a server to listen for incoming connections. Just for the sake of information, the wifi access point is currently open with no password or key.

My simplified program flow is this:

  1. On the Android device, scan for available wifi networks and find the one that matches the name of the access point that the IoT device created
  2. Connect to the IoT-device's access point
  3. Create a TcpClient and connect to the server on the IoT device (IP address of 192.168.1.1).
  4. Send some information to the IoT device.

On the Android side, I am using C# with Xamarin. I have a couple of different test devices: a 10" tablet running Android 10 and a 5" phone running Android 8.1. I've researched the Android documentation as well as several other Stack Overflow posts that go into detail about how to programmatically have the Android device connect to the wifi access point. Here are just a few of the other Stack Overflow posts that I have read:

  1. Connect To Wifi Android Q
  2. How to connect to WiFi programmatically
  3. How do I connect to a specific Wi-Fi network in Android programmatically?
  4. Android Q, programmatically connect to different WiFi AP for internet
  5. Android 10 / API 29 : how to connect the phone to a configured network?

Unfortunately, I have having some mixed results even after looking at all of these sources. It will occasionally work, but only a minority of the time. It is also complicated by the fact that Android has separate APIs for connecting to wifi depending on which version of Android is running on the device.

Here is my primary method to actually connect to the wifi access point:

public void ConnectToWifiNetwork(string ssid)
{
    if (Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.Q)
    {
        var wifi_network_specifier = (new WifiNetworkSpecifier.Builder()).SetSsid(ssid).Build();
        var network_request = (new NetworkRequest.Builder()).AddTransportType(TransportType.Wifi)
            .SetNetworkSpecifier(wifi_network_specifier).Build();
        connectivity_manager.RequestNetwork(network_request, network_callback);
    }
    else if (Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
    {
        //See if the desired network configuration already exists
        var configured_networks = wifi_manager.ConfiguredNetworks;
        var specified_network = configured_networks.Where(x => x.Ssid.Contains(ssid)).FirstOrDefault();
        int network_id = -1;

        //If no network configuration already exists....
        if (specified_network == null)
        {
            //Create a wifi configuration with the appropriate SSID
            specified_network = new WifiConfiguration();
            specified_network.Ssid = '"' + ssid + '"';
            specified_network.AllowedKeyManagement.Set((int)Android.Net.Wifi.KeyManagementType.None);

            //Add the configuration to the wifi manager's list of configured networks
            network_id = wifi_manager.AddNetwork(specified_network);
        }
        else
        {
            //Otherwise, if a network configuration does already exist, grab the network ID
            network_id = specified_network.NetworkId;
        }

        //If we have a valid network ID...
        if (network_id != -1)
        {
            //Let's attempt to connect to the network
            wifi_manager.Disconnect();
            wifi_manager.EnableNetwork(network_id, true);
            wifi_manager.Reconnect();

            //Now let's bind to the network we just connected to
            var network_request = (new NetworkRequest.Builder()).AddTransportType(TransportType.Wifi).Build();
            connectivity_manager.RequestNetwork(network_request, network_callback);
        }
    }
}

Please note that the variables connectivity_manager and wifi_manager are defined elsewhere in the code, and they are respectively of the types ConnectivityManager and WifiManager. Also, the network_callback variable is also defined elsewhere in the code, and it is of type WifiConnectorNetworkCallback, which I have defined as such:

class WifiConnectorNetworkCallback : ConnectivityManager.NetworkCallback
{
    WifiConnector source;

    public WifiConnectorNetworkCallback(WifiConnector p)
    {
        source = p;
    }

    public override void OnAvailable(Network network)
    {
        source.connectivity_manager.BindProcessToNetwork(network);
        source.WifiNetworkConnected?.Invoke(source, new EventArgs());
    }

    public override void OnUnavailable()
    {
        base.OnUnavailable();
    }
}

Finally, you will notice that when the "OnAvailable" method is called, it invokes an event handler. The method called by the event handler is where I actually create a TcpClient and attempt to the connect to the server on the IoT device. It is defined as such:

private void WifiNetworkConnected(object sender, EventArgs e)
{
    TcpClient client = new TcpClient();
    client.Connect(IPAddress.Parse("192.168.1.1"), 80);
    
    //Create a network stream to send data
    NetworkStream writer = client.GetStream();

    //Write some data
    string data = "test";
    writer.Write(Encoding.ASCII.GetBytes(data), 0, data.Length);

    //Disconnect from the server
    writer.Close();
    client.Close();
}

Now, I have found that the program can behave in a few different ways. If I am running the app on my Android 8.1 device, the typical 3 lines of code that are found in many other StackOverflow questions/answers do not work to connect to the wifi AP:

wifi_manager.Disconnect();
wifi_manager.EnableNetwork(network_id, true);
wifi_manager.Reconnect();

I must include the call to RequestNetwork from the ConnectivityManager class for it to even connect to the wifi AP at all, which is why this line of code exists:

connectivity_manager.RequestNetwork(network_request, network_callback);

But even when using the call to RequestNetwork, on both Android 8.1 and Android 10, it still doesn't fully work. If I were to just stop there and attempt to create a TcpClient and connect to the server on the AP, I get a Network Unreachable exception.

Therefore, after much research and digging around, I decided to add the following line of code in the OnAvailable method after calling RequestNetwork:

parent.connectivity_manager.BindProcessToNetwork(network);

This seems to help sometimes. It has increased my success rate of connecting to the AP with the TcpClient from 0% to maybe 10% or 20%, but unfortunately now I often get a Connection Refused exception.

Now, there is one way in which I seem to have a 100% success rate: if I manually connect to the wifi AP using the Android settings application, and then I return to my Android application and create the TcpClient, it seems to function successfully every single time. Because of this, I don't think there is a bug on the side of the IoT microcontroller. It seems like something is going on in Android where if I manually connect to the AP it works fine, but using the code described above to connect to the AP I keep getting either "Network Unreachable" or "Connection Refused" exceptions.

Can anyone help clarify why I am getting these exceptions? Is there a step I am missing in connecting to the AP? Thanks for any help!

David
  • 1,847
  • 4
  • 26
  • 35
  • 2
    This new API on Android Q is really . As you experience the success rate of connecting is a bit hit and miss, also depends on which device you run this code on. I wrote a blog post about how to connect here: https://blog.ostebaronen.dk/2019/11/android-10-wifi.html You seem to be doing more or less the same. I don't think there is more you can do about it. People have been raising the same issues in the comments on my blog post. – Cheesebaron Oct 06 '20 at 09:30
  • Thanks for linking to your excellent blog post. Indeed, it looks like our approaches are very similar. I seem to get better success if I have previously added the network manually, but that defeats the purpose of what I am trying to do. I wish the wifi module on the IoT device supported P2P connections so I could use that API, but alas it doesn't. I'm considering switching to a different module that supports BLE, hoping that Android BLE APIs and the module may play more nicely with each other. – David Oct 06 '20 at 09:59

0 Answers0