Android Wifi-API 在 Android 10+ 上真的那么坏吗?

Is the Android Wifi-API really so broken on Android 10+?

我正在研究 Wifi 自动连接功能,我很震惊 API 是多么糟糕。

我现在使用了 5 种不同的 API,但我仍然没有以用户期望的方式获得它。

我有一个设置可以在 Android 10+ 上启用 wifi 自动连接我会试试这个:

  1. 检查我是否持有 ACCESS_WIFI_STATE 权限:
    appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
    
    当我拥有权限时,我继续 2。如果没有,我继续 6。
  2. 当我获得许可后,我会检查 Android 11+ 是否保存了之前的 运行 我的 wifi 建议 wifiManager.networkSuggestions.isNotEmpty() 如果是,我会检查我使用的是哪个 Wifi ' 当前连接见第 x 步。在较低级别我跳过这个并转到步骤 3
  3. 我将 wifi 建议 API 与 WifiNetworkSuggestion.Builder() 一起使用,并建议用户使用我的 wifi
    val status = wifiManager.addNetworkSuggestions(listOf(suggestion))
    // Strage bug: On first usage the return value is always SUCCESS
    val success = status == WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS
    
  4. 当用户接受我的建议时,我到目前为止都很好。我无法在 Android 10 上验证我的建议是否被接受。在 Android 11 这是可能的(见步骤 2)。但是我仍然不知道设备是否真的连接了 wifi。所以我转到第 7 步
  5. 当用户拒绝了我的建议时,我就快退出游戏了,我再也不能问用户了(从技术上讲,有一个隐藏得很好的功能,您可以在其中切换权限,但没有用户会发现它) .在 Android 10 上,我可以检查我是否连接了正确的 wifi,至少在理论上是这样(请参阅第 7 步)。
  6. 在 Android 11+ 我可以回退到 Settings.ACTION_WIFI_ADD_NETWORKS 意图,它总是可以添加 Wifi(缺点是用户可以共享 wifi 密码)
  7. 我需要验证之前的步骤是否有效以及用户是否确实连接到所需的 wifi。为此,我已经拥有权限 ACCESS_WIFI_STATEACCESS_NETWORK_STATEACCESS_COARSE_LOCATIONACCESS_FINE_LOCATION。然而 ConnectivityManager API 不 return 实际的 SSID,这让我发疯。
  8. 如果用户没有连接到正确的 wifi 我会尝试使用 Settings.Panel.ACTION_WIFI 操作让用户连接到正确的 wifi

总结一下:

真的这么乱还是有更简单的方法?


我目前正在这样访问 SSID:

private val currentSsid: String?
    get() =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val wifiInfo = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)?.transportInfo as? WifiInfo
            wifiInfo?.ssid
        } else {
            wifiManager.connectionInfo?.ssid
        }

基于 Unable to get WIFI SSID using onCapabilitiesChanged in Android 12 我认为不支持我访问该值的方式。我目前得到的值是 "<unknown ssid>".

好吧,只回答了一半,但无论如何它可能会有帮助。下面是我获取用户当前SSID的方法(需要持有定位权限):

fun updateSsid(networkCapabilities: NetworkCapabilities) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        currentSsid = (networkCapabilities.transportInfo as? WifiInfo)?.ssid?.trim('"')
    }
}

wifiCallback = when {
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
        object: ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
            override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
                updateSsid(networkCapabilities)
            }
        }
    }
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q -> {
        object: ConnectivityManager.NetworkCallback() {
            override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
                updateSsid(networkCapabilities)
            }
        }
    }
    else -> null
}

wifiCallback?.let {
    fragment.lifecycle.addObserver(object: DefaultLifecycleObserver {
        override fun onResume(owner: LifecycleOwner) {
            connectivityManager.registerNetworkCallback(request, it)
        }

        override fun onPause(owner: LifecycleOwner) {
            connectivityManager.unregisterNetworkCallback(it)
        }
    })
}

所以基本上,您的代码的某些方面需要检查 api 的级别 <10、10、11 和 12。真是一团糟。

如果您希望用户选择配置的 wifi,这可能非常方便:

fun showWifiDialog() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        try {
            fragment.startActivity(Intent(android.provider.Settings.Panel.ACTION_WIFI))
        } catch (e: Exception) {
            // ignored
        }
    }
}