如何在 Android 3.x 或 4.x 上以编程方式配置静态 IP 地址、网络掩码、网关

Posted

技术标签:

【中文标题】如何在 Android 3.x 或 4.x 上以编程方式配置静态 IP 地址、网络掩码、网关【英文标题】:How to configure a static IP address, netmask, gateway programmatically on Android 3.x or 4.x 【发布时间】:2012-05-03 22:35:59 【问题描述】:

我已签入 Stack Overflow 问题API for configuring static IP addresses in an android application

它在 Android 2.3 之前有效。但是,在更高的 API 级别上没有运气。例如, 我把设置

android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_USE_STATIC_IP, "1");        
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_IP, "192.168.0.100");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_NETMASK, "255.255.255.0");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_DNS1, "192.168.0.254");
android.provider.Settings.System.putString(getContentResolver(), android.provider.Settings.System.WIFI_STATIC_GATEWAY, "192.168.0.254");

但我回去检查:

Setting --> Wi-Fi --> Long Press Access Point SSID --> Modify Network --> check Show advanced options

IP Settings 字段仍为DHCP,但不是Static

确实,我可以使用android.provider.Settings.System.getString() 来取回我设置的内容。它证明设置保存在某处,但系统只是忽略它。

系统在 Android 3.x 和 4.x 上使用除 android.provider.Settings.System 之外的设置,因为该设置是按接入点 SSID 设置的。我可以像在 Android 2.3 上一样修改一个 SSID 的设置吗?

【问题讨论】:

【参考方案1】:

我意识到 3.x 或 4.x 上没有针对每个 SSID 设置的 API。于是,我查看了源码,发现每个SSID的配置都存放在android.net.wifi.WifiConfiguration中,android.net.wifi.WifiConfiguration是从android.net.wifi.WifiManager获取的。

在下面的代码中,IpAssignment 是一个枚举,STAICDHCPNONE。 而linkProperties是对象存储IP地址、网关、DNS等...

linkAddress 是 IP 地址,其网络掩码为 prefixLength(网络掩码中有多少位 1)。

mRoutesArrayListRouteInfo 可以表示网关。

mDnses 是 DNS 的 ArrayListInetAddress

首先,使用WifiConfigurationSSID获取当前配置

WifiConfiguration wifiConf = null;
WifiManager wifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiInfo connectionInfo = wifiManager.getConnectionInfo();
List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();        
for (WifiConfiguration conf : configuredNetworks)
    if (conf.networkId == connectionInfo.getNetworkId())
        wifiConf = conf;
        break;              
    

由于IpAssignmentlinkProperties是隐藏的,所以可以通过反射获取对象。

以下方法可以设置SSID WifiConfiguration上声明的IP地址设置:

    public static void setIpAssignment(String assign , WifiConfiguration wifiConf)
    throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException
        setEnumField(wifiConf, assign, "ipAssignment");     
    

    public static void setIpAddress(InetAddress addr, int prefixLength, WifiConfiguration wifiConf)
    throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException,
    NoSuchMethodException, ClassNotFoundException, InstantiationException, InvocationTargetException
        Object linkProperties = getField(wifiConf, "linkProperties");
        if(linkProperties == null)return;
        Class laClass = Class.forName("android.net.LinkAddress");
        Constructor laConstructor = laClass.getConstructor(new Class[]InetAddress.class, int.class);
        Object linkAddress = laConstructor.newInstance(addr, prefixLength);

        ArrayList mLinkAddresses = (ArrayList)getDeclaredField(linkProperties, "mLinkAddresses");
        mLinkAddresses.clear();
        mLinkAddresses.add(linkAddress);        
    

    public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
    throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
    ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException
        Object linkProperties = getField(wifiConf, "linkProperties");
        if(linkProperties == null)return;
        Class routeInfoClass = Class.forName("android.net.RouteInfo");
        Constructor routeInfoConstructor = routeInfoClass.getConstructor(new Class[]InetAddress.class);
        Object routeInfo = routeInfoConstructor.newInstance(gateway);

        ArrayList mRoutes = (ArrayList)getDeclaredField(linkProperties, "mRoutes");
        mRoutes.clear();
        mRoutes.add(routeInfo);
    

    public static void setDNS(InetAddress dns, WifiConfiguration wifiConf)
    throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException
        Object linkProperties = getField(wifiConf, "linkProperties");
        if(linkProperties == null)return;

        ArrayList<InetAddress> mDnses = (ArrayList<InetAddress>)getDeclaredField(linkProperties, "mDnses");
        mDnses.clear(); //or add a new dns address , here I just want to replace DNS1
        mDnses.add(dns); 
    

    public static Object getField(Object obj, String name)
    throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
        Field f = obj.getClass().getField(name);
        Object out = f.get(obj);
        return out;
    

    public static Object getDeclaredField(Object obj, String name)
    throws SecurityException, NoSuchFieldException,
    IllegalArgumentException, IllegalAccessException 
        Field f = obj.getClass().getDeclaredField(name);
        f.setAccessible(true);
        Object out = f.get(obj);
        return out;
      

    private static void setEnumField(Object obj, String value, String name)
    throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException
        Field f = obj.getClass().getField(name);
        f.set(obj, Enum.valueOf((Class<Enum>) f.getType(), value));
    

