如果启用了 VPN,如何获取 WiFi 的 SSID?

How to get SSID of WiFi if VPN is enabled?

提问人:moonlightcheese 提问时间:11/17/2023 更新时间:11/17/2023 访问量:12

问:

我已注册 .手机已打开并连接了 WiFi,但是当我调用它时,它会给出一个类名,而该类名在我可以找到的 Android SDK 文档中不存在。如果设备通过 VPN 连接,如何获取 SSID?为什么这个类不在文档中?NetworkCallbackconnectivityManager.registerDefaultNetworkCallback(networkCallback)capabilities.transportInfo!!::class.qualifiedNameandroid.net.VpnTransportInfo

初始化:

private val networkRequest = NetworkRequest.Builder()
        .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
        .build()
init {
    //upon register, callback will be called, so no need to initialize
    
    //registerDefaultNetworkCallback does not call back with a WifiInfo when using VPN
    //connectivityManager.registerDefaultNetworkCallback(networkCallback)

    //should return only WifiInfo as TransportInfo since networkRequest only requests Wifi transport type
    connectivityManager.registerNetworkCallback(networkRequest, networkCallback)
}

NetworkCallback 实现:

private val networkCallback = object : ConnectivityManager.NetworkCallback() {
    override fun onAvailable(network: Network) {
        super.onAvailable(network)
        logger.warn("NETWORK AVAILABLE: $network")
        _connectivityState.update {
            getConnectivityState()
        }
    }

    override fun onCapabilitiesChanged(
        network: Network,
        networkCapabilities: NetworkCapabilities
    ) {
        super.onCapabilitiesChanged(network, networkCapabilities)
        logger.warn("NETWORK CAPABILITIES CHANGED: $network")
        _connectivityState.update {
            getConnectivityState()
        }
    }

    override fun onLost(network: Network) {
        super.onLost(network)
        logger.warn("NETWORK LOST: $network")
        _connectivityState.update {
            getConnectivityState()
        }
    }
}

getConnectivity 实现:

fun getConnectivityState(
    network: Network? = connectivityManager.getActiveNetwork()
): ConnectivityState {
    //these objects represent information about the currently active network
    val capabilities = connectivityManager.getNetworkCapabilities(network)
    val linkProperties = connectivityManager.getLinkProperties(network)
    val connectivityState = ConnectivityState()

    if(wifiManager.isWifiEnabled) {
        //if wifi is enabled, it should be the current active network
        val isWifi = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
        if(isWifi != null) {
            if(isWifi) {
                //the current active link is a WiFi connection
                logger.info("Network is WiFi.")
                connectivityState.source = ConnectivitySource.WIFI
                if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
                    logger.info("Version code:  ${Build.VERSION.SDK_INT} >= ${Build.VERSION_CODES.S}, using capabilities.getTransportInfo()")
                    val transportInfo = capabilities.transportInfo
                    logger.info("Got TransportInfo")
                    if(transportInfo is WifiInfo) {
                        logger.info("TransportInfo 'is' WifiInfo")
                        connectivityState.ssid = transportInfo.ssid
                        logger.info("WifiInfo SSID" + connectivityState.ssid)
                    } else {
                        logger.info("TransportInfo 'is NOT' WifiInfo (" + transportInfo!!::class.qualifiedName + ")")
                    }
                } else {
                    logger.info("Version code:  ${Build.VERSION.SDK_INT} < ${Build.VERSION_CODES.S}, using wifiManager.connectionInfo")
                    //old method of obtaining WifiInfo through ConnectivityManager
                    val wifiInfo = wifiManager.connectionInfo
                    connectivityState.ssid = wifiInfo.ssid
                    logger.info("WifiInfo SSID" + connectivityState.ssid)
                }
            } else {
                //the current link does not have the WiFi transport classification
                logger.info("WiFi is enabled but network link is NOT WiFi!")
            }
        } else {
            //can't get information about wifi?
            logger.error("Unable to discover if network is WiFi!  Transport == WiFi returned null.")
        }
    } else {
        //wifi is disabled, but should probably be enabled for the app to connect
        logger.warn("WiFi is disabled!")
    }

    val isCellular = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
    if(isCellular != null) {
        if(isCellular) {
            logger.info("Network is cellular data.")
            connectivityState.source = ConnectivitySource.CELLULAR
        } else {
            logger.info("Network is NOT cellular data.")
        }
    } else {
        logger.error("Unable to discover if network is cellular!  Transport == cellular returned null.")
    }

    return connectivityState
}

相关日志行,为便于阅读而缩写:

NETWORK CAPABILITIES CHANGED: 272
Network is WiFi.
Version code:  34 >= 31, using capabilities.getTransportInfo()
Got TransportInfo
TransportInfo 'is NOT' WifiInfo (android.net.VpnTransportInfo)
Network is NOT cellular data.
安卓 Kotlin

评论


答:

0赞 LPanic 11/25/2023 #1

类 android.net.VpnTransportInfo 未记录在公共 SDK 中,它似乎是一个内部类或隐藏 API 的一部分。这可能就是您在官方 Android 文档中找不到有关它的信息的原因。

要在设备通过 VPN 连接时获取 SSID,可以考虑使用替代方法。您可以使用 ConnectivityManager.getNetworkInfo(network) 方法获取有关网络的信息,然后将其强制转换为 WifiInfo 以获取 SSID,而不是依赖于 capabilities.transportInfo 属性。

我已经修改了您的代码,您可能想尝试一下:

    override fun onCapabilitiesChanged(
    network: Network,
    networkCapabilities: NetworkCapabilities
) {
    super.onCapabilitiesChanged(network, networkCapabilities)
    logger.warn("NETWORK CAPABILITIES CHANGED: $network")
    _connectivityState.update {
        getConnectivityState(network)
    }
}

// Modify getConnectivityState to take a network parameter
fun getConnectivityState(network: Network?): ConnectivityState {
    val capabilities = connectivityManager.getNetworkCapabilities(network)
    val linkProperties = connectivityManager.getLinkProperties(network)
    val connectivityState = ConnectivityState()

    if (wifiManager.isWifiEnabled) {
        val isWifi = capabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)
        if (isWifi != null && isWifi) {
            val networkInfo = connectivityManager.getNetworkInfo(network)
            if (networkInfo != null && networkInfo.type == ConnectivityManager.TYPE_WIFI) {
                // Cast to WifiInfo and get the SSID
                val wifiInfo = networkInfo.extraInfo as? WifiInfo
                if (wifiInfo != null) {
                    connectivityState.ssid = wifiInfo.ssid
                    logger.info("WifiInfo SSID: ${connectivityState.ssid}")
                } else {
                    logger.error("Failed to cast extraInfo to WifiInfo")
                }
            }
        }
    }

    // ... (remaining code)

    return connectivityState
}

In this modified code, getNetworkInfo(network) is used to obtain a NetworkInfo object, and extraInfo is then cast to WifiInfo to get the SSID. This approach should be more reliable and should work whether the device is connected through a VPN or not.

请注意,此方法假定 Wi-Fi 网络的网络类型ConnectivityManager.TYPE_WIFI。

评论

0赞 moonlightcheese 11/27/2023
你提到的方法在 API 29 中已弃用。我正在使用 minSDK = 28 进行编译,但想使用未弃用的方法来获取 SSID 信息。似乎 Android 团队忽略了在 VPN 后面获取 SSID 的这种范式,因为我没有找到有关如何使用新类来实现此效果的信息。Network