可以在Android Q上添加网络配置吗?

时间:2019-07-05 15:39:06

标签: android android-wifi wifimanager androidq

背景

我注意到在WifiManager类中有一个名为addNetwork的函数,如果我想恢复或保存网络信息(网络名AKA SSID以及密码和类型),以便我也可以连接到它。

问题

我找不到有关如何执行此操作的太多信息。我已经在StackOverflow上看到了各种示例,并且如果我以Android API 28(或更低版本)为目标,那么我确实成功地使其添加了网络,甚至连接到网络。

但是,当定位到Android 29(Android Q)时,它无法添加网络。

我发现的东西

由于我正在尝试使用Android Q beta 4的Pixel 2,我认为这可能是因为addNetwork已过时,因此文档甚至会这样说,并且如果我定位到Android Q,它将不会有效,但实际上不起作用:

  

兼容性注意:对于面向Build.VERSION_CODES.Q的应用程序   或更高版本,此API将始终返回-1。

通过准备WifiConfiguration并添加它,直到Android Q(不包括),它看起来应该可以工作。以后,我也可以根据需要连接到它。在Android Q上,它似乎已被WifiNetworkSuggestion取代,但似乎根本就不是在添加网络:

  

网络建议对象用于为以下设备提供Wi-Fi网络:   自动连接到网络时的注意事项。应用无法直接   创建这个对象,他们必须使用   WifiNetworkSuggestion.Builder#build()获取此实例   对象。

     

应用可以使用以下方式向平台提供此类网络的列表   WifiManager#addNetworkSuggestions(列表)。

这是我当前的代码,适用于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”,这意味着它失败了。

我还在问题跟踪器(here上发现了一个问题,我写了这个问题here),告诉有人需要该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网络详细信息),它说成功了,但是在OS的Wifi设置上什么也没有发生。使用我添加的密码,该网络不存在。所以我真的不知道它做了什么...

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

enter image description here

但是当我选择接受时,它还是没有做任何事情。

我尝试制作另一个POC,以为我可能做错了,但后来它甚至没有显示通知。由于我认为整个行为都是错误,因此我已经here对此进行了报道。

不仅如此,而且我发现,如果确实应该以一种或另一种方式添加网络,则它仍然存在一些严重的限制,例如最大添加的网络(here)以及已被删除卸载应用程序(here

问题

  1. 如何正确处理Android Q?真的没有API可以添加网络了吗?

  2. 如果WifiNetworkSuggestion与添加网络无关,那么它到底是用来做什么的?

  3. 由于我对添加网络的窍门不熟悉,因此我的代码关于添加网络的所有可能方式是否正确?我之所以这样问,是因为有人写了here,人们应该启用Wifi并确保pingSupplicant返回true。是真的吗还是仅致电addNetwork就足够了?

  4. 如果现在无法使用常规API添加网络,那么是否有可能通过使用有根设备代替解决方案?也许一些adb命令?

4 个答案:

答案 0 :(得分:5)

我坚持同样的问题,但是以某种方式达到了连接所需网络的可重现状态,并且我想分享我的发现可能会有所帮助。

摘要: 您必须先禁用所有自动连接,然后再应用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设置中断开网络连接(即,按下图所示的断开连接框图标),即使您使用wifiManager.removeNetworkSuggestions(listOf(it))删除了建议的网络并添加了网络,您的网络也将被禁止自动连接24小时再次。而且,即使您再次卸载并安装了应用,也可以

Connected Wifi details

不幸的是,这是Android系统根据here所述添加的限制:

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

  1. 如果您在连接到建议的WiFi时卸载了应用程序,则系统将自动关闭连接。
  2. 如果您有多个建议,可以按照here的说明使用WifiNetworkSuggestion.Builder().setPriority(<Priority Integer>)来优先处理它们:

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

  1. 如果您在通知提示中按了“否”,则可以按照here所述从(Settings > Apps & notifications > Special App access > Wi-Fi Control > App name)进行更改:

拒绝网络建议通知的用户会从应用中删除CHANGE_WIFI_STATE权限。用户可以稍后通过进入Wi-Fi控制菜单(设置>应用和通知>特殊应用访问> Wi-Fi控制>应用名称)来授予此批准。

答案 1 :(得分:2)

希望我能回答您所有的问题,因为我目前正努力解决类似的问题。

几个小时后,我终于可以使用这种方法连接到所需的网络:

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

答案 2 :(得分:1)

好像他们已经在Android 11(API 30)中添加了对添加网络配置的支持,该配置保留在应用程序范围之外,并保存为系统网络配置,就像使用不推荐使用的WiFiManager方法{{3} }。您需要做的就是使用addNetwork来显示一个系统对话框,询问用户是否要继续向系统添加新的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)

对话框如下:

ACTION_WIFI_ADD_NETWORKS

然后我们只需要使用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,我们有一些灰色区域,我尚无法找到好的解决方案。如果有人有其他想法,请与我分享。 ;)

答案 3 :(得分:0)

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