之后,我可以为这个 SSID 设置和更新WifiConfiguration

    try
        setIpAssignment("STATIC", wifiConf); //or "DHCP" for dynamic setting
        setIpAddress(InetAddress.getByName("192.168.0.100"), 24, wifiConf);
        setGateway(InetAddress.getByName("4.4.4.4"), wifiConf);
        setDNS(InetAddress.getByName("4.4.4.4"), wifiConf);
        wifiManager.updateNetwork(wifiConf); //apply the setting
            wifiManager.saveConfiguration(); //Save it
    catch(Exception e)
        e.printStackTrace();
    

编辑: 抱歉,我不检查具有与 Android 4.x 类似 UI 的 Android 3.x 设备。 在 Android 3.x 中,网关存储在 mGatewayslinkProperties 中。 mGatewaysArraylist 类型的 InetAddress。因此,以下应该适用于 Android 3.x。

public static void setGateway(InetAddress gateway, WifiConfiguration wifiConf)
        throws SecurityException, IllegalArgumentException, NoSuchFieldException, IllegalAccessException, 
        ClassNotFoundException, NoSuchMethodException, InstantiationException, InvocationTargetException
            Object linkProperties = getField(wifiConf, "linkProperties");
            if(linkProperties == null)return;
            ArrayList mGateways = (ArrayList)getDeclaredField(linkProperties, "mGateways");
            mGateways.clear();
            mGateways.add(gateway);
        

Edit2:方法setIpAddresssetGatewaysetDNS应输入为InetAddress类型。

【讨论】:

嗨,我正在使用此代码,但在 `Class routeInfoClass = Class.forName("android.net.RouteInfo");` 语句中出现错误。我该如何解决。它说java.lang.ClassNotFoundException: android.net.RouteInfo。提前致谢。 我在 3.x 中遇到错误,当我将项目更改为 4.x 并在 ICS 设备中进行测试时,它可以工作。如何在 3.x 中解决此问题? 真的有效吗?我试过 3.x 甚至 4.x 版本,没有显示效果。它始终是 DHCP,并且不会更改为静态。因此,地址不会发生任何变化。 第一个代码块中的小错字。 if (WifiConf.networkId == connectionInfo.getNetworkId()) 应该是: if (conf.networkId == connectionInfo.getNetworkId()) 和: WifiConf = conf;应该是:wifiConf = conf; 确保你调用了 wifiManager.saveConfiguration();在 wifiManager.updateNetwork(wifiConf) 之后;正确应用设置。参考:***.com/questions/18136000/…【参考方案2】:

适用于安卓 5.1.0

      WifiConfiguration GetCurrentWifiConfiguration(WifiManager manager)
    
    if (!manager.isWifiEnabled())
        return null;

    List<WifiConfiguration> configurationList = manager.getConfiguredNetworks();
    WifiConfiguration configuration = null;
    int cur = manager.getConnectionInfo().getNetworkId();
    for (int i = 0; i < configurationList.size(); ++i)
    
        WifiConfiguration wifiConfiguration = configurationList.get(i);
        if (wifiConfiguration.networkId == cur)
            configuration = wifiConfiguration;
    

    return configuration;




