Подписаться на получение новых статей на почту:

Выбор и подключение к сети Wi-Fi на Android. Шаг №88

Всем привет. Давайте вспомним, что  в прошлой статье мы с Вами остановились на поиске сетей Wi-Fi и переносе их SSID-ов в кликабельный список. В этой записи рассмотрим один из способов подключения к необходимой сети из нашего приложения SmartHouse. На самом деле задача оказалась довольно непростой, начиная с версии Android 5.

Наш план действий: – рассмотреть способы подключения к сетям; – изменения в политике безопасности; – способы получения необходимых разрешений; – добавление функциональных кнопок в разрабатываемое приложение. Итак все по порядку.

Для начала добавим проверку включения модуля wi-fi, если выключен — включаем.

 

 

public void enableWifi() {  //включение модуля Wi-Fi
     WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
     if (!wifiManager.isWifiEnabled()) {        
          wifiManager.setWifiEnabled(true);    
          Toast toast = Toast.makeText(getApplicationContext(), "Wifi включен", Toast.LENGTH_SHORT);         toast.show();    
     }
}

Ниже скриншот, где видно что программа подключается непосредственно к модулю. Выводим всплывающее сообщение.

Wifi_включен

Перед следующим шагом необходимо оговорить защищенность сети. Методы защиты WPA, WEP пока не будем рассматривать. Для начала мы с Вами используем открытую сеть, без логина и пароля. Попробуем из конфигурации сетей Wi-Fi подключится к ESP8266. И здесь на скриншоте, ниже, мы видим сообщение «У доступі до мережі відмовлено».

 

 

 

 

Доступ-wifi Дело в том, что если в сети отсутствует интернет – соединение, то из соображений политики безопасности мы его не получим. Естественно  приложение не откроет сокет. В более ранних версиях выводилось сообщение о том, что «подключенная сеть Wi-Fi не имеет доступ к интернету». Чтобы сокет открылся, пользователь должен зайти в это оповещение и подтвердить, что он хочет подключиться именно к этой, «безинтернетной» сети. В версиях выше 5-ой, и уж точно 6.0, на которой опробовано приложение, данных оповещений нет. Во вторых наша задача написать код, который подключается к выбранной сети и минимизирует движения со стороны пользователя. Данные новшества введены ради безопасности, чтобы без ведома пользователя приложения не коннектились к непонятным сетям. Один из вариантов решения, точнее разрешения – это прописание данной строки в коде:

Settings.Global.putInt (getContentResolver (), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 0); , где CAPTIVE_PORTAL_DETECTION_ENABLED должен быть глобальным параметром, который установкой в 0 избегает проверки (тестирование сервером google) wi-fi на интернет-доступ.

К сожалению в зависимости от версии Android Studio и ОС Android  данная константа может быть скрыта и не поддерживаться. Из-за чего может потребоваться использовать напрямую строку «captive_portal_detection_enabled», а не константу:

Settings.Global.putInt (getContentResolver (), «captive_portal_detection_enabled», 0);  Но это также может не помочь.

error

Есть возможность использовать adb (для изменения пользовательских настроек), для чего необходимо иметь root-права. Этот способ я даже не пробовал использовать, т.к. нас интересует только программное решение. Так как у нас приложение должно будет вносить изменения, то нам необходимо получить разрешение WRITE_SETTINGS, что позволяет программе читать или записывать защищенные системные настройки.  Данное разрешение было отменено из пользовательских приложений (как в не системных приложениях) в api23.

Итак чтобы использовать WRITE_SETTINGS необходимо:
— Прописать в манифесте <uses-permission android:name="android.permission.WRITE_SETTINGS"/>
 Может потребоваться также <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>. Хотя если есть WRITE_SETTINGS то последнее не требуется. По состоянию на Android 6.0.1 CHANGE_NETWORK_STATE автоматически предоставляется при запросе в вашем файле манифеста. Вышеуказанные проверки WRITE_SETTINGS требуются только для 6.0;
