0

I want to change the DNS server on my device. Ideally I would like to do it both with Wifi and 3G/4G but I see that mobile network brings another complexity so I started with WifiManager by setting a static IP.

The code seems to work fine but the phone doesn't change IP (looking at settings->Wifi->ssid->ip address), doesn't respond to the ping from my linux pc (arp is incomplete) and is not showing up in the wifi router (dhcp configured till 192.168.1.230).

What do you think I do wrong?

Are they other ways of changing the DNS server (besides vpnservices;))?

I am using the code below that comes mainly from Yeung in How to configue a static IP address, netmask, gateway programmatically on Android 3.x or 4.x

I am using Android 6.0

logcat output:

11-01 09:45:17.167 12890-12890/com.narb.dns I/DNS: config:
                                                   * ID: 17 SSID: "Livebox-FB64" PROVIDER-NAME: null BSSID: any FQDN: null PRIO: 4777
                                                    numAssociation 72
                                                    validatedInternetAccess
                                                    KeyMgmt: WPA_PSK Protocols: WPA RSN WAPI
                                                    AuthAlgorithms:
                                                    PairwiseCiphers: TKIP CCMP SMS4
                                                    GroupCiphers: TKIP CCMP SMS4
                                                    PSK: *
                                                    Limited: 0
                                                   IP config:
                                                   IP assignment: STATIC
                                                   Static configuration: IP address 192.168.1.236/24 Gateway 192.168.1.1  DNS servers: [ 8.8.8.8 192.168.1.1 ] Domains
                                                   Proxy settings: NONE
                                                    autoJoinBSSID=any cuid=1000 cname=android.uid.system:1000 luid=1000 lname=android.uid.system:1000 lcuid=-1 userApproved=USER_APPROVED noInternetAccessExpected=false roamingFailureBlackListTimeMilli: 1000
                                                   triggeredLow: 0 triggeredBad: 0 triggeredNotHigh: 0
                                                   ticksLow: 0 ticksBad: 0 ticksNotHigh: 7
                                                   triggeredJoin: 0
                                                   autoJoinBailedDueToLowRssi: false
                                                   autoJoinUseAggressiveJoinAttemptThreshold: 0
11-01 09:45:17.237 12890-12965/com.narb.dns I/Adreno: QUALCOMM build                   : 8249e7b, Iacb76f3f7d
                                                      Build Date                       : 03/22/16
                                                      OpenGL ES Shader Compiler Version: XE031.06.00.05
                                                      Local Branch                     : 
                                                      Remote Branch                    : refs/tags/AU_LINUX_ANDROID_LA.BR.1.2.6_RB1.06.00.01.179.016
                                                      Remote Branch                    : NONE
                                                      Reconstruct Branch               : NOTHING
11-01 09:45:17.307 12890-12890/com.narb.dns W/art: Before Android 4.1, method int android.support.v7.widget.ListViewCompat.lookForSelectablePosition(int, boolean) would have incorrectly overridden the package-private method in android.widget.ListView

Code: I commented manager.saveconfiguration because it's deprecated and the doc is saying that updatenetwork is sufficient (anyway I tried with saveconfig, reconnect, associate - same result)

package com.narb.dns;