@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public void setWifiProxySettings5()

    //get the current wifi configuration
    WifiManager manager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
    WifiConfiguration config = GetCurrentWifiConfiguration(manager);
    if(null == config)
        return;

    try
    
        //linkProperties is no longer in WifiConfiguration
        Class proxyInfoClass = Class.forName("android.net.ProxyInfo");
        Class[] setHttpProxyParams = new Class[1];
        setHttpProxyParams[0] = proxyInfoClass;
        Class wifiConfigClass = Class.forName("android.net.wifi.WifiConfiguration");
        Method setHttpProxy = wifiConfigClass.getDeclaredMethod("setHttpProxy", setHttpProxyParams);
        setHttpProxy.setAccessible(true);

        //Method 1 to get the ENUM ProxySettings in IpConfiguration
        Class ipConfigClass = Class.forName("android.net.IpConfiguration");
        Field f = ipConfigClass.getField("proxySettings");
        Class proxySettingsClass = f.getType();

        //Method 2 to get the ENUM ProxySettings in IpConfiguration
        //Note the $ between the class and ENUM
        //Class proxySettingsClass = Class.forName("android.net.IpConfiguration$ProxySettings");

        Class[] setProxySettingsParams = new Class[1];
        setProxySettingsParams[0] = proxySettingsClass;
        Method setProxySettings = wifiConfigClass.getDeclaredMethod("setProxySettings", setProxySettingsParams);
        setProxySettings.setAccessible(true);


        ProxyInfo pi = ProxyInfo.buildDirectProxy("127.0.0.1", 8118);
        //Android 5 supports a PAC file
        //ENUM value is "PAC"
        //ProxyInfo pacInfo = ProxyInfo.buildPacProxy(Uri.parse("http://localhost/pac"));

        //pass the new object to setHttpProxy
        Object[] params_SetHttpProxy = new Object[1];
        params_SetHttpProxy[0] = pi;
        setHttpProxy.invoke(config, params_SetHttpProxy);

        //pass the enum to setProxySettings
        Object[] params_setProxySettings = new Object[1];
        params_setProxySettings[0] = Enum.valueOf((Class<Enum>) proxySettingsClass, "STATIC");
        setProxySettings.invoke(config, params_setProxySettings);

        //save the settings
        manager.updateNetwork(config);
        manager.disconnect();
        manager.reconnect();
    
    catch(Exception e)
    
        Log.v("wifiProxy", e.toString());
    

【讨论】:

它可以在 api 23 上运行吗?我在 Android M 上尝试过,但它不工作。 不知道 api 23。我在棒棒糖设备中使用此代码进行测试【参考方案3】:

对于 Android 5.0+,一个 WIP 解决方案。由于某种原因,它还不能工作。欢迎评论。

void changeWifiConfiguration(boolean dhcp, String ip, int prefix, String dns1, String gateway) 
    WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    if(!wm.isWifiEnabled()) 
        // wifi is disabled
        return;
    
    // get the current wifi configuration
    WifiConfiguration wifiConf = null;
    WifiInfo connectionInfo = wm.getConnectionInfo();
    List<WifiConfiguration> configuredNetworks = wm.getConfiguredNetworks();   
    if(configuredNetworks != null) 
        for (WifiConfiguration conf : configuredNetworks)
            if (conf.networkId == connectionInfo.getNetworkId())
                wifiConf = conf;
                break;              
            
        
    
    if(wifiConf == null) 
        // wifi is not connected
        return;
    
    try 
        Class<?> ipAssignment = wifiConf.getClass().getMethod("getIpAssignment").invoke(wifiConf).getClass();
        Object staticConf = wifiConf.getClass().getMethod("getStaticIpConfiguration").invoke(wifiConf);
        if(dhcp) 
            wifiConf.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConf, Enum.valueOf((Class<Enum>) ipAssignment, "DHCP"));
            if(staticConf != null) 
                staticConf.getClass().getMethod("clear").invoke(staticConf);
            
         else 
            wifiConf.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConf, Enum.valueOf((Class<Enum>) ipAssignment, "STATIC"));
            if(staticConf == null) 
                Class<?> staticConfigClass = Class.forName("android.net.StaticIpConfiguration");
                staticConf = staticConfigClass.newInstance();
            
            // STATIC IP AND MASK PREFIX
            Constructor<?> laConstructor = LinkAddress.class.getConstructor(InetAddress.class, int.class);
            LinkAddress linkAddress = (LinkAddress) laConstructor.newInstance(
                    InetAddress.getByName(ip), 
                    prefix);
            staticConf.getClass().getField("ipAddress").set(staticConf, linkAddress);
            // GATEWAY
            staticConf.getClass().getField("gateway").set(staticConf, InetAddress.getByName(gateway));
            // DNS
            List<InetAddress> dnsServers = (List<InetAddress>) staticConf.getClass().getField("dnsServers").get(staticConf);
            dnsServers.clear();
            dnsServers.add(InetAddress.getByName(dns1)); 
            dnsServers.add(InetAddress.getByName("8.8.8.8")); // Google DNS as DNS2 for safety
            // apply the new static configuration
            wifiConf.getClass().getMethod("setStaticIpConfiguration", staticConf.getClass()).invoke(wifiConf, staticConf);
        
        // apply the configuration change
        boolean result = wm.updateNetwork(wifiConf) != -1; //apply the setting
        if(result) result = wm.saveConfiguration(); //Save it
        if(result) wm.reassociate(); // reconnect with the new static IP
     catch(Exception e) 
        e.printStackTrace();
    

