31

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

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

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 Settings field is still stated DHCP but not 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.

The system uses the setting other than android.provider.Settings.System on 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?

Nick Cardoso
  • 20,807
  • 14
  • 73
  • 124
Yeung
  • 2,202
  • 2
  • 27
  • 50

8 Answers8

61

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.WifiConfiguration which is gotten from android.net.wifi.WifiManager.

In the below code, IpAssignment is an Enum, either STAIC, DHCP or NONE. And linkProperties is the object store IP address, gateway, DNS, etc...

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

mRoutes is ArrayList of RouteInfo that can indicate gateway.

mDnses is ArrayList of InetAddress for DNS.

Firstly, get the current configuration using WifiConfiguration SSID

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 IpAssignment and linkProperties are hidden, the object can be gotten from reflection.

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

    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 WifiConfiguration for this 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 mGateways of linkProperties. mGateways is Arraylist of type InetAddress. Therefore, following should work in 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, setDNS should be inputted as InetAddress type.

YoussefDir
  • 287
  • 1
  • 3
  • 16
Yeung
  • 2,202
  • 2
  • 27
  • 50
  • Hi, I am using this code and I am getting error in the ` Class routeInfoClass = Class.forName("android.net.RouteInfo");` statement. How can I resolve it. It says `java.lang.ClassNotFoundException: android.net.RouteInfo`. Thanks in advance. – Sandeep Kumar P K May 11 '12 at 06:44
  • I am getting error in 3.x, when i changed project to 4.x and tested in a ICS device, it works. How can I solve this issue in 3.x? – Sandeep Kumar P K May 11 '12 at 06:56
  • Thanks for notify this. I nearly make mistake on this on Android 3.x. – Yeung May 14 '12 at 05:33
  • I have tested on an Android 3.x device and it work. I can think of one mistake maybe the methods input parameter should be InetAddress type. But I think you have noticed that and otherwise it won't work on ICS. What exception do you get? Or, is it just take no effect? – Yeung May 17 '12 at 04:21
  • 2
    Is it really working? I have tried on 3.x and even on 4.x version, No effects shown. It always DHCP and it doesn't changed to Static. Therefore, no changes happens to address. – Harpreet Mar 01 '13 at 05:16
  • It didnt worked for me? any idea why? tested on android 4.2.2 cyanomode. – Simon Dorociak Aug 08 '13 at 09:37
  • I'm getting a nullpointer exception when calling wifiManager.updateNetowrk(wifiConf). ANy idea about this? – Mr.Noob Sep 02 '13 at 11:51
  • 5
    Small typo in the first code block. if (WifiConf.networkId == connectionInfo.getNetworkId()){ Should be: if (conf.networkId == connectionInfo.getNetworkId()){ And: WifiConf = conf; Should be: wifiConf = conf; – dubmojo Nov 13 '13 at 20:48
  • 2
    Make sure u call wifiManager.saveConfiguration(); after wifiManager.updateNetwork(wifiConf); to correctly apply the settings. Ref: http://stackoverflow.com/questions/18136000/assign-static-ip-address-for-wifi-network-on-android-3-x-and-4-x – M. Usman Khan Feb 27 '14 at 06:44
  • "mLinkAddresses" field not found. I am running on 4.1.2. help? – M. Usman Khan Feb 27 '14 at 11:07
  • How can i change dns when phone is using 3G connection ? – Emre Koç Mar 29 '14 at 16:31
  • Mr Noob check that you have the correct permissions. I was missing: – Shadoath May 29 '14 at 16:47
  • Does anybody know why it doesn't work on Android L? Got `Java.lang.NoSuchFieldException: ipAssignment at java.lang.Class.getField(Class.java:1048)` – KennyPowers Nov 30 '14 at 17:36
  • @Yeung please come with the update for Android 5.0 :) – KennyPowers Dec 23 '14 at 07:43
  • @KennyPowers Give me a break. It is saying **on Android 3.x or 4.x** :) – Yeung Dec 24 '14 at 03:50
  • do you know if programmatically I can know if a wifi has services blockeds like whatsapp instagram, etc..? – Skizo-ozᴉʞS ツ Jul 22 '15 at 19:19
  • @Yeung Does this solution requires root access? – noti Aug 28 '15 at 10:55
  • For Android 3.1, I am getting ClassnotFoundException when I try to get RouteInfo class through reflection. Can anyone please share how to fix that ? – Abdul Rehman Nov 23 '15 at 06:28
  • code is worked for me.I can see config i set through the above code in wifi settings.but how to clear dns cache programatically ? i have to reboot device or emmulator to get affected. And how can i restore previous setting on my app uninstallation? – Harshal Bhatt Nov 28 '15 at 05:50
  • Will this code work with IPV6 also? if yes than what will be length of prefix value accepted by user? – Abhinav Singh Maurya Dec 11 '15 at 08:30
  • @AlexMartin . Hi Alex. could you make it to work on non rooted phone? I am trying on non rooted 5.0 release and it is not working for me. Thanks for any help – Sushil Jan 13 '16 at 16:33
  • from my experience it is not only required to use wifiManager.saveConfiguration() but also call wifiManager.reassociate() so that the new static configuration is not only set, but actively used with the current Access Point – Robin Gawenda Jan 29 '16 at 10:46
  • since Android 5.0(SDK 21) you should take a look at the new methods in WifiConfiguration class based on the new IpConfiguration and StaticIpConfiguration classes – Robin Gawenda Jan 29 '16 at 11:47
  • @Yeung has elaborated well on how to configure Wi-Fi for different Android OS versions. In my application, I have used the same to configure Static & DHCP connection for android devices 2.x and above. Now i want to retrieve IPAssignment, SubnetMask and IPAddress from configuration and sync it with server. Can anyone please help me getting there ? – Abdul Rehman Mar 08 '16 at 07:49
  • Thanks for the code! I tested in on my samsung note 3 running 4.4.2. It worked only when a) STATIC is used b) Turn off, then turn on wifi programmatically after DNS is changed. – ChinLoong Mar 20 '16 at 05:43
  • It worked for me, I have set IP address and gateway address with help of this answer. – Tushar Thakur Feb 20 '17 at 09:50
4

@Robin

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

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

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();                        
}
Som
  • 83
  • 2
  • 9
  • 1
    thanks. enableNetwork might be the missing bit. Hoewever is it really required to disable all other networks? Did you also try it with enableNetwork/wifiConf.networkId, false)? – Robin Gawenda Apr 12 '17 at 12:36
3

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

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();
    }
}
Robin Gawenda
  • 559
  • 5
  • 21
1

For Android 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());
    }
}
Jackson Chengalai
  • 3,907
  • 1
  • 24
  • 39
0

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.

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.

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

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

Using adb shell and running the command:

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

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

Knubo
  • 8,333
  • 4
  • 19
  • 25
0

As a kotlin extension of WifiConfiguration, working on 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")
    }
}
crgarridos
  • 8,758
  • 3
  • 49
  • 61
0

@Yeung, everyone

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

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

This is what is happening in my experiments on Pie. I try to apply static configuration by listening to 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
    }
}
sam
  • 1
  • 2
0

@Robin Thank you so much, your solution works for me exactly on Android 7 (required system app privilege).

  • 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
    }
  }
}
phostann
  • 141
  • 1
  • 2