import android.content.Context;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Context context = getApplicationContext();


        WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);

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

        if (wifiConf != null)
        {
            try
            {
                setStaticIpConfiguration(manager, wifiConf,
                        InetAddress.getByName("192.168.1.236"), 24,
                        InetAddress.getByName("192.168.1.1"),
                        new InetAddress[] { InetAddress.getByName("8.8.8.8"),
                                InetAddress.getByName("192.168.1.1") });

                Log.i("DNS","config:\n"+wifiConf);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }

    private static void setStaticIpConfiguration(WifiManager manager, WifiConfiguration config,
                InetAddress ipAddress, int prefixLength, InetAddress gateway,
                InetAddress[] dns) throws ClassNotFoundException,
                IllegalAccessException, IllegalArgumentException, InvocationTargetException,
                NoSuchMethodException, NoSuchFieldException, InstantiationException    {

        // First set up IpAssignment to STATIC.
        Object ipAssignment = getEnumValue("android.net.IpConfiguration$IpAssignment", "STATIC");
        callMethod(config, "setIpAssignment", new String[] { "android.net.IpConfiguration$IpAssignment" },
                new Object[] { ipAssignment });

        // Then set properties in StaticIpConfiguration.
        Object staticIpConfig = newInstance("android.net.StaticIpConfiguration");
        Object linkAddress = newInstance("android.net.LinkAddress", new Class<?>[] {
                InetAddress.class, int.class }, new Object[] { ipAddress, prefixLength });

        setField(staticIpConfig, "ipAddress", linkAddress);
        setField(staticIpConfig, "gateway", gateway);
        getField(staticIpConfig, "dnsServers", ArrayList.class).clear();
        for (int i = 0; i < dns.length; i++)
            getField(staticIpConfig, "dnsServers", ArrayList.class).add(dns[i]);

        callMethod(config, "setStaticIpConfiguration", new String[] {
                "android.net.StaticIpConfiguration" }, new Object[] { staticIpConfig });
        manager.updateNetwork(config);
     //   manager.reassociate();
     //   manager.saveConfiguration();
    }

    private static Object newInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException
    {
        return newInstance(className, new Class<?>[0], new Object[0]);
    }

    private static Object newInstance(String className, Class<?>[] parameterClasses, Object[] parameterValues) throws NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException
    {
        Class<?> clz = Class.forName(className);
        Constructor<?> constructor = clz.getConstructor(parameterClasses);
        return constructor.newInstance(parameterValues);
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private static Object getEnumValue(String enumClassName, String enumValue) throws ClassNotFoundException
    {
        Class<Enum> enumClz = (Class<Enum>)Class.forName(enumClassName);
        return Enum.valueOf(enumClz, enumValue);
    }

    private static void setField(Object object, String fieldName, Object value) throws IllegalAccessException, IllegalArgumentException, NoSuchFieldException
    {
        Field field = object.getClass().getDeclaredField(fieldName);
        field.set(object, value);
    }

    private static <T> T getField(Object object, String fieldName, Class<T> type) throws IllegalAccessException, IllegalArgumentException, NoSuchFieldException
    {
        Field field = object.getClass().getDeclaredField(fieldName);
        return type.cast(field.get(object));
    }

    private static void callMethod(Object object, String methodName, String[] parameterTypes, Object[] parameterValues) throws ClassNotFoundException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException
    {
        Class<?>[] parameterClasses = new Class<?>[parameterTypes.length];
        for (int i = 0; i < parameterTypes.length; i++)
            parameterClasses[i] = Class.forName(parameterTypes[i]);

        Method method = object.getClass().getDeclaredMethod(methodName, parameterClasses);
        method.invoke(object, parameterValues);
    }
}

Manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.narb.dns">

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
narb
  • 958
  • 1
  • 13
  • 39
  • you cannot -repeat you cannot change it unless phone is rooted. and it is an older API version – Niraj Sanghani Nov 01 '17 at 09:09
  • Really? I'm surprised because 1. there is an api - wifimanager that doesn't mention the need to be rooted, 2. despite a deprecation it seems to still be valid. How would you do it? Rooting the phone is not an option. There are several DNS changer app in the store but they seem to mainly use vpnservices. – narb Nov 01 '17 at 09:23
  • it is not possible X end of story X – Niraj Sanghani Nov 01 '17 at 09:24

1 Answers1

0

I figured out that you must separate the method calls and wait for a successful response before calling the next one. This is with Nougat.

callMethod(config, "setIpAssignment", new String[] { "android.net.IpConfiguration$IpAssignment" },
                new Object[] { ipAssignment });
int updateNetworkResult = manager.updateNetwork(config);

if (updateNetworkResult == -1) return;

callMethod(config, "setStaticIpConfiguration", new String[] {
                "android.net.StaticIpConfiguration" }, new Object[] { staticIpConfig });
manager.updateNetwork(config);
Ace
  • 1