【讨论】:

这个拯救了我的一天【参考方案4】:

@罗宾

感谢您的解决方案在运行 Android M 6.0.1 的 My Nexus 设备上运行良好。

我已经替换了 // apply the configuration change boolean result = wm.updateNetwork(wifiConf) != -1; //apply the setting if(result) result = wm.saveConfiguration(); //Save it if(result) wm.reassociate(); // reconnect with the new static IP

以下

int netId = manager.updateNetwork(wifiConf);
boolean result =  netId!= -1; //apply the setting
if(result)
    boolean isDisconnected =  manager.disconnect();
    boolean configSaved = manager.saveConfiguration(); //Save it
    boolean isEnabled = manager.enableNetwork(wifiConf.networkId, true);
    // reconnect with the new static IP
    boolean isReconnected = manager.reconnect();                        

【讨论】:

谢谢。 enableNetwork 可能是缺少的位。然而,真的需要禁用所有其他网络吗?您是否也尝试过 enableNetwork/wifiConf.networkId, false)?【参考方案5】:

如果您尝试在 6.x 上使用适用于 Android 5.x 的解决方案,您的应用程序将被拒绝这样做。为此,您可能需要 root 设备并将应用程序设为设备所有者。

我对这个问题进行了一些研究,我的发现是,如果将应用程序设置为设备所有者,则过去适用于 Andrdoi 5.x 的代码可能会起作用。

使用此处找到的示例是一个很好的示例:

https://github.com/googlesamples/android-DeviceOwner/

使用 adb shell 并运行命令:

dpm set-device-owner com.example.android.deviceowner/.DeviceOwnerReceiver

将使应用程序设备所有者并且可以设置静态IP。

【讨论】:

【参考方案6】:

作为WifiConfiguration 的 kotlin 扩展,适用于 Android 5+

fun WifiConfiguration.setHttpProxyCompat(proxyInfo: ProxyInfo) 
    if (Build.VERSION.SDK_INT >= 26) 
        httpProxy = proxyInfo
        Timber.i("Setting proxy using 26+ method")
     else 
        val proxySettings = Class.forName("android.net.IpConfiguration\$ProxySettings")
        val valueOf = proxySettings.getMethod("valueOf", String::class.java)
        val static = valueOf.invoke(proxySettings, "STATIC")

        val setProxy = this::class.java.getDeclaredMethod("setProxy", proxySettings, ProxyInfo::class.java)
        setProxy.isAccessible = true

        setProxy.invoke(this, static, proxyInfo)
        Timber.i("Setting proxy using reflection")
    

【讨论】:

【参考方案7】:

@Yeung,大家

据我了解,Android 会在连接到 SSID 后立即启动 dhclient。

所以代码建议,只有在Android已经获得IP地址(dhcp成功)之后才会应用静态配置,对吧?

这就是我在 Pie 实验中发生的情况。我尝试通过收听 WifiManager.NETWORK_STATE_CHANGED_ACTION 来应用静态配置

intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
...........
...........
if ( action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION) )

    NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
    if (info.isConnectedOrConnecting())
    
        //apply static IP to current wifi connnections as per above code
    

【讨论】:

【参考方案8】:

@Robin 非常感谢,您的解决方案在 Android 7 上完全适用于我(必需 系统应用权限)。

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:sharedUserId="android.uid.system"></manifest>
app.build.gradle
android 
  signingConfigs 
        system_keystore 
            storeFile file('/Users/**/system.keystore')
            storePassword '****'
            keyAlias '****'
            keyPassword '****'
        
    

  buildTypes 
    debug 
      signingConfig signingConfigs.system_keystore
    
    release 
      signingConfig signingConfigs.system_keystore
    
  

【讨论】:

以上是关于如何在 Android 3.x 或 4.x 上以编程方式配置静态 IP 地址、网络掩码、网关的主要内容,如果未能解决你的问题,请参考以下文章

如何在Android上以编程方式启用或禁用GPS?

如何使用 fltk 1.3 创建 opengl 3.x 或 4.x 上下文?

如何在 Android 上以编程方式截取屏幕截图? [复制]

如何在 Android 上以编程方式从 Mac 地址获取 IP 地址?

如何在android studio上以编程方式检查自动启动权限是启用还是禁用

如何在不同的移动平台上以相同的方式显示内容?