首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >安卓WiFi管理器enableNetwork返回true,但NetworkInfo状态始终为DISCONNECTED/SCANNING

安卓WiFi管理器enableNetwork返回true,但NetworkInfo状态始终为DISCONNECTED/SCANNING
EN

Stack Overflow用户
提问于 2018-12-11 04:53:10
回答 1查看 564关注 0票数 0

我已经编写了一些代码来启用具有给定networkId的网络,并通过BroadcastReceiver处理异步响应。但是,即使enableNetwork返回true (表示操作系统成功发出命令),我的BroadcastReceiver也从未收到状态为CONNECTEDNetworkInfo,它收到两个事件:DISCONNECTED,然后是DISCONNECTED/SCANNING

从我读过的所有官方文档和各种SO问题来看,如果enableNetwork返回true,那么注册用于处理NETWORK_STATE_CHANGED_ACTION意图的BroadcastReceiver应该始终接收一个状态为CONNECTEDNetworkInfo对象。

代码如下:

代码语言:javascript
复制
/**
 * Connects to the wifi access point at specified [ssid] with specified [networkId]
 * And returns the [WifiInfo] of the network that has been connected to
 */
private fun connect(context: Context,
                    wifiManager: WifiManager,
                    ssid: String,
                    networkId: Int) = Single.create<WifiInfo> { emitter ->

    val wifiConnectionReceiver = object : BroadcastReceiver() {
        var oldSupplicantState: SupplicantState? = null

        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == WifiManager.NETWORK_STATE_CHANGED_ACTION) {
                val networkInfo = intent.getParcelableExtra<NetworkInfo>(WifiManager.EXTRA_NETWORK_INFO) ?: return

                if (networkInfo.detailedState == NetworkInfo.DetailedState.DISCONNECTED) {
                    context.applicationContext.unregisterReceiver(this)
                    emitter.onError(WiFiException("Failed to connect to wifi network"))
                }
                else if (networkInfo.detailedState == NetworkInfo.DetailedState.CONNECTED) {
                    val wifiInfo = intent.getParcelableExtra<WifiInfo>(WifiManager.EXTRA_WIFI_INFO) ?: return
                    if (ssid == wifiInfo.ssid.unescape()) {
                        context.applicationContext.unregisterReceiver(this)
                        emitter.onSuccess(wifiInfo)
                    }
                }
            } else if (intent.action == WifiManager.SUPPLICANT_STATE_CHANGED_ACTION) {
                val supplicantState = intent.getParcelableExtra<SupplicantState>(WifiManager.EXTRA_NEW_STATE)
                val oldSupplicantState = this.oldSupplicantState
                this.oldSupplicantState = supplicantState

                if (supplicantState == SupplicantState.DISCONNECTED) {
                    if (oldSupplicantState == null || oldSupplicantState == SupplicantState.COMPLETED) {
                        return
                    }
                    val possibleError = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1)
                    if (possibleError == WifiManager.ERROR_AUTHENTICATING) {
                        context.applicationContext.unregisterReceiver(this)
                        emitter.onError(WiFiException("Wifi authentication failed"))
                    }
                } else if (supplicantState == SupplicantState.SCANNING && oldSupplicantState == SupplicantState.DISCONNECTED) {
                    context.applicationContext.unregisterReceiver(this)
                    emitter.onError(WiFiException("Failed to connect to wifi network"))
                }
            }
        }
    }

    val networkStateChangedFilter = IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION)
    networkStateChangedFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)

    context.applicationContext.registerReceiver(wifiConnectionReceiver, networkStateChangedFilter)

    emitter.setCancellable {
        if (!emitter.isDisposed)
            context.applicationContext.unregisterReceiver(wifiConnectionReceiver)
    }

    wifiManager.enableNetwork(networkId, true)
}

有人能帮上忙吗?我真的被难住了。我传递的networkId是有效的,因为它是从addNetwork创建的,由于它没有返回-1,所以它成功了。

EN

回答 1

Stack Overflow用户

发布于 2018-12-13 02:30:30

好了,我终于弄明白了这一点,我希望我的答案能为将来遇到类似问题的任何人提供一些启发,因为这很糟糕,让我很头疼。

我问题中的代码并不完全正确,但它也不是我问题的根本原因。问题的根本原因是我错误地配置了通过WiFiConfigManager.addNetwork()WiFiConfig表中注册的WiFiConfig对象。

我对WifiConfigManager.addNetwork()的合同做了一个巨大的假设。我假设如果该操作成功(即未返回-1),则传递的WiFiConfig配置正确。这个假设是错误的,我正在创建的WiFiConfig上的allowedAuthAlgorithmsallowedProtocolsallowedKeyManagersallowedPairwiseCipher BitSet是不正确的,但是对addNetwork()的调用成功了。我相信这是因为对addNetwork()的调用实际上并不做任何事情,只是验证配置是否有效,可以放在WiFiConfig表中,这与验证它是否是给定WiFi访问点的正确配置有很大的不同。这一点得到了addNetwork()源代码中的注释的支持,这些注释没有像许多其他WiFiManager函数那样声明异步状态的传递,这表明(至少对我而言),由于调用addNetwork(),操作系统没有尝试与接入点进行通信。

