Android WiFiManager enableNetwork 返回 false
Posted
技术标签:
【中文标题】Android WiFiManager enableNetwork 返回 false【英文标题】:Android WiFiManager enableNetwork returning false 【发布时间】:2019-05-06 07:26:41 【问题描述】:明确:
代码中最有可能出现问题的部分是connect函数,您可以在代码块中找到它。
编辑:
我对 LogCat 进行了深入研究,发现了一些有趣的东西(这恰好发生在 enableNetwork 被调用的那一刻):
2018-12-04 20:13:14.508 1315-7000/? I/WifiService: enableNetwork uid=10158 disableOthers=true
2018-12-04 20:13:14.508 1315-1607/? D/WifiStateMachine: connectToUserSelectNetwork netId 49, uid 10158, forceReconnect = false
2018-12-04 20:13:14.541 1315-1607/? D/WifiConfigStore: Writing to stores completed in 14 ms.
2018-12-04 20:13:14.541 1315-1607/? E/WifiConfigManager: UID 10158 does not have permission to update configuration "SKYD7F55"WPA_PSK
2018-12-04 20:13:14.541 1315-1607/? I/WifiStateMachine: connectToUserSelectNetwork Allowing uid 10158 with insufficient permissions to connect=49
权限:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
我的任务是创建应用程序的一部分,用户将能够在其中看到扫描的 WiFi 接入点 (ScanResult
) 的列表,能够选择一个,如果需要身份验证,请出现一个屏幕,他们在其中输入PSK
。输入PSK
后,系统将尝试连接接入点,首先创建并配置WifiConfig
对象,使用addNetwork
将配置添加到Wifi配置表,然后disconnect
,enableNetwork
和reconnect
(按此顺序)。
我正在使用RX-Java2
,以便我可以链接网络设置的各个步骤。例如,disconnect
方法返回一个Completable
,如果WifiManager.disconnect()
成功,它会发出一个完成的事件。它通过注册BroadcastReceiver
来监听NETWORK_STATE_CHANGED_ACTION
,然后在networkInfo extra 具有详细状态DISCONNECTED
时发出完成事件。同样的逻辑也适用于connect()
函数。
现在,addNetwork()
成功了(所以我的 WiFi 配置函数是正确的),然后我使用 andThen
将断开连接 Completable
链接到连接 Single
。我在我的代码中设置了断点,可以看到一切都以正确的顺序运行,断开连接成功,然后它的广播接收器注册成功,但是enableNetwork()
调用返回false(表明enableNetwork
命令失败由操作系统发布)。
我 99% 确定这不是我如何使用 RX 的问题,但是,鉴于 addNetwork
和 disconnect
正在成功(表明我的 wifi 配置创建代码很好),我开始怀疑是否A) 我的 RX 代码错误,或者,B) 我的 WiFi 配置创建错误。
因此,我将发布下面的所有代码以及用例,非常感谢任何建议。
Emitters.kt:
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.NetworkInfo
import android.net.wifi.SupplicantState
import android.net.wifi.WifiConfiguration
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import com.google.common.base.Optional
import io.reactivex.*
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import java.lang.Exception
private const val TAG = "Emitters"
private const val SIGNAL_STRENGTH_RANGE = 4
/**
* Use case of these emitters (in presenter or interactor):
*
* // If network is open, then go ahead and connect to it, else show enter password form
* isNetworkOpen(context, scanResult).flatMapCompletable isNetworkOpen ->
* if (isNetworkOpen)
* connectToOpenWifi(context, scanResult.ssid, scanResult.bssid)
* else
* Completable.error(WifiException("The specified network requires a password")
*
* .subscribeOn(Schedulers.io())
* .observeOn(AndroidSchedulers.mainThread())
* .subscribe(
* view?.showSuccessfullyConnected()
* , error ->
* when (error)
* is WifiAuthException ->
* val auth = error.wifiScanResult.auth
* val keyManagement = error.wifiScanResult.keyManagement
* val security = "$auth/$keyManagement"
* viewStateStack.add(NetworkSummaryViewState(error.wifiScanResult, security))
* switchToViewState(viewStateStack.peek())
* else ->
* viewStateStack.add(FailedToConnectViewState(networkName))
* switchToViewState(viewStateStack.peek())
*
*
*
* )
*
* // Called by view to connect to closed network with provided password
* connectToClosedWifi(context, scanResult, password)
* .subscribeOn(Schedulers.io())
* .observeOn(AndroidSchedulers.mainThread())
* .subscribe(
* view?.showSuccessfullyConnected()
* , error ->
* view?.showFailedToConnect()
* )
*/
/**
* Creates a Flowable that emits WiFiScanResults
*/
fun wifiScanResults(context: Context): Flowable<Set<WiFiScanResult>> = Flowable.create<Set<WiFiScanResult>> ( emitter ->
val wifiManagerWrapper = WifiManagerWrapper(context.applicationContext)
if (!wifiManagerWrapper.wifiManager.isWifiEnabled && !wifiManagerWrapper.wifiManager.setWifiEnabled(true))
wifiManagerWrapper.dispose()
emitter.onError(WiFiException("WiFi not enabled and couldn't enable it"))
return@create
// Broadcast receiver that handles wifi scan results
val wifiScanReceiver = object : BroadcastReceiver()
override fun onReceive(context: Context, intent: Intent)
if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
val scanResults = wifiManagerWrapper.wifiManager.scanResults
if (scanResults !== null)
emitter.onNext(scanResults.map scanResult ->
val signalStrength = WifiManager.calculateSignalLevel(scanResult.level, SIGNAL_STRENGTH_RANGE)
val capabilities = scanResult.capabilities.substring(1, scanResult.capabilities.indexOf(']') -1)
.split('-')
.toSet()
WiFiScanResult(scanResult.SSID,
scanResult.BSSID,
capabilities.elementAtOrNull(0) ?: "",
capabilities.elementAtOrNull(1) ?: "",
capabilities.elementAtOrNull(2) ?: "",
signalStrength)
.toSet())
if (!wifiManagerWrapper.wifiManager.startScan())
emitter.onError(WiFiException("WiFi not enabled"))
val wifiScanResultsIntentFilter = IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)
context.applicationContext.registerReceiver(wifiScanReceiver, wifiScanResultsIntentFilter)
emitter.setCancellable
context.unregisterReceiver(wifiScanReceiver)
wifiManagerWrapper.dispose()
if (!wifiManagerWrapper.wifiManager.startScan())
emitter.onError(WiFiException("WiFi not enabled"))
, BackpressureStrategy.LATEST).subscribeOn(Schedulers.io())
/**
* Returns a single indicating if the [scanResult] is open
*/
fun isNetworkOpen(context: Context,
scanResult: WiFiScanResult): Single<Boolean> = Single.create<Boolean> emitter ->
val wifiManagerWrapper = WifiManagerWrapper(context.applicationContext)
emitter.setCancellable
wifiManagerWrapper.dispose()
if (scanResult.auth.contains("WEP"))
emitter.onSuccess(true)
else
emitter.onSuccess(false)
/**
* Attempts to connect to an open wifi access point specified by [scanResult]
* Emits a completed event if successful, else emits an error
*/
fun connectToOpenWifi(context: Context,
scanResult: WiFiScanResult): Completable = Completable.create emitter ->
val ssid = scanResult.ssid
val bssid = scanResult.bssid
val wifiManagerWrappper = WifiManagerWrapper(context.applicationContext)
if (!wifiManagerWrappper.wifiManager.isWifiEnabled && !wifiManagerWrappper.wifiManager.setWifiEnabled(true))
wifiManagerWrappper.dispose()
emitter.onError(WiFiException("Wifi not enabled"))
val updateWifiStateObs = getExistingConfiguration(wifiManagerWrappper.wifiManager, ssid, bssid).flatMap existingConfig ->
if (!existingConfig.isPresent)
createOpenWifiConfiguration(scanResult).flatMap wifiConfig ->
val newNetworkId = wifiManagerWrappper.wifiManager.addNetwork(wifiConfig)
if (newNetworkId < 0)
throw WiFiException("Failed to add new access point $scanResult.ssid")
val currentWifiConnection = wifiManagerWrappper.wifiManager.connectionInfo
if (currentWifiConnection !== null)
disconnect(context, wifiManagerWrappper.wifiManager).andThen(
connect(context, wifiManagerWrappper.wifiManager, wifiConfig.SSID, newNetworkId)
)
else
connect(context, wifiManagerWrappper.wifiManager, wifiConfig.SSID, newNetworkId)
else
Single.just(existingConfig.get())
val compositeDisposable = CompositeDisposable()
emitter.setCancellable
compositeDisposable.clear()
wifiManagerWrappper.dispose()
try
compositeDisposable.add(updateWifiStateObs.subscribe(
emitter.onComplete()
, error ->
emitter.onError(error)
))
catch (ex: Exception)
compositeDisposable.clear()
wifiManagerWrappper.dispose()
emitter.onError(ex)
/**
* Attempts to connect to an closed [scanResult] by providing the given [preSharedKey]
* Emits a completed event if successful, else emits an error
*/
fun connectToClosedWifi(context: Context,
scanResult: WiFiScanResult,
preSharedKey: String): Completable = Completable.create emitter ->
val ssid = scanResult.ssid
val bssid = scanResult.bssid
val wifiManagerWrappper = WifiManagerWrapper(context.applicationContext)
if (!wifiManagerWrappper.wifiManager.isWifiEnabled && !wifiManagerWrappper.wifiManager.setWifiEnabled(true))
wifiManagerWrappper.dispose()
emitter.onError(WiFiException("Wifi not enabled"))
val updateWifiStateObs =
getExistingConfiguration(wifiManagerWrappper.wifiManager, ssid, bssid).flatMap existingConfig ->
if (!existingConfig.isPresent)
createClosedWifiConfiguaration(scanResult, preSharedKey).flatMap wifiConfig ->
val newNetworkId = wifiManagerWrappper.wifiManager.addNetwork(wifiConfig)
if (newNetworkId < 0)
throw WiFiException("Failed to add new access point $scanResult.ssid")
val currentWifiConnection = wifiManagerWrappper.wifiManager.connectionInfo
if (currentWifiConnection !== null)
disconnect(context, wifiManagerWrappper.wifiManager).andThen(
connect(context, wifiManagerWrappper.wifiManager, wifiConfig.SSID, newNetworkId)
)
else
connect(context, wifiManagerWrappper.wifiManager, wifiConfig.SSID, newNetworkId)
else
Single.just(existingConfig.get())
val compositeDisposable = CompositeDisposable()
emitter.setCancellable
compositeDisposable.clear()
wifiManagerWrappper.dispose()
try
compositeDisposable.add(updateWifiStateObs.subscribe(
emitter.onComplete()
, error ->
emitter.onError(error)
))
catch (ex: Exception)
compositeDisposable.clear()
wifiManagerWrappper.dispose()
emitter.onError(ex)
/**
* Wrapper class for WiFiManager that creates a multicast lock to make the app handle multicast wifi packets
* Handles disposing of the lock and cleaning up of resources via the dispose method
*/
private class WifiManagerWrapper(context: Context)
val wifiManager = context.applicationContext.getSystemService(Context.WIFI_SERVICE)
as? WifiManager ?: throw IllegalStateException("Could not get system Context.WIFI_SERVICE")
// Create and acquire a multicast lock to start receiving multicast wifi packets
private var lock = wifiManager.createMulticastLock(TAG + "_lock").apply
acquire()
// Dispose of the lock
fun dispose()
if (lock.isHeld)
try
lock.release()
catch (ignore: Exception)
EventReporter.i(TAG, "Failed to release lock on wifi manager wrapper")
lock = null
/**
* Disconnects from the connected wifi network and emits a completed event if no errors occurred
*/
private fun disconnect(context: Context,
wifiManager: WifiManager) = Completable.create emitter ->
val wifiDisconnectionReceiver = 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.DISCONNECTED)
context.applicationContext.unregisterReceiver(this)
emitter.onComplete()
val networkStateChangedFilter = IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION)
context.applicationContext.registerReceiver(wifiDisconnectionReceiver, networkStateChangedFilter)
emitter.setCancellable
if (!emitter.isDisposed)
context.applicationContext.unregisterReceiver(wifiDisconnectionReceiver)
if (!wifiManager.disconnect())
emitter.onError(WiFiException("Failed to issue disconnect command to wifi subsystem"))
/**
* 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)
wifiManager.reconnect()
/**
* Returns a Single, wrapping an Optional.absent if no existing configuration exists with the passed [ssid] and [bssid], else the found [WifiConfiguration]
*/
private fun getExistingConfiguration(wifiManager: WifiManager,
ssid: String,
bssid: String) = Single.create<Optional<WifiConfiguration>> emitter ->
val configuredNetworks = wifiManager.configuredNetworks
if (configuredNetworks.isEmpty())
emitter.onSuccess(Optional.absent())
emitter.onSuccess(Optional.fromNullable(configuredNetworks.firstOrNull configuredNetwork ->
configuredNetwork.SSID.unescape() == ssid && configuredNetwork.BSSID == bssid
))
/**
* Emits a single of the open [WifiConfiguration] created from the passed [scanResult]
*/
private fun createOpenWifiConfiguration(scanResult: WiFiScanResult) = 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
var allowedProtocols = 0
when
auth.isEmpty() ->
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.WPA
auth.contains("WPA2") -> allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
auth.contains("WPA") ->
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.WPA
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
config.allowedProtocols.set(allowedProtocols)
var allowedAuthAlgos = 0
when
auth.contains("EAP") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.LEAP
auth.contains("WPA") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.OPEN
auth.contains("WEP") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.SHARED
config.allowedAuthAlgorithms.set(allowedAuthAlgos)
var allowedKeyManagers = WifiConfiguration.KeyMgmt.NONE
if (keyManagement.contains("IEEE802.1X"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.IEEE8021X
else if (auth.contains("WPA") && keyManagement.contains("EAP"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.WPA_EAP
else if (auth.contains("WPA") && keyManagement.contains("PSK"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.WPA_PSK
config.allowedKeyManagement.set(allowedKeyManagers)
var allowedPairWiseCiphers = WifiConfiguration.PairwiseCipher.NONE
if (pairwiseCipher.contains("CCMP"))
allowedPairWiseCiphers = allowedPairWiseCiphers or WifiConfiguration.PairwiseCipher.CCMP
if (pairwiseCipher.contains("TKIP"))
allowedPairWiseCiphers = allowedPairWiseCiphers or WifiConfiguration.PairwiseCipher.TKIP
config.allowedPairwiseCiphers.set(allowedPairWiseCiphers)
config
/**
* Emits a single of the closed [WifiConfiguration] created from the passed [scanResult] and [preSharedKey]
* Or, emits an error signalling the [preSharedKey] was empty
*/
private fun createClosedWifiConfiguaration(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
var allowedProtocols = 0
when
auth.isEmpty() ->
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.WPA
auth.contains("WPA2") -> allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
auth.contains("WPA") ->
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.WPA
allowedProtocols = allowedProtocols or WifiConfiguration.Protocol.RSN
config.allowedProtocols.set(allowedProtocols)
var allowedAuthAlgos = 0
when
auth.contains("EAP") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.LEAP
auth.contains("WPA") || auth.contains("WPA2") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.OPEN
auth.contains("WEP") -> allowedAuthAlgos = allowedAuthAlgos or WifiConfiguration.AuthAlgorithm.SHARED
config.allowedAuthAlgorithms.set(allowedAuthAlgos)
var allowedKeyManagers = WifiConfiguration.KeyMgmt.NONE
if (keyManagement.contains("IEEE802.1X"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.IEEE8021X
else if (auth.contains("WPA") && keyManagement.contains("EAP"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.WPA_EAP
else if (auth.contains("WPA") && keyManagement.contains("PSK"))
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.WPA_PSK
else if (preSharedKey.isNotEmpty())
allowedKeyManagers = allowedKeyManagers or WifiConfiguration.KeyMgmt.WPA_PSK
else if (preSharedKey.isEmpty())
allowedKeyManagers = allowedAuthAlgos or WifiConfiguration.KeyMgmt.WPA_PSK
config.allowedKeyManagement.set(allowedKeyManagers)
var allowedPairWiseCiphers = WifiConfiguration.PairwiseCipher.NONE
if (pairwiseCipher.contains("CCMP"))
allowedPairWiseCiphers = allowedPairWiseCiphers or WifiConfiguration.PairwiseCipher.CCMP
if (pairwiseCipher.contains("TKIP"))
allowedPairWiseCiphers = allowedPairWiseCiphers or WifiConfiguration.PairwiseCipher.TKIP
config.allowedPairwiseCiphers.set(allowedPairWiseCiphers)
if (preSharedKey.isNotEmpty())
if (auth.contains("WEP"))
if (preSharedKey.matches("\\pXDigit+".toRegex()))
config.wepKeys[0] = preSharedKey
else
config.wepKeys[0] = "\"" + preSharedKey + "\""
config.wepTxKeyIndex = 0
else
config.preSharedKey = "\"" + preSharedKey + "\""
config
/**
* Extension function to remove escaped " from a string
*/
private fun String.unescape() =
if (this.startsWith("\""))
this.replace("\"", "")
else
this
【问题讨论】:
您正在使用哪种设备进行测试?什么Android版本?以后的版本将需要一个运行时权限对话框,以便用户授予其中一些权限。 我已经在运行 8.1.0 的 OnePlus 5 上进行了测试。我已向用户请求 COARSE_LOCATION 的运行时许可,并已在我的测试设备上授予它。 还在运行 Android 8.1.0 的已扎根三星 S7 上进行了测试 你可能想检查这个答案。 ***.com/a/40497793/5053013 另外,我们发现Android的保存配置并不总是存储BSSID。您可能会考虑将此代码更改为仅检查 SSID:configuredNetwork.SSID.unescape() == ssid && configuredNetwork.BSSID == bssid
到 configuredNetwork.SSID.unescape() == ssid
---- 旁注 #2 我注意到您不听 SupplicantState.ASSOCIATING
。真正的连接/断开只有在您不再关联时才存在。
【参考方案1】:
好的,我终于想通了,我希望我在这里的回答对以后遇到类似问题的人有所启发,因为这很讨厌,让我很头疼。
问题的根本原因是我错误地配置了通过WiFiConfigManager.addNetwork()
在WiFiConfig
表中注册的WiFiConfig
对象。
我对@987654324@ 的合同做了很大的假设。我假设如果该操作成功(即没有返回-1
),那么传递的WiFiConfig
配置正确。这个假设不正确,我创建的WiFiConfig
上的allowedAuthAlgorithms
、allowedProtocols
、allowedKeyManagers
和allowedPairwiseCipher
BitSet
不正确,但对addNetwork()
的调用成功。我相信这是因为对addNetwork()
的调用实际上并没有做任何事情,除了验证配置是否可以放入WiFiConfig
表中,这与验证它是否 完全不同>是给定 WiFi 接入点的正确配置。这由 addNetwork()
源代码中的 cmets 支持,它不像许多其他 WiFiManager
函数那样声明异步状态的传递,这表明(至少对我而言)没有尝试与访问进行通信由于调用addNetwork()
,操作系统提出了这一点。
由于一位同事提出了一个非常有用的建议,即通过操作系统连接到有问题的接入点,然后将操作系统为该接入点创建的 WiFiConfig
对象与我自己的代码生成的对象进行比较以发现差异注意到我的WiFiConfig
配置不正确。不久之后,我解决了最初的问题。
现在,为什么我的 WiFiConfig
对象创建不正确?那是因为我对如何配置 WiFi 知之甚少(即各种术语以及所有协议、算法和密钥管理器背后的含义)。因此,在阅读了官方文档并没有收集到太多有用的信息后,我转向 *** 问题和答案,并发现了一种用于正确设置 WiFiConfig
的重复模式,它们似乎都使用 BitWise
运算符来创建 Int
值最终传递给WiFiConfig.allowedProtocols.set()
、WiFiConfig.allowedPairwiseCiphers.set()
、WiFiConfig.allowedKeyManagement.set()
和WiFiConfig.allowedAuthAlgorithm.set()
函数。
事实证明,每个配置选项的底层 BitSet
是一个数据结构,它维护一个动态调整大小的位向量,其中 WiFiConfig 对象中给定 BitSet
实例中的位索引 隐式对应于WiFiConfig对象内隐式关联String
数组中元素的索引。因此,如果您希望提供多个protocols
、keyManagements
、pairwiseCiphers
或authAlgorithms
,则需要在底层对应的BitSet
上调用set
,并传入与元素对应的正确索引与所选协议匹配的隐式关联的字符串数组。
重写我的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("\\pXDigit+".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)
【讨论】:
所以你知道网络何时真正连接(BroadcastReceiver被触发),但我如何确定它没有连接成功? 在广播接收器中,您可以在if语句中添加else case来处理不同的状态。您可能想查看NetworkInfo.DetailedState.FAILED
。但是,NetworkInfo 类已被弃用,因此请谨慎操作。以上是关于Android WiFiManager enableNetwork 返回 false的主要内容,如果未能解决你的问题,请参考以下文章
WifiManager.getScanResults() 未在 Android Jelly Bean 上提供 SSID
Android WifiManager::getScanResults() 仍然返回空列表