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配置表,然后disconnectenableNetworkreconnect(按此顺序)。

我正在使用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 的问题,但是,鉴于 addNetworkdisconnect 正在成功(表明我的 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 &amp;&amp; configuredNetwork.BSSID == bssidconfiguredNetwork.SSID.unescape() == ssid ---- 旁注 #2 我注意到您不听 SupplicantState.ASSOCIATING。真正的连接/断开只有在您不再关联时才存在。 【参考方案1】:

好的,我终于想通了,我希望我在这里的回答对以后遇到类似问题的人有所启发,因为这很讨厌,让我很头疼。

问题的根本原因是我错误地配置了通过WiFiConfigManager.addNetwork()WiFiConfig 表中注册的WiFiConfig 对象。

我对@9​​87654324@ 的合同做了很大的假设。我假设如果该操作成功(即没有返回-1),那么传递的WiFiConfig 配置正确。这个假设不正确,我创建的WiFiConfig 上的allowedAuthAlgorithmsallowedProtocolsallowedKeyManagersallowedPairwiseCipher 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数组中元素的索引。因此,如果您希望提供多个protocolskeyManagementspairwiseCiphersauthAlgorithms,则需要在底层对应的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() 仍然返回空列表

WifiManager 中的 startScan() 在 Android P 中已弃用

android 得到连接热点的ip的方法

android manager wifi 怎么用

android WifiManager中Rssi是啥意思