Tho*_*ook 7 android wifimanager android-wifi kotlin rx-java2
TO BE CLEAR:
The most likely part of the code which has the problem is the connect function, which you can find in the code block.
EDIT:
I've had a good dig through LogCat and found something interesting (this occurred the exact moment enableNetwork was called):
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
Run Code Online (Sandbox Code Playgroud)
PERMISSIONS:
<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" />
Run Code Online (Sandbox Code Playgroud)
I've been tasked to create a part of an app where the user will be able to see a list of scanned WiFi access points (ScanResult
), be able to select one, and, if it requires auth, be presented with a screen where they enter the PSK
. After entering the PSK
, the system will attempt to connect to the access point by first creating and configuring a WifiConfig
object, using addNetwork
to add the config to the Wifi config table, then disconnect
, enableNetwork
and reconnect
(in that order).
I'm using RX-Java2
so that I can chain the various steps of the network setup. For instance, the disconnect
method returns a Completable
which emits a completed event if WifiManager.disconnect()
succeeds. It does so by registering a BroadcastReceiver
to listen for NETWORK_STATE_CHANGED_ACTION
and then emitting a completion event if the networkInfo extra has detailed state DISCONNECTED
. The same logic applies for the connect()
function.
Now, addNetwork()
is succeeding (so my WiFi config functions are correct), and then I am chaining disconnect Completable
to the connect Single
using andThen
. I have put breakpoints in my code and can see that everything is running in the correct order, disconnect is succeeding and then has it's broadcast receiver registered successfully, but enableNetwork()
call is returning false (indicating that the enableNetwork
command failed to be issued by the OS).
I am 99% sure this is not an issue with how I'm using RX, but, given that addNetwork
and disconnect
are succeeding (indicating my wifi config creation code is fine) I am starting to wonder if either A) My RX code is wrong, or, B) My WiFi config creation is wrong.
因此,我将发布以下所有代码以及用例,并非常感谢任何建议.
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("\\p{XDigit}+".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
Run Code Online (Sandbox Code Playgroud)
好吧,我终于弄明白这一点了,我希望我的答案能为未来遇到类似问题的人带来一些启示,因为这是令人讨厌的,让我头痛不已.
问题的根本原因是我错误地配置了通过表WiFiConfig
中注册的对象.WiFiConfig
WiFiConfigManager.addNetwork()
我曾就合同做出了大量假设WifiConfigManager.addNetwork()
.我假设如果该操作成功(即没有返回-1
),则WiFiConfig
正确配置传递.这种假设是不正确的allowedAuthAlgorithms
,allowedProtocols
,allowedKeyManagers
和allowedPairwiseCipher
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
WiFiConfig对象中的关联数组.因此,如果希望提供多个protocols
,keyManagements
,pairwiseCiphers
或authAlgorithms
你需要调用set
于底层相应BitSet
,传递,其中将对应于所述的元件的正确索引隐式地,其匹配所选择的协议相关联的字符串数组.
重写我的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
}
Run Code Online (Sandbox Code Playgroud)
这是新的连接代码:
/**
* 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)
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1025 次 |
最近记录: |