Проблемы с подключением к точке доступа Wi-Fi в Android (Xamarin)

В настоящее время я работаю над проектом, в котором у меня есть приложение для Android, которому необходимо подключиться к устройству IoT и отправить ему некоторую информацию. Устройство IoT использует микроконтроллер SAMD21 с модулем Wi-Fi ATWINC1500. ATWINC1500 способен создавать точку доступа Wi-Fi, к которой я должен иметь возможность подключиться с устройства Android. Он не поддерживает прямые Wi-Fi (p2p) соединения и не поддерживает Bluetooth. В настоящее время на устройстве IoT я создаю точку доступа, а затем запускаю сервер для прослушивания входящих подключений. Просто для информации: точка доступа Wi-Fi в настоящее время открыта без пароля или ключа.

Моя упрощенная программа такова:

  1. На устройстве Android просканируйте доступные сети Wi-Fi и найдите ту, которая соответствует имени точки доступа, созданной устройством IoT.
  2. Подключитесь к точке доступа IoT-устройства
  3. Создайте TcpClient и подключитесь к серверу на устройстве IoT (IP-адрес 192.168.1.1).
  4. Отправьте некоторую информацию на устройство IoT.

Что касается Android, я использую C # с Xamarin. У меня есть несколько разных тестовых устройств: планшет 10 под управлением Android 10 и телефон 5 под управлением Android 8.1. Я изучил документацию Android, а также несколько других сообщений о переполнении стека, в которых подробно рассказывается о том, как программно подключить устройство Android к точке доступа Wi-Fi. Вот лишь несколько других сообщений о переполнении стека, которые я прочитал:

  1. Подключиться к Wi-Fi Android Q
  2. Как программно подключиться к Wi-Fi
  3. Как подключиться к программно для конкретной сети Wi-Fi в Android?
  4. Android Q, программно подключайтесь к другой точке доступа Wi-Fi для Интернета < / а>
  5. Android 10 / API 29: как подключить телефон к настроенной сети?

К сожалению, даже после просмотра всех этих источников у меня были неоднозначные результаты. Это срабатывает иногда, но только в меньшей степени. Это также осложняется тем, что Android имеет отдельные API для подключения к Wi-Fi в зависимости от того, какая версия Android работает на устройстве.

Вот мой основной способ подключения к точке доступа Wi-Fi:

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);
        }
    }
}

Обратите внимание, что переменные connectivity_manager и wifi_manager определены в другом месте кода, и они относятся к типам соответственно ConnectivityManager и WifiManager. Кроме того, переменная network_callback также определена в другом месте кода и имеет тип WifiConnectorNetworkCallback, который я определил как таковой:

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();
    }
}

Наконец, вы заметите, что когда вызывается метод OnAvailable, он вызывает обработчик событий. В методе, вызываемом обработчиком событий, я фактически создаю TcpClient и пытаюсь подключиться к серверу на устройстве IoT. Это определяется как таковое:

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();
}

Теперь я обнаружил, что программа может вести себя по-разному. Если я запускаю приложение на своем устройстве Android 8.1, типичные 3 строки кода, которые встречаются во многих других вопросах / ответах StackOverflow, не работают для подключения к Wi-Fi. AP:

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

Я должен включить вызов RequestNetwork из класса ConnectivityManager, чтобы он вообще мог подключиться к точке доступа Wi-Fi, поэтому существует эта строка кода:

connectivity_manager.RequestNetwork(network_request, network_callback);

Но даже при использовании вызова RequestNetwork как на Android 8.1, так и на Android 10 он по-прежнему не работает полностью. Если бы я просто остановился на этом и попытался создать TcpClient и подключиться к серверу на AP, я бы получил исключение Network Unreachable.

Поэтому после долгих исследований и копаний я решил добавить следующую строку кода в метод OnAvailable после вызова RequestNetwork:

parent.connectivity_manager.BindProcessToNetwork(network);

Кажется, это помогает иногда. Это увеличило мою успешность подключения к AP с помощью TcpClient с 0% до, может быть, 10% или 20%, но, к сожалению, теперь я часто получаю исключение Connection Refused.

Теперь есть один способ, которым я, кажется, добился 100% успеха: если я вручную подключаюсь к точке доступа Wi-Fi с помощью приложения настроек Android, а затем я возвращаюсь в мое приложение для Android и создайте TcpClient, он каждый раз работает успешно. Из-за этого я не думаю, что есть ошибка на стороне микроконтроллера IoT. Похоже, что что-то происходит в Android, где, если я вручную подключаюсь к AP, он работает нормально, но, используя код, описанный выше для подключения к AP, я продолжаю получать либо Network Unreachable, либо Connection Отказано в исключениях.

Может ли кто-нибудь помочь прояснить, почему я получаю эти исключения? Я пропустил какой-то шаг при подключении к точке доступа? Спасибо за любую помощь!


person David    schedule 06.10.2020    source источник
comment
Этот новый API на Android Q действительно ????. Как вы понимаете, успешное подключение может быть несколько неожиданным, это также зависит от того, на каком устройстве вы запускаете этот код. Я написал в блоге сообщение о том, как подключиться здесь: blog.ostebaronen.dk /2019/11/android-10-wifi.html Похоже, вы делаете примерно то же самое. Я не думаю, что с этим можно что-то сделать. Люди поднимали те же вопросы в комментариях к моему сообщению в блоге.   -  person Cheesebaron    schedule 06.10.2020
comment
Спасибо за ссылку на ваш отличный пост в блоге. Действительно, похоже, что наши подходы очень похожи. Кажется, я добьюсь большего успеха, если ранее добавлял сеть вручную, но это противоречит цели того, что я пытаюсь сделать. Я бы хотел, чтобы модуль Wi-Fi на устройстве IoT поддерживал P2P-соединения, чтобы я мог использовать этот API, но, увы, это не так. Я подумываю о переходе на другой модуль, поддерживающий BLE, в надежде, что API-интерфейсы Android BLE и модуль могут лучше взаимодействовать друг с другом.   -  person David    schedule 06.10.2020