- Вызовите Settings.System.canWrite (), чтобы узнать, имеете ли вы право на запись настроек.
 — Если canWrite () возвращает false, запустите действие ACTION_MANAGE_WRITE_SETTINGS, чтобы пользователь мог согласиться, и ваше приложение действительно могло писать настройки. Это необходимо сделать один раз.
В файл активности добавим функцию проверки данного разрешения.

private boolean checkSystemWritePermission() { //разрешаем программе вносить изменения
    boolean retVal = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        retVal = Settings.System.canWrite(this);
        if(retVal){
            Toast.makeText(this, "Write allowed :-) ", Toast.LENGTH_LONG).show();
            return (retVal);
        }else{
           Toast.makeText(this, "Write not allowed :-( ", Toast.LENGTH_LONG).show();
           Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_WRITE_SETTINGS);
           intent.setData(Uri.parse("package:" + this.getPackageName()));//открывает разрешение
           intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }
    }
 return retVal;
}

Начиная с android Marshmellow, нам необходимо использовать разрешения во время выполнения, которые направлены на повышение безопасности, или использовать когда это необходимо. Ниже скриншоты проверки и включения:
write_not

Дозвіл

 

 

write_yes

Разрешение для программы получили. Теперь для подключения к wi-fi используем следующую функцию:

private void connectToWiFi(String wifiName) {
    WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
    WifiConfiguration configuration = new WifiConfiguration();
    configuration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
    wifiManager.addNetwork(configuration);
    List<WifiConfiguration>
    list = wifiManager.getConfiguredNetworks(); //возвращает список сетей
    for (WifiConfiguration i : list) {
       if (i.SSID != null && i.SSID.equals("\"" + wifiName + "\"")) {
          wifiManager.disconnect();
          wifiManager.enableNetwork(i.networkId, true);
          wifiManager.reconnect();
          break;
     }
   }
 }

Данный код, выше, будет соединять только сети с интернет-доступом. Здесь также имеем изменения. Ниже ссылка на статью (Connecting your App to a Wi-Fi Device) по этому поводу: https://android-developers.googleblog.com/2016/07/connecting-your-app-to-wi-fi-device.html

Кратенькая выдержка из статьи: But, on Lollipop and above, if that network doesn’t have internet connectivity network, requests will not be routed to it.
Routing network requests
To direct all the network requests from your app to an external Wi-Fi device, call ConnectivityManager#setProcessDefaultNetwork on Lollipop devices, and on Marshmallow call ConnectivityManager#bindProcessToNetwork instead, which is a direct API replacement. Note that these calls require android.permission.INTERNET; otherwise they will just return false.
где – bindProcessToNetwork - связывает текущий процесс с сетью. Здесь произошли изменения в сетевых технологиях и Wi-Fi. Раньше, если приложение форсировало подключение устройства к определённой Wi-Fi сети, используя enableNetwork () с настройкой disableAllOthers=true, устройство отключалось от других сетей, типа мобильной передачи данных. В этом релизе устройство больше не отключается от других сетей. Если в приложении для targetSdkVersion задано “20” или ниже, то оно привязывается к заданной Wi-Fi. Если же targetSdkVersion “21” или выше, используйте мультисетевые API (такие как openConnection (), bindSocket () и новый метод bindProcessToNetwork ()), чтобы быть уверенным, что трафик идёт через выбранную сеть. Теперь ваши приложения могут изменять состояние WifiConfiguration объектов только если они сами эти объекты создали. Вы не можете изменять или удалять WifiConfiguration объекты, созданные пользователем или другими приложениями.

Для подключения к беспроводной сети без интернет соединения во первых мы должны:

