Android,在网络共享和连接到接入点时检测 WiFi 的本地 IP 和子网掩码

Android, detect local IP and subnet mask for WiFi, both while tethering and connected to access point

我需要在 Android 设备上检测 WiFi 网络上的本地 IP 地址 和子网掩码 (以便严格正确计算 UDP 广播地址对于本地子网)。

当设备连接到接入点时,以下内容正常工作:

// Only works when NOT tethering
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

DhcpInfo dhcp = wifi.getDhcpInfo();
if (dhcp == null)
    throw new IOException("No DHCPInfo on WiFi side.");

foo(dhcp.ipAddress, dhcp.netmask);

但是当 android 设备通过网络共享提供接入点时它不起作用:当 Android 设备是它的客户端时,DhcpInfo 似乎包含 DCHP 服务器设置的信息,而不是 Android 设备本身提供 DHCP 服务。在网络共享时,我能找到的最有希望的解决方案是:

// No way to get subnet mask
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);

WifiInfo info = wifi.getConnectionInfo();
if (info == null)
   throw new IOException("No connection info on WiFi side.");

foo(info.getIpAddress(), info.??? /* netmask*/ );

编辑:错误,在我的测试中,即使这仅在未绑定时才有效。网络共享时 IP 始终为 0。

但没有什么比 WifiInfo.getNetMask() 更重要的是,在那种情况下我如何获得子网掩码? (这种缺席让我觉得很奇怪,因为那里还有很多其他信息。我是否遗漏了一些明显的东西?)

此外,理想情况下,我想要一个不需要区分 Android 设备是否提供网络共享的解决方案,只需在 WiFi 网络上获取本地 IP 地址和子网掩码,在任何情况下,无论是 Android 设备正在提供还是接入点的客户端。

即使是标准 Java(即不是 Android 特定的)NetworkInterface.getNetworkInterfaces(),似乎也没有办法获得子网掩码(除了不允许区分哪个对应WiFi)。我错过了什么?

我目前找到的最佳解决方案:

让我感到困惑的是 info/interface 关于网络共享是如此 cumbersome/hidden 得到,但当你从 WifiManagerConnectivityManager 获取信息时却没有考虑到Wifi 类型:它仅在不处于网络共享状态时才有效。我实际上迷失了调查的那个分支。

我目前找到的最佳解决方案是使用标准 Java NetworkInterface.getNetworkInterfaces(),而不是任何 Android API.

根据实验,Android 似乎足够聪明,可以将网络接口设置为空广播到外部移动网络。这实际上很有意义,因为 Android 默默地丢弃涉及外部移动网络的 UDP 广播。

    // This works both in tethering and when connected to an Access Point

    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();

    while (interfaces.hasMoreElements()) 
    {
        NetworkInterface networkInterface = interfaces.nextElement();

        if (networkInterface.isLoopback())
            continue; // Don't want to broadcast to the loopback interface

        for (InterfaceAddress interfaceAddress : networkInterface.getInterfaceAddresses()) 
        {
                InetAddress broadcast = interfaceAddress.getBroadcast();

                // InetAddress ip = interfaceAddress.getAddress();                  
                // interfaceAddress.getNetworkPrefixLength() is another way to express subnet mask

                // Android seems smart enough to set to null broadcast to
                //  the external mobile network. It makes sense since Android
                //  silently drop UDP broadcasts involving external mobile network.
                if (broadcast == null)
                    continue;

                ... // Use the broadcast                
        }
    }

至于子网掩码,getNetworkPrefixLength()的结果可以强制转换为子网掩码。我直接使用 getBroadcast() 因为那是我的最终目标。

此代码似乎不需要特殊权限(没有 ACCESS_WIFI_STATE 也没有 NETWORK,只是 INTERNET)。

代码段的主要参考:http://enigma2eureka.blogspot.it/2009/08/finding-your-ip-v4-broadcast-address.html