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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10278461/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 03:07:10  来源:igfitidea点击:

How to configure a static IP address, netmask, gateway programmatically on Android 3.x or 4.x

androidnetworkingsettingsipv4

提问by Yeung

I have checked in Stack Overflow question API for configuring static IP addresses in an Android application.

我已检查 Stack Overflow 问题API 以在 Android 应用程序中配置静态 IP 地址

It works until Android 2.3. However, there is no luck on a higher API level. For example, I put the setting

它适用于 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");

But I go back to check by:

但我回去检查:

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

The IP Settingsfield is still stated DHCPbut not Static.

IP Settings字段仍被声明,DHCP但不是Static

It is true that I can use android.provider.Settings.System.getString()to get back what I set. It prove that the setting is saved somewhere but the system just ignore it.

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

The system uses the setting other than android.provider.Settings.Systemon Android 3.x and 4.x as the setting is set per Access Point SSID. Can I modify the setting on one SSID just like how it works on Android 2.3?

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

回答by Yeung

I realise that there is no API on 3.x or 4.x for those setting per SSID. Therefore, I checked out the source code and found out that the configuration of each SSID is stored in android.net.wifi.WifiConfigurationwhich is gotten from android.net.wifi.WifiManager.

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

In the below code, IpAssignmentis an Enum, either STAIC, DHCPor NONE. And linkPropertiesis the object store IP address, gateway, DNS, etc...

在下面的代码中,IpAssignment是一个枚举,要么STAICDHCP要么NONE。并且linkProperties是对象存储IP地址、网关、DNS等...

linkAddressis IP address and its netmask as prefixLength (how many bit 1 in netmask).

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

mRoutesis ArrayListof RouteInfothat can indicate gateway.

mRoutesArrayListRouteInfo,可以指示网关。

mDnsesis ArrayListof InetAddressfor DNS.

mDnsesArrayListInetAddress对DNS。

Firstly, get the current configuration using WifiConfigurationSSID

首先,使用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;              
    }
}

As the IpAssignmentand linkPropertiesare hidden, the object can be gotten from reflection.

由于IpAssignmentlinkProperties是隐藏的,因此可以从反射中获取对象。

The following method can set the declared IP address setting on SSID WifiConfiguration:

以下方法可以在 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));
    }

After that, I can set setting and update WifiConfigurationfor this SSID.

之后,我可以WifiConfiguration为此 SSID设置设置和更新。

    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();
    }

Edit: Sorry for I don't check for Android 3.x device that have silmilar UI with Android 4.x. In Android 3.x, the gateway is storted in mGatewaysof linkProperties. mGatewaysis Arraylistof type InetAddress. Therefore, following should work in Android 3.x.

编辑:抱歉,我不检查与 Android 4.x 具有相似 UI 的 Android 3.x 设备。在安卓3.x中,网关在stortedmGatewayslinkPropertiesmGatewaysArraylist类型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: The methods setIpAddress, setGateway, setDNSshould be inputted as InetAddresstype.

EDIT2:所述方法setIpAddresssetGatewaysetDNS应当被输入作为InetAddress类型。

回答by Som

@Robin

@罗宾

Thanks your solution works fine for me on My Nexus device running on Android M 6.0.1.

感谢您的解决方案在运行 Android M 6.0.1 的我的 Nexus 设备上对我来说很好。

I have replaced the // 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

我已经更换了 // 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

with the following

与以下

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();                        
}

回答by Robin Gawenda

For Android 5.0+ a WIP solution. It does not yet work for some reason. Comments welcome.

对于 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();
    }
}

回答by Hymanson Chengalai

For Android 5.1.0

对于安卓 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());
    }
}

回答by Knubo

If you try to use the solution for Android 5.x on 6.x your application will be denied doing this. To do this you proabably need to root your device and make the application the device owner.

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

I've dug some into the problem and my findings is that code that used to work for Andrdoi 5.x might work if the application is set to be the device owner.

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

A good example of how this is done is using the example found here:

一个很好的例子是使用这里找到的例子:

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

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

Using adb shell and running the command:

使用 adb shell 并运行命令:

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

dpm 设置设备所有者 com.example.android.deviceowner/.DeviceOwnerReceiver

will make the application device owner and it is possible to set static IP.

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

回答by crgarridos

As a kotlin extension of WifiConfiguration, working on Android 5+

作为 kotlin 的扩展WifiConfiguration,适用于 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")
    }
}

回答by sam

@Yeung, everyone

@杨,大家

As far as I understand, Android would start dhclient immediately after connecting to an SSID.

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

So the code suggested, would apply static configuration only after Android had already obtained an IP address (dhcp success), right?

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

This is what is happening in my experiments on Pie. I try to apply static configuration by listening to WifiManager.NETWORK_STATE_CHANGED_ACTION

这就是我在 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
    }
}