Android 10+上的Android Wifi-API真的如此不可靠吗?

5

我正在开发Wifi自动连接功能,但惊奇地发现API存在严重问题。

我现在使用了5个不同的API,但仍无法按照用户期望的方式实现。

我有一个设置项可以在Android 10+上启用Wifi自动连接,我将尝试使用它:

  1. Check if I hold the ACCESS_WIFI_STATE permission with:
    appOpsManager.unsafeCheckOp("android:change_wifi_state", Process.myUid(), context.packageName) == AppOpsManager.MODE_ALLOWED
    
    When I own the permission I go on with 2. if not I continue with 6.
  2. When I hold the permission I'll check on Android 11+ if from a previous run my wifi suggestion was saved wifiManager.networkSuggestions.isNotEmpty() if that is true I check which Wifi I'm currently connected with see step x. On lower levels I skip this and go to step 3
  3. I use the wifi suggestion API with the WifiNetworkSuggestion.Builder() and suggest the user my wifi with
    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. When the user accepts my suggestion I'm fine so far. I cannot verify on Android 10 if my suggestion was accepted. On Android 11 this is possible (see step 2). However I still don't know if the device actually connected with the wifi. So I go to step 7
  5. When the user rejected my suggestion I'm almost out of the game I can never ask the user again (well technically there is a well hidden feature where you can switch the permission, but no user will ever find it). On Android 10 I can just check if I'm connected with the right wifi, well at least theoretically (see step 7).
  6. On Android 11+ I can fallback to the Settings.ACTION_WIFI_ADD_NETWORKS intent which can always add Wifis (with the downside that the user can share the wifi password)
  7. I need to verify if the steps before worked and the user is actually connected to the desired wifi. To do so I already own the permissions ACCESS_WIFI_STATE, ACCESS_NETWORK_STATE, ACCESS_COARSE_LOCATION and ACCESS_FINE_LOCATION. However the ConnectivityManager API don't return the actual SSID, that drives me crazy.
  8. If the user is not connected to the right wifi I would try to use the Settings.Panel.ACTION_WIFI action to let the user to connect to the right wifi

总结一下:

  • 我使用了 WifiNetworkSuggestion API
  • 尽可能使用 Settings.ACTION_WIFI_ADD_NETWORKS 作为备选方案
  • 我尝试验证用户当前连接的是正确的wifi
  • 我尝试使用 Settings.Panel.ACTION_WIFI 操作来帮助用户连接到正确的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>"


我不确定为什么你有第一步。ACCESS_WIFI_STATE是一个普通权限,自Android 1.0以来就存在。除了已经root的设备或者疯狂的ROM之外,如果你的应用程序已经安装并运行,你就拥有这个权限。你是否看到其他情况? - CommonsWare
然而,ConnectivityManager API并不返回实际的SSID,这让我很疯狂。但是,对于我来说,它确实可以通过ACCESS_FINE_LOCATION返回。请注意,根据情况,您可能会得到SSID的null值,特别是如果您正在连接一个没有互联网访问权限且用户尚未接受通知的网络。一旦他们接受了通知,您应该就能获取到SSID了。 - CommonsWare
嘿@CommonsWare,很高兴引起了您的注意。关于ACCESS_WIFI_STATE权限检查,请查看此错误:https://issuetracker.google.com/issues/224071894。似乎当您按“不,谢谢”时,该权限会被撤销(更改方法也在那里记录)。 - rekire
关于wifi ssid,我得到的值是"<unknown ssid>"。请参见无法在Android 12中使用onCapabilitiesChanged获取WIFI SSID,我认为我访问该值的方式不受支持。现在我将尝试使用那个标志来广播接收器。 - rekire
关于前者,谢谢!这是新的(也令人不安),但我还没有使用建议API(尽管我可能很快会用)。关于后者,那可能是未接受通知值而不是null——自从我上次探索这种情况以来已经有一段时间了。我知道在用户接受该通知之前有一个中间值。然而,我没有在回调中使用onCapabilitiesChanged();也许getNetworkCapabilities()的行为有所不同。 - CommonsWare
我没有验证他们何时更改了行为,但在Android 12上,那是一个很好的对话框。在我看来,这实际上非常有帮助。我已经花了整整一周的时间来开发这个功能,并且从Android 1.0的RC时期开始就一直在使用Android。 - rekire
1个回答

4

只有部分答案,不过或许还是有帮助的。以下是我获取用户当前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)
        }
    })
}

基本上,你的代码需要检查小于10、10、11和12级别的API。真是一团糟。
如果你希望用户选择配置的Wi-Fi,则这可能非常方便。
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
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接