Можно ли добавить конфигурацию сети на Android Q?

Фон

Я заметил, что в классе WifiManager есть функция под названием addNetwork, который может быть полезен, если я хочу восстановить или сохранить информацию о сети (имя сети AKA SSID, вместе с паролем и типом), чтобы я также мог подключиться к ней.

Эта проблема

Я не могу найти много информации о том, как это сделать. Я видел различные примеры в StackOverflow, и если я нацелен на Android API 28 (или ниже), мне действительно удается добавить сеть и даже подключиться к ней.

Однако при ориентации на Android 29 (Android Q) не удается добавить сеть.

Что я нашел

Поскольку я пытаюсь использовать Pixel 2 с Android Q beta 4, я думаю, что, возможно, это потому, что addNetwork устарел, поэтому в документации даже сказано, и что если я нацелен на Android Q, это не сработает, и действительно, это не так ' т работать:

Примечание о совместимости: для приложений, предназначенных для Build.VERSION_CODES.Q или выше, этот API всегда будет возвращать -1.

Кажется, что это должно работать до Android Q (исключая), - это подготовить WifiConfiguration и добавить его. Позже я тоже могу к нему подключиться, если захочу. В Android Q кажется, что он был заменен на WifiNetworkSuggestion , но похоже, что речь идет не о добавлении сети:

Объект «Предложение сети» используется для предоставления сети Wi-Fi для рассмотрения при автоматическом подключении к сетям. Приложения не могут напрямую создавать этот объект, они должны использовать WifiNetworkSuggestion.Builder # build () для получения экземпляра этого объекта.

Приложения могут предоставить платформе список таких сетей с помощью WifiManager # addNetworkSuggestions (List).

Вот мой текущий код для pre-Android-Q

@WorkerThread
fun addNetwork(context: Context, networkName: String, networkPassword: String? = null, keyMgmt: Int = WifiConfiguration.KeyMgmt.NONE) {
    val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val conf = WifiConfiguration()
    conf.SSID = "\"$networkName\""
    conf.preSharedKey = if (networkPassword.isNullOrEmpty()) "" else "\"$networkPassword\""
    conf.allowedKeyManagement.set(keyMgmt)
    when (keyMgmt) {
        WifiConfiguration.KeyMgmt.WPA_PSK -> {
            //WPA/WPA2
        }
        WifiConfiguration.KeyMgmt.IEEE8021X -> {
        }
        WifiConfiguration.KeyMgmt.WPA_EAP -> {
        }
        WifiConfiguration.KeyMgmt.NONE -> {
            if (networkPassword.isNullOrEmpty()) {
                //open network
                conf.wepKeys[0] = "\"\""
            } else {
                //wep
                conf.wepKeys[0] = "\"" + networkPassword + "\""
                conf.wepTxKeyIndex = 0
                conf.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40)
            }
        }
    }
    if (networkPassword.isNullOrEmpty()) {
        //open network
        conf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE)
    } else {
    }
    wifiManager.isWifiEnabled = true
    while (!wifiManager.pingSupplicant()) {
        Log.d("AppLog", "waiting to be able to add network")
    }
    val networkId = wifiManager.addNetwork(conf)
    if (networkId == -1)
        Log.d("AppLog", "failed to add network")
    else {
        wifiManager.enableNetwork(networkId, false)
        Log.d("AppLog", "success to add network")
    }
}

Кажется, для этого требуются только эти разрешения:

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>

Но в любом случае это работает до тех пор, пока вы не нацеливаетесь на Android Q (API 29) и выше. Когда вы нацеливаетесь на него, я действительно всегда получаю «-1», что означает, что он терпит неудачу.

Я также обнаружил проблему в системе отслеживания проблем (здесь и Я писал об этом здесь), рассказывая о том, кому нужен API. назад, но я не уверен, что дело в добавлении сети.

Глядя на WifiNetworkSuggestion, я не вижу, чтобы у него было столько всего, что нужно установить, как у WifiConfiguration, через его построитель, так что это еще одна причина, по которой я подозреваю, что дело не в добавлении сети.

Но я все равно попробовал. Вот код, который я пытался, например, добавить в обычную сеть WPA:

@WorkerThread
fun addNetworkAndroidQ(context: Context, networkName: String, networkPassword: String? = null) {
    val wifiManager = context.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val list = ArrayList<WifiNetworkSuggestion>()
    val builder = WifiNetworkSuggestion.Builder().setSsid(networkName)
    if (!networkPassword.isNullOrEmpty())
        builder.setWpa2Passphrase(networkPassword)
    list.add(builder.build())
    val result = wifiManager.addNetworkSuggestions(list)
    if (result == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS)
        Log.d("AppLog", "success")
    else Log.d("AppLog", "failed")
}

При запуске (я дал ему данные о своей сети Wi-Fi, после того, как ОС забыла об этом), он говорит, что это удалось, но в настройках Wi-Fi ОС ничего не произошло. Там нет сети с добавленным мной паролем. Так что я действительно не понимаю, что он сделал ...

Через несколько долгих секунд я заметил уведомление, спрашивающее меня, можно ли подключиться к предлагаемым сетям, созданным приложением:

введите описание изображения здесь

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

Я попытался сделать еще один POC, подумав, что, возможно, сделал это неправильно, но тогда он даже не показывал уведомление. Поскольку я считаю, что все это поведение является ошибкой, я сообщил об этом здесь < / а>.

Более того, я обнаружил, что, если действительно предполагается добавить сеть тем или иным способом, у нее все еще есть некоторые серьезные ограничения, такие как максимальное количество добавленных сетей ( здесь) и удаляется при удалении приложения ( здесь)

Вопросы

  1. Как именно следует обращаться с Android Q? Неужели больше нет API для добавления сети?

  2. Если WifiNetworkSuggestion не о добавлении сети, то для чего она на самом деле используется?

  3. Поскольку я недостаточно знаком с лакомыми кусочками добавления сети, верен ли мой код в отношении всех возможных способов добавления сети? Я спрашиваю об этом, потому что кто-то написал здесь, что люди должны включить Wi-Fi и убедиться, что pingSupplicant возвращает true. Это правда? Или достаточно просто позвонить addNetwork?

  4. Если сейчас невозможно добавить сеть с помощью обычного API, есть ли решение, используя вместо этого рутированное устройство? Может какая-то команда adb?


РЕДАКТИРОВАТЬ: не знаю, как это сделать официально, но используя adb, вы могли бы добавить Wifi-сети на Android 11. Нужно проверить adb shell cmd wifi help.


person android developer    schedule 05.07.2019    source источник
comment
Есть обновления по этому поводу? У меня такие же проблемы, и я видел, как некоторые приложения, ориентированные на API 29, подключались к его конфигурации Wi-Fi без таких уведомлений / подсказок.   -  person Aswin P Ashok    schedule 01.04.2020
comment
@AswinPAshok Судя по тому, что я читал, Google планирует сделать этот API более гибким на Android R. Возможно, позволит сделать его постоянным (остается после удаления приложения).   -  person android developer    schedule 01.04.2020
comment
Можете ли вы взглянуть на это изображение: imgur.com/cdv3fHR. После этого запроса некоторые приложения могут подключиться к их конфигурациям Wi-Fi. Что это за намерение? Простите за глупость.   -  person Aswin P Ashok    schedule 01.04.2020
comment
@AswinPAshok Понятия не имею. Предлагаю задать новый вопрос. Я тестировал этот API только из любопытства и потому, что хочу иметь эту функциональность в сторонних приложениях :)   -  person android developer    schedule 01.04.2020
comment
@androiddeveloper, как я могу установить параметры прокси, такие как имя хоста и порт прокси? и настройки IP?   -  person Anant Shah    schedule 11.05.2020
comment
@AnantShah Извините, я понятия не имею. Предлагаю поиграть с API и посмотреть, что предлагается.   -  person android developer    schedule 12.05.2020
comment
@androiddeveloper кажется, что многие классы аннотированы UnsupportedAppUsage, например android.net.IpConfiguration, android.net.StaticIpConfiguration, не удалось найти документацию или пример для установки полей wifiConfiguration   -  person Anant Shah    schedule 12.05.2020
comment
@androiddeveloper Я также проверил код AOSP для приложения настроек Wi-Fi, чтобы установить конфигурацию Wi-Fi в Android Q. Но они используют класс WifiConfigController.java, а в строке № 1264 написан код, в котором используется неподдерживаемая аннотация использования приложения StaticIpConfiguration.   -  person Anant Shah    schedule 12.05.2020
comment
Не уверен, что может помочь: github.com/aosp-mirror/platform_packages_apps_settings/blob/   -  person android developer    schedule 13.05.2020
comment
Опять же, извините, я не могу вам помочь.   -  person android developer    schedule 16.05.2020