由于一位同事提出了一个非常有用的建议:通过操作系统连接到有问题的接入点,然后将操作系统为该接入点创建的WiFiConfig对象与我自己的代码生成的对象进行比较,以找出差异,我注意到我的WiFiConfig配置不正确。在这之后不久,我解决了最初的问题。

现在,为什么我的WiFiConfig对象创建得不正确?这是因为我对如何配置WiFi (即各种术语以及所有协议、算法和密钥管理器背后的含义)知之甚少。因此,在阅读了官方文档但没有收集到太多有用的信息后,我转向StackOverflow问题和答案,找到了正确设置WiFiConfig的循环模式,它们似乎都使用BitWise运算符来创建Int值,该值最终被传递给WiFiConfig.allowedProtocols.set()WiFiConfig.allowedPairwiseCiphers.set()WiFiConfig.allowedKeyManagement.set()WiFiConfig.allowedAuthAlgorithm.set()函数。

事实证明,这些配置选项中的每一个的底层BitSet是一种数据结构,该数据结构维护位的动态大小调整向量,其中WiFiConfig对象中给定BitSet实例中的位的索引对应于String数组中的元素的索引,该元素与WiFiConfig对象内的上述BitSet隐式关联。因此,如果您希望提供多个protocolskeyManagementspairwiseCiphersauthAlgorithms,则需要在底层BitSet上调用set,传入与所选协议匹配的字符串数组元素所对应的正确索引。

在重写了我的WiFiConfig创建代码之后,这个问题自行解决了。虽然我在原始帖子中的代码中有一个bug,但它也已经被修复了。

下面是新的WiFiConfig创建代码:

代码语言:javascript
复制
/**
 * Emits a single of the [WifiConfiguration] created from the passed [scanResult] and [preSharedKey]
 */
private fun createWifiConfiguration(scanResult: WiFiScanResult, preSharedKey: String) = Single.fromCallable<WifiConfiguration> {
    val auth = scanResult.auth
    val keyManagement = scanResult.keyManagement
    val pairwiseCipher = scanResult.pairwiseCipher

    val config = WifiConfiguration()
    config.SSID = "\"" +  scanResult.ssid + "\""
    config.BSSID = scanResult.bssid

    if (auth.contains("WPA") || auth.contains("WPA2")) {
        config.allowedProtocols.set(WifiConfiguration.Protocol.WPA)
        config.allowedProtocols.set(WifiConfiguration.Protocol.RSN)
    }

    if (auth.contains("EAP"))
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.LEAP)
    else if (auth.contains("WPA") || auth.contains("WPA2"))
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN)
    else if (auth.contains("WEP"))
        config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED)

    if (keyManagement.contains("IEEE802.1X"))
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X)
    else if (auth.contains("WPA") && keyManagement.contains("EAP"))
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP)
    else if (auth.contains("WPA") && keyManagement.contains("PSK"))
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)
    else if (auth.contains("WPA2") && keyManagement.contains("PSK"))
        config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK)

    if (pairwiseCipher.contains("CCMP") || pairwiseCipher.contains("TKIP")) {
        config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP)
        config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP)
    }

    if (preSharedKey.isNotEmpty()) {
        if (auth.contains("WEP")) {
            if (preSharedKey.matches("\\p{XDigit}+".toRegex())) {
                config.wepKeys[0] = preSharedKey
            } else {
                config.wepKeys[0] = "\"" + preSharedKey + "\""
            }
            config.wepTxKeyIndex = 0
        } else {
            config.preSharedKey = "\"" + preSharedKey + "\""
        }
    }

    config
}

下面是新的连接代码:

代码语言:javascript
复制
/**
 * Connects to the wifi access point at specified [ssid] with specified [networkId]
 * And returns the [WifiInfo] of the network that has been connected to
 */
private fun connect(context: Context,
                    wifiManager: WifiManager,
                    ssid: String,
                    networkId: Int) = Single.create<WifiInfo> { emitter ->

    val wifiConnectionReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == WifiManager.NETWORK_STATE_CHANGED_ACTION) {
                val networkInfo = intent.getParcelableExtra<NetworkInfo>(WifiManager.EXTRA_NETWORK_INFO) ?: return

                if (networkInfo.detailedState == NetworkInfo.DetailedState.CONNECTED) {
                    val wifiInfo = intent.getParcelableExtra<WifiInfo>(WifiManager.EXTRA_WIFI_INFO) ?: return
                    if (ssid.unescape() == wifiInfo.ssid.unescape()) {
                        context.applicationContext.unregisterReceiver(this)
                        emitter.onSuccess(wifiInfo)
                    }
                }
            }
        }
    }

    val networkStateChangedFilter = IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION)
    networkStateChangedFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION)

    context.applicationContext.registerReceiver(wifiConnectionReceiver, networkStateChangedFilter)

    emitter.setCancellable {
        if (!emitter.isDisposed)
            context.applicationContext.unregisterReceiver(wifiConnectionReceiver)
    }

    wifiManager.enableNetwork(networkId, true)
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53713480

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档