android - Android WiFi Manager enableNetwork 返回 true 但 NetworkInfo 状态始终为 DISCONNECTED/SCANNING
问题描述
我已经编写了一些代码来启用具有给定 的网络networkId
,并通过BroadcastReceiver
. 但是,即使enableNetwork
返回 true(表示操作系统成功发出命令)我BroadcastReceiver
也从未收到NetworkInfo
withCONNECTED
状态,但它会收到 2 个事件:DISCONNECTED
然后DISCONNECTED/SCANNING
.
从我读过的所有官方文档和各种 SO 问题中,如果enableNetwork
返回 true,那么BroadcastReceiver
注册的处理NETWORK_STATE_CHANGED_ACTION
意图应该总是收到一个NetworkInfo
带有 state 的对象CONNECTED
。
这是代码:
/**
* 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。
解决方案
好的,我终于想通了,我希望我在这里的回答能为以后遇到类似问题的人提供一些启示,因为这很讨厌,让我很头疼。
我的问题中的代码并不完全正确,但是,它也不是我的问题的根本原因。问题的根本原因是我错误地配置WiFiConfig
了WiFiConfig
通过WiFiConfigManager.addNetwork()
.
我对 . 的合同做了一个巨大的假设WifiConfigManager.addNetwork()
。我假设如果该操作成功(即没有返回-1
),那么传递WiFiConfig
的配置正确。这个假设是不正确的,我创建的allowedAuthAlgorithms
,allowedProtocols
和allowedKeyManagers
onallowedPairwiseCipher
BitSet
不WiFiConfig
正确,但调用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
每个配置选项的基础是一个数据结构,它维护一个动态调整大小的位向量,其中BitSet
WiFiConfig 对象中给定实例中的位索引对应于String
数组中元素的索引隐式关联到BitSet
WiFiConfig 对象中的上述内容。因此,如果您希望提供多个protocols
, keyManagements
,pairwiseCiphers
或者authAlgorithms
您需要调用set
底层BitSet
,传入正确的索引,该索引对应于与所选协议匹配的 String 数组的元素。
重写我的WiFiConfig
创建代码后,问题自行解决。虽然我在原始帖子中的代码中有一个错误,但也已修复。
这是新的 WiFiConfig 创建代码:
/**
* 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
}
这是新的连接代码:
/**
* 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)
}
推荐阅读
- playwright - 相当于 cy.scrollTo('bottom') 的剧作家
- ios - 如何将 UICollectionViewDiffableDataSource reorderHandlers 与自定义组合布局一起使用?
- mips - 我不知道如何在 MIPS 编程中处理数组
- arrays - 如何使用powershell将字符串变量拆分为两个数组对象变量?
- python - 向 GCP IoT Core 发送状态和事件后,paho mqtt python 客户端经常断开连接并出现错误代码 7“连接丢失”
- c# - 如何匹配另一个对象的旋转增量?
- docker - 运行带有 FF_NETWORK_PER_BUILD 标志的 Gitlab CI 作业时, --network=host 是否仍连接到主机网络?
- javascript - 如何根据属性对对象数组进行排序
- reactjs - useSelector 重新渲染后无法获取状态的最新值并返回 null
- sql - 选择计数数高于平均计数数的每个计数行