— добавить в манифест <uses-permission android:name="android.permission.INTERNET"/> -что позволяет приложениям открывать сетевые сокеты;
— использовать ConnectivityManager#bindProcessToNetwork. Ниже функция где перед подключением к сокету, проверяем версию ОС.
Здесь мы создаем ConnectivityManager для запроса сети с высокой пропускной способностью Wi-Fi или сотовая сеть. Используем класс NetworkRequest.Builder для создания объекта NetworkRequest и указываем сетевые функции и тип передачи, в которых ваше приложение заинтересовано. Для сканирования подходящих сетей вызоваем requestNetwork () или registerNetworkCallback () и передаем объект NetworkRequest и реализацию ConnectivityManager.NetworkCallback. Используйте метод requestNetwork (), если вы хотите использовать подходящую сеть после ее обнаружения. Для получения только уведомлений найденных сетей без подключения, используйте вместо этого метод registerNetworkCallback ()Когда система обнаруживает подходящую сеть, она подключается к ней и вызывает обратный вызов onAvailable () вашего экземпляра NetworkCallback. Если подходящая сеть не найдена, метод onAvailable () не вызывается, поэтому вы должны вручную произвести запрос. Можно использовать объект network из обратного вызова для получения дополнительной информации или прямого трафика выбранной сети.

 private void bindToNetwork() {
        final ConnectivityManager connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    builder = new NetworkRequest.Builder(); //set the transport type do WIFI
                    builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);//addTransportType-Добавляет заданный транспортный запрос к этому строителю.
                    connectivityManager.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() {
                        @Override
                        public void onAvailable(Network network) {
                             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                                connectivityManager.bindProcessToNetwork(network);//Use the Network object to bind the process to it.
                             }else { //This method was deprecated in API level 23
                             ConnectivityManager.setProcessDefaultNetwork(network);
                        }try {//do a callback or something else to alert your code that it's ok to send the message through socket now              } catch (Exception e) {
                              e.printStackTrace();
                        }
                        connectivityManager.unregisterNetworkCallback(this);}
            } ); } }

Ниже скриншот подсоединения телефона к модулю esp8266 и его свойства в конфигурации телефона.
wi-fi_esp

propert_wi-fi

Чтобы оптимизировать потребление батареи, сетевое соединение должно оставаться зарегистрированным только на время действия. Для чего следует рассмотреть вопрос о выпуске сети. Поэтому добавим кнопку в приложение которая будет освобождать сеть.

case R.id.button2:
 ConnectivityManager connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
       connectivityManager.bindProcessToNetwork(null);
      } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
       connectivityManager.setProcessDefaultNetwork(null);
 }
 break;

На по следок приведу ссылку на статью (Programatically connecting to a WiFi network with Captive Portal on Marshmallow) программного подключения к сетям где используется библиотека android-wificonnect, выложенная на github.com. (https://www.intentfilter.com/). Я ее пока не использовал. Но по описанию она имеет включения/выключения выше описанных нюансов.

Подводим итог. В выше написанной статье мы рассмотрели включение модуля Wi-Fi. Получили необходимые разрешения для чтения и записи системных настроек. Рассмотрели изменения в политике безопасности подключения к сетям. Написали и разобрали функцию подключения без интернет доступа. Добавили кнопку освобождения сети. Хочется отметить, что данный код не всегда подключает модуль, например при отсутствии мобильного интернета, приложение не будет подсоединяться. На данном этапе для дальнейшего тестирования приложения и модуля остановимся на данном коде. В будущем рассмотрим стандарт Wi-Fi Direct (Wi-Fi Peer-to-Peer). В следующей статье научимся передавать данные через модуль ESP8266 в микроконтроллер STM32 и обратно. Добавим поле для вывода информации в приложение. На этом сегодня и остановимся. Всем пока.

Просмотрено 8795 раз.

Я на Google+

Выбор и подключение к сети Wi-Fi на Android. Шаг №88: 2 комментария

  1. Здравствуйте! Если не требуется подключаться автоматически, отключите параметр «Автоподключение». Чтобы при следующем подключении к сети появился экран приветствия, отключите параметр «Автовход». 

Добавить комментарий

Ваш e-mail не будет опубликован.

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Subscribe without commenting