Ответы (4)


Я застрял с той же проблемой, но каким-то образом я достиг воспроизводимого состояния для подключения желаемой сети, и я хочу поделиться своими выводами, это может помочь.

Вкратце: перед применением логики WifiNetworkSuggestion необходимо отключить все автоматические соединения.

Для получения дополнительной информации прочтите следующее:

Я использовал следующий код (аналогичный тому, что вы используете):

private fun connectUsingNetworkSuggestion(ssid: String, password: String) {
    val wifiNetworkSuggestion = WifiNetworkSuggestion.Builder()
        .setSsid(ssid)
        .setWpa2Passphrase(password)
        .build()

    // Optional (Wait for post connection broadcast to one of your suggestions)
    val intentFilter =
        IntentFilter(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION);

    val broadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (!intent.action.equals(WifiManager.ACTION_WIFI_NETWORK_SUGGESTION_POST_CONNECTION)) {
                return
            }
            showToast("Connection Suggestion Succeeded")
            // do post connect processing here
        }
    }
    registerReceiver(broadcastReceiver, intentFilter)

    lastSuggestedNetwork?.let {
        val status = wifiManager.removeNetworkSuggestions(listOf(it))
        Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
    }
    val suggestionsList = listOf(wifiNetworkSuggestion)
    var status = wifiManager.addNetworkSuggestions(suggestionsList)
    Log.i("WifiNetworkSuggestion", "Adding Network suggestions status is $status")
    if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_ERROR_ADD_DUPLICATE) {
        showToast("Suggestion Update Needed")
        status = wifiManager.removeNetworkSuggestions(suggestionsList)
        Log.i("WifiNetworkSuggestion", "Removing Network suggestions status is $status")
        status = wifiManager.addNetworkSuggestions(suggestionsList)
    }
    if (status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS) {
        lastSuggestedNetwork = wifiNetworkSuggestion
        lastSuggestedNetworkSSID = ssid
        showToast("Suggestion Added")
    }
}

Итак, вот шаги:

  1. Установите новую версию / или удалите все предложения, которые вы добавили ранее
  2. Убедитесь, что вы забыли все окружающие сети, чтобы ваше устройство не подключалось автоматически
  3. Добавить список предложений сети Wi-Fi
  4. Зайдите в настройки Wi-Fi, чтобы сканировать сети, или подождите, пока не запустится следующее сканирование.
  5. Появится окно с уведомлением:

«Уведомление» 6. Когда вы нажмете «Да», система автоматически подключится к ней через ваше приложение, и Интернет будет работать нормально. См. Следующее:

«Подключено

Обратите внимание на следующее:

  1. Если вы отключите сеть в настройках Wi-Fi (например, нажмете значок отключения корзины на следующем изображении), ваша сеть будет заблокирована на 24 часа от автоматического подключения, даже если вы удалили предложенную сеть с помощью wifiManager.removeNetworkSuggestions(listOf(it)) и добавили ее снова. И даже если вы удалите и снова установите приложение

Сведения о подключенном Wi-Fi

К сожалению, это ограничение, добавленное системой Android, как описано здесь:

Если пользователь использует средство выбора Wi-Fi для явного отключения от одной из предложенных сетей при подключении к нему, то эта сеть будет занесена в черный список на 24 часа. В течение периода черного списка эта сеть не будет рассматриваться для автоматического подключения, даже если приложение удалит и повторно добавит сетевое предложение, соответствующее сети.

  1. Если вы удалите приложение при подключении к предложенному Wi-Fi, система автоматически закроет соединение.
  2. Если у вас есть несколько предложений, вы можете установить их приоритеты, используя WifiNetworkSuggestion.Builder().setPriority(<Priority Integer>), как упоминалось здесь:

Укажите приоритет этой сети среди других сетевых предложений, предоставляемых одним и тем же приложением (приоритеты не влияют на предложения разных приложений). Чем выше число, тем выше приоритет (т. Е. Значение 0 = самый низкий приоритет).

  1. Если вы нажали «Нет» в уведомлении, вы можете изменить его с (Settings > Apps & notifications > Special App access > Wi-Fi Control > App name), как описано здесь:

Пользователь, отклоняющий уведомление о предложении сети, удаляет разрешение CHANGE_WIFI_STATE из приложения. Пользователь может предоставить это разрешение позже, войдя в меню управления Wi-Fi (Настройки> Приложения и уведомления> Доступ к специальным приложениям> Управление Wi-Fi> Имя приложения).

person Mohammad Sayed    schedule 16.04.2020
comment
Спасибо, что поделились ответом, он действительно полезен. Но я застрял в том, что метод removeNetworkSuggestions на самом деле не отключает меня от Wi-Fi, который я подключил с помощью networkSuggestion. Только удаление приложения отключает меня от Wi-Fi. Любой другой способ отключить? - person Rahul Sharma; 04.11.2020

Я хотел бы получить ответы на все ваши вопросы, потому что в настоящее время я борюсь с аналогичными проблемами.

Спустя много часов я наконец смог подключиться к желаемой сети, используя такой подход:

val wifiNetworkSpecifier = WifiNetworkSpecifier.Builder()
    .setSsid(ssid)
    .setWpa2Passphrase(passphrase)
    .setBssid(mac)
    .build()

val networkRequest = NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
    .setNetworkSpecifier(wifiNetworkSpecifier)
    .build()

val connectivityManager = applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager?

connectivityManager?.requestNetwork(networkRequest, ConnectivityManager.NetworkCallback())

Вы можете получать множество событий через ConnectivityManager.NetworkCallback().

person Sebastian Helzer    schedule 02.09.2019
comment
Подождите, что это за API? Это похоже на старую? Это без ограничений? Без подсказки? Добавляет сеть навсегда? Остается после удаления? - person android developer; 04.09.2019
comment
Это API, представленный в Android Q (10). Насколько я могу судить, это соединение сохраняется, пока NetworkCallback не удален. Сеть не сохраняется в сетевых конфигурациях. - person Sebastian Helzer; 04.09.2019
comment
Значит, он примерно такой же, как API, который я нашел, не так ли? Он удаляется при удалении приложения? В чем разница между этим и тем, что я нашел? Просит ли пользователя добавить его? - person android developer; 04.09.2019
comment
Упомянутый выше API создан для подключения к сети, которая не обеспечивает подключение к Интернету (например, внешнее устройство, такое как Chromecast или камера), в отличие от API предложений WiFi. Используя вышеуказанный API, пользователь получит всплывающее окно с выбранным SSID, чтобы он мог подтвердить подключение к нему. Пользователь будет подключен к этой сети, но соединение доступно только внутри приложения (оно не будет работать во внешнем браузере, таком как Chrome). - person Mohru; 20.04.2020
comment
@SebastianHelzer Можете ли вы подтвердить комментарий Мохру о том, что Интернет не работает для подключений, установленных вами с помощью метода WifiNetworkSpecifier? Я думаю, что соединение также ограничено вашим собственным приложением, поэтому другие приложения не смогут его использовать. - person Julian Schweppe; 12.08.2020
comment
@JulianSchweppe Мору прав, соединение доступно только внутри приложения и полезно для связи с внешними устройствами. - person Sebastian Helzer; 20.08.2020
comment
Мне удалось успешно подключиться с помощью NetworkRequest, который есть в вашем фрагменте кода, но у меня нет подключения к Интернету, даже в моем приложении. Я вижу значок Wi-Fi в строке состояния с крестиком в нижней части, что означает отсутствие Интернета. Могу ли я упустить какую-то конфигурацию? - person KBog; 17.10.2020
comment
@Mohru Другое приложение может получить доступ к запрошенной вами сети Wi-Fi с помощью этого: val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { // bind this so all api calls are performed over this new network connectManager.bindProcessToNetwork(network) onWifiConnected() } } - person Vũ Hồng Kỳ; 04.02.2021
comment
@ VũHồngKỳ Возможно, они могут, но вы должны поместить этот код прямо в приложение. Вы не сможете сделать это с существующими приложениями, такими как Chrome. - person Mohru; 05.02.2021

Похоже, они добавили поддержку в Android 11 (API 30) для добавления сетевой конфигурации, которая сохраняется за пределами области приложения и сохраняется как системная сетевая конфигурация, как это было сделано с устаревшим методом WiFiManager addNetwork. Все, что вам нужно сделать, это использовать ACTION_WIFI_ADD_NETWORKS для отображения системного диалогового окна. который спрашивает пользователя, хочет ли он продолжить добавление нового предложения Wi-Fi в систему. Вот как мы начинаем этот диалог:

// used imports
import android.provider.Settings.ACTION_WIFI_ADD_NETWORKS
import android.provider.Settings.EXTRA_WIFI_NETWORK_LIST
import android.app.Activity
import android.content.Intent
import android.net.wifi.WifiNetworkSuggestion





// show system dialog for adding new network configuration
    val wifiSuggestionBuilder = WifiNetworkSuggestion.Builder()
                .setSsid("network SSID")
                .build()

    val suggestionsList = arraylistOf(wifiSuggestionBuilder)
    val intent = new Intent(ACTION_WIFI_ADD_NETWORKS)
    intent.putParcelableArrayListExtra(EXTRA_WIFI_NETWORK_LIST, suggestionsList);
    activity.startActivityForResult(intent, 1000)

Диалог выглядит так:

И тогда нам просто нужно обработать результат в onActivityResult методе следующим образом:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == 1000) {
        if (resultCode == Activity.RESULT_OK) {
            // network succesfully added - User pressed Save
        } else if (resultCode == Activity.RESULT_CANCELED) {
            // failed attempt of adding network to system - User pressed Cancel
        }
    }
}

Но поскольку я тестировал этот код на устройствах Android, на которых установлены более старые версии Android (ниже API30), у меня возникает сбой каждый раз, когда я хочу, чтобы он отображал это диалоговое окно для добавления новой конфигурации сети. Это сбой:

java.lang.RuntimeException: Unable to start activity: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.settings.WIFI_ADD_NETWORKS (has extras) }

Похоже, что новый способ не поддерживается из коробки. Итак, для API30 мы можем использовать новое действие Intent, для API 28 и ниже мы все еще можем использовать старый способ добавления сетей, но для API29 у нас есть какая-то серая зона, где я пока не смог найти хорошее решение. Если у кого-то есть идея, что еще делать, поделитесь со мной. ;)

person BVantur    schedule 07.10.2020
comment
Спасибо. О том, почему он не работает с API 29, потому что ACTION_WIFI_ADD_NETWORKS взят из API 30: developer.android.com/reference/android/provider/ - person android developer; 07.10.2020

Ответ @ Себастьяна Хельцера мне подходит. Я использую java в своем приложении. Это может помочь пользователям Java ...

WifiNetworkSpecifier wifiNetworkSpecifier = new WifiNetworkSpecifier.Builder()
                .setSsid(ssid)
                .setWpa2Passphrase(password)
                .build();
NetworkRequest networkRequest = new NetworkRequest.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                .setNetworkSpecifier(wifiNetworkSpecifier)
                .build();
ConnectivityManager connectivityManager = (ConnectivityManager)this.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
connectivityManager.requestNetwork(networkRequest, new ConnectivityManager.NetworkCallback());
person Amanullah Asraf    schedule 11.08.2020
comment
Удалось ли вам подключить желаемую сеть? А как насчет подключения к Интернету? Работает? - person yong; 25.08.2020
comment
успешно подключился к желаемой сети, но подключение к Интернету не установлено. - person Amanullah Asraf; 02.09.2020
comment
Удастся ли @AmanullahAsraf установить подключение к Интернету с помощью вашего метода, даже только для вашего приложения? - person KBog; 17.10.2020