Android Q 是否可以添加网络配置?

17

背景

我注意到在WifiManager类中有一个名为addNetwork的函数,如果想要恢复或保存网络信息(网络名称即SSID,包括密码和类型),则该函数可能非常有用,以便后续可以连接到网络。

问题

我找不到如何实现这样的功能的详细信息。我在StackOverflow上看到了各种示例,如果我针对Android API 28(或更低版本)进行操作,则确实可以添加一个网络并连接到它。

然而,当针对Android 29(Android Q)时,它无法添加网络。

我所发现的

由于我正在尝试使用安装有Android Q beta 4的Pixel 2,因此我认为可能是因为addNetwork被弃用了,因此文档甚至这样说,并且如果我针对Android Q进行操作,则它将无法正常工作,实际上它确实无法正常工作:

 

兼容性说明:针对Build.VERSION_CODES.Q或更高版本的应用程序,此API将始终返回-1。

在Android Q之前,它似乎应该按照以下方式工作:准备WifiConfiguration并添加它。以后,如果需要,可以连接到它。在Android Q上,它似乎被WifiNetworkSuggestion取代,但似乎与添加网络无关:

 

网络建议对象用于提供考虑自动连接到网络时的Wi-Fi网络。应用程序不能直接创建此对象,必须使用WifiNetworkSuggestion.Builder#build()来获取此对象的实例。

    

应用程序可以使用WifiManager#addNetworkSuggestions(List)向平台提供这些网络的列表。

这是我的当前代码,用于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")
}

在运行时(我提供了我的WiFi网络详细信息,并使操作系统忘记它后),它说它成功了,但是操作系统的WiFi设置中没有出现任何东西。该网络不存在,密码也没有被添加。所以我真的不知道它做了什么......

几秒钟后,我注意到了一个通知,询问我是否可以连接应用程序建议的网络:

enter image description here

但是当我选择接受时,它仍然没有做任何事情,就像之前一样。

我尝试制作另一个POC,以为可能做错了,但是那时它甚至没有显示通知。由于我认为整个行为都是错误,我已经在这里报告了这个问题。

不仅如此,我还发现如果确实有一种方法可以添加网络,它仍然有一些严重的限制,例如最大添加网络数(这里)和在卸载应用程序时被删除(这里)。

问题

  1. 应该如何精确处理Android Q?确实不再有API可以添加网络了吗?

  2. 如果WifiNetworkSuggestion不是用于添加网络,那么它究竟被用于什么?

  3. 由于我对添加网络的细节不太熟悉,我的代码是否正确涵盖了所有可能的添加网络方式?我之所以问这个问题,是因为有人在这里写道,人们应该启用Wifi并确保pingSupplicant返回true。这是真的吗?还是只调用addNetwork就足够了?

  4. 如果现在使用常规API无法添加网络,那么也许使用rooted设备会有解决方案吗?也许有一些adb命令?


编辑:不确定如何正式做,但使用adb,您可能能够在Android 11上添加WiFi网络。需要检查adb shell cmd wifi help


这个问题有任何更新吗?我遇到了同样的问题,并且我已经看到一些针对 API 29 的应用程序在连接其 WiFi 配置时没有出现这样的通知/提示。 - Aswin P Ashok
1
@AswinPAshok 我不知道。我建议你提出一个新问题。我只是出于好奇测试了这个API,因为我想在第三方应用程序上拥有这个功能 :) - android developer
@AnantShah 抱歉,我不知道。我建议你试着使用API并看看提供了什么功能。 - android developer
@androiddeveloper 看起来许多类都被注释为“UnsupportedAppUsage”,例如android.net.IpConfiguration、android.net.StaticIpConfiguration,但是找不到任何文档或示例来设置wifiConfiguration字段。 - Anant Shah
不确定是否有帮助:https://github.com/aosp-mirror/platform_packages_apps_settings/blob/master/src/com/android/settings/wifi/WifiConfigController.java - android developer
显示剩余5条评论
4个回答

10

我遇到了同样的问题,但不知为何我找到了一个可重现的状态以连接所需网络,并希望分享我的发现以帮助他人。

总结一下: 在应用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. 添加WiFi网络建议列表
  4. 进入WiFi设置扫描网络或等待下一次扫描运行
  5. 将出现通知提示:

Notification prompt 6. 当您按“是”时,系统将通过您的应用程序自动连接并正常工作。请参见以下内容:

Connected to Suggested Network

请注意以下内容:

  1. 如果您从WiFi设置中断开网络(即在以下图像中按disconnect bin图标),则即使使用wifiManager.removeNetworkSuggestions(listOf(it))删除了建议的网络并再次添加了它,您的网络也将被阻止24小时自动连接。即使您卸载并重新安装应用程序

Connected Wifi details

不幸的是,这是由Android系统添加的限制,如此处所述:

如果用户使用Wi-Fi选择器明确从其中一个网络建议断开连接,则该网络将被列入黑名单24小时。在黑名单期间,即使应用程序删除并重新添加与网络相对应的网络建议,该网络也不会考虑进行自动连接。

  1. 如果您在连接到建议的WiFi时卸载应用程序,则系统将自动关闭连接。
  2. 如果您有多个建议,可以使用WifiNetworkSuggestion.Builder().setPriority(<Priority Integer>)来设置优先级,如此处所述:

指定此网络在同一应用程序提供的其他网络建议中的优先级(优先级不影响不同应用程序的建议)。数字越高,优先级越高(即值为0 = 最低优先级)。

  1. 如果您在通知提示中选择了“否”,可以按照此处的说明,从 (设置 > 应用程序和通知 > 特殊应用程序访问 > Wi-Fi控制 > 应用名称) 更改:

用户拒绝网络建议通知将会移除应用的 CHANGE_WIFI_STATE 权限。用户可以随后进入 Wi-Fi 控制菜单 (设置 > 应用程序和通知 > 特殊应用程序访问 > Wi-Fi控制 > 应用名称) 授予该权限。


谢谢分享答案,非常有帮助。但是我遇到了一个问题,使用removeNetworkSuggestions方法并不能真正将我从使用networkSuggestion连接的wifi中断开。只有卸载应用程序才能将我与wifi断开连接。还有其他方法可以断开连接吗? - Rahul Sharma
不错的解决方案,但是在Android 10(API 29)中是否可以保存网络配置呢? - aguiarroney

5
看起来Android 11 (API 30)已经支持添加网络配置,这些配置可以在应用范围之外保持并作为系统网络配置保存,就像使用已弃用的WiFiManager方法addNetwork一样。您只需要使用ACTION_WIFI_ADD_NETWORKS显示一个系统对话框,询问用户是否要继续添加新的Wifi建议到系统中。这是如何启动该对话框的方式:
// 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(低于API30)的Android设备上测试此代码时,每次尝试显示用于添加新网络配置的对话框时,都会导致崩溃。以下是崩溃信息:

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,我还没有找到一个好的解决办法,有人有什么想法请与我分享。;)


2
谢谢。关于为什么它在API 29上不起作用,是因为ACTION_WIFI_ADD_NETWORKS是从API 30开始的:https://developer.android.com/reference/android/provider/Settings#ACTION_WIFI_ADD_NETWORKS - android developer
你在API 29级上做了什么解决方案? - Omar Redani

4

我希望我能回答你所有的问题,因为我目前也在面对类似的问题。

经过多个小时的尝试,我终于通过以下方法成功连接到了所需的网络:

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() 接收到许多事件。

1
等等,这个API是什么?这跟旧的一样吗?这没有限制吗?没有“建议”吗?它会永久添加网络吗?删除后还会保留吗? - android developer
这是在Android Q(10)中引入的API。据我所知,只要NetworkCallback未被移除,此连接将持续存在。该网络不会保存在网络配置中。 - Sebastian Helzer
2
上述API是为连接不提供互联网连接的网络(例如类似Chromecast或相机的外部设备)而创建的,不同于WiFi建议API。使用上述API,用户将会得到一个弹窗,其中包括已选SSID,以便确认连接。用户将会连接到该网络,但是该连接仅在应用内可用(在外部浏览器如Chrome中无法使用)。 - Mohru
@Mohru 其他应用程序仍然可以通过使用以下方式访问您请求的wifi网络:val networkCallback = object : ConnectivityManager.NetworkCallback() { override fun onAvailable(network: Network) { // 绑定此项,以便所有API调用都在此新网络上执行 connectManager.bindProcessToNetwork(network) onWifiConnected() } } - Vũ Hồng Kỳ
@VũHồngKỳ 可能他们可以,但是你必须直接将这段代码放入应用程序中。你无法在现有的应用程序(如Chrome)中执行此操作。 - Mohru
显示剩余4条评论

1

@Sebastian Helzer的答案对我有用。我在我的应用程序中使用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());

1
你成功连接了所需的网络吗?互联网连接怎么样?它正常工作吗? - yong
1
成功连接到所需网络,但无法建立互联网连接。 - Amanullah Asraf
@AmanullahAsraf,您是否有运用您的方法成功建立互联网连接的好运气,即使只是针对您的应用程序? - KBog

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接