android 如何更新Dns

android 如何更新Dns

android 更新DNS的方式有如下几种。

  1. 通过代码更新,这里又分为有线网络和无线网络。
  2. 通过命令行更新
  3. 通过hosts文件更新

通过代码更新

通过代码更新DNS,部分使用了android系统隐藏的函数和功能,因此读者可以使用如下两种方法来使用这些功能。
第一种,通过反射
第二种,自定义一个frameworkd的jar包。把隐藏的api显示出来,同时在编译时,指定这个framework.jar为仅参与编译。如下

//此处将framwork.jar放入了一个android的module中,然后使用下面的命令进行引用
dependencies {
    compileOnly project(':framework')
}

因为framework.jar里面的api和android sdk中的api几乎完全一样,因此,还需要调整编译器引用的jar包顺序
可参考如下文章 https://blog.csdn.net/hacker_crazy/article/details/78465959

有线网络

第一步获取EtherNetManager

EthernetManager manager = (EthernetManager) ApplicationUtil.getApplication().getSystemService("ethernet");

第二步设置一个IpConfiguration

//使用静态IP
StaticIpConfiguration staticIpConfiguration = new StaticIpConfiguration();
staticIpConfiguration.ipAddress = //设置ip地址
staticIpConfiguration.gateway = //设置网关
staticIpConfiguration.dnsServers.add(InetAddress.getByName(xx));//在ip配置中使用DNS配置
IpConfiguration ipConfiguration = manager.getConfiguration();
ipConfiguration.setStaticIpConfiguration(staticIpConfiguration);
ipConfiguration.setIpAssignment(IpConfiguration.IpAssignment.STATIC);
manager.setConfiguration(ipConfiguration);

第三部清除Dns缓存

//清理Dns Cache
Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
ApplicationUtil.getApplication().sendBroadcastAsUser(intent, UserHandle.ALL);

无线网络

第一步获取WifiManager

WifiManager wifiManager = (WifiManager) ApplicationUtil.getApplication().getSystemService(Context.WIFI_SERVICE);

第二步设置IpConfigure

private void setWifiConfig(IpConfig ipConfig) throws IpConfig.IpConfigParameterException {
    
    
        WifiManager wifiManager = (WifiManager) ApplicationUtil.getApplication().getSystemService(Context.WIFI_SERVICE);
        boolean dhcp = ipConfig.ipType == IpConfig.IpType.DHCP;
        String ip = ipConfig.ip;
        String netMask = ipConfig.netMask;
        String gateway = ipConfig.gateWay;
        String dns[] = ipConfig.dns;
        if (!dhcp) {
    
    
            if (TextUtils.isEmpty(ip)) {
    
    
                throw new IpConfig.IpConfigParameterException("Ip地址为空");
            }
            if (TextUtils.isEmpty(netMask)) {
    
    
                throw new IpConfig.IpConfigParameterException("子网掩码地址为空");
            }
            if (TextUtils.isEmpty(gateway)) {
    
    
                throw new IpConfig.IpConfigParameterException("网关地址为空");
            }
            if (dns.length < 1) {
    
    
                throw new IpConfig.IpConfigParameterException("dns 不能为空");
            } else if (dns.length >= 2) {
    
    
                throw new IpConfig.IpConfigParameterException("dns 暂时支持一个");
            }
        }

        String dns1 = dns[0];

        int prefix = netMaskStringtoInt(netMask);

        if (!wifiManager.isWifiEnabled()) {
    
    
            // wifi is disabled
            //return flag;
            Timber.w("wifi 被关闭,不能更改wifi配置");
        }
        // get the current wifi configuration
        WifiConfiguration wifiConfig = null;
        WifiInfo connectionInfo = wifiManager.getConnectionInfo();
        List<WifiConfiguration> configuredNetworks = wifiManager.getConfiguredNetworks();
        if (configuredNetworks != null) {
    
    
            for (WifiConfiguration conf : configuredNetworks) {
    
    
                if (conf.networkId == connectionInfo.getNetworkId()) {
    
    
                    wifiConfig = conf;
                    break;
                }
            }
        }
        if (wifiConfig == null) {
    
    
            // wifi is not connected
            //return flag;
            Timber.w("wifi 没有连接 ,不能更改wifi配置");
        }

        try {
    
    
            Class<?> ipAssignment = wifiConfig.getClass().getMethod("getIpAssignment").invoke(wifiConfig).getClass();
            Object staticConf = wifiConfig.getClass().getMethod("getStaticIpConfiguration").invoke(wifiConfig);
            if (dhcp) {
    
    
                wifiConfig.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConfig, Enum.valueOf((Class<Enum>) ipAssignment, "DHCP"));
                if (staticConf != null) {
    
    
                    staticConf.getClass().getMethod("clear").invoke(staticConf);
                }
            } else {
    
    
                wifiConfig.getClass().getMethod("setIpAssignment", ipAssignment).invoke(wifiConfig, 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(Constant.dns2)); // Google DNS as DNS2 for safety
                // apply the new static configuration
                wifiConfig.getClass().getMethod("setStaticIpConfiguration", staticConf.getClass()).invoke(wifiConfig, staticConf);
            }

            Timber.d("wificongi--" + wifiConfig.toString());

            wifiManager.save(wifiConfig, new WifiManager.ActionListener() {
    
    
                @Override
                public void onSuccess() {
    
    
                    Timber.d("success");
                }

                @Override
                public void onFailure(int reason) {
    
    
                    Timber.d("onFailure:" + reason);
                }
            });
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
    }

从上面可以看到,通过反射,获得对应的方法和字段,并调用。
其中参数的IpConfig是自定义的类型,用于保存一些信息,基于保密,就不提供了,可以根据源码直接推导出来

第三部依然是清除dns缓存

Intent intent = new Intent(Intent.ACTION_CLEAR_DNS_CACHE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
ApplicationUtil.getApplication().sendBroadcastAsUser(intent, UserHandle.ALL);

通过命令行更新

使用命令行更新需要获得root权限,该方法可以在前期调试Dns时,进行快速的设置。命令如下
使用ndc命令进行设置,使用dumpsys命令进行查看。

# ndc 命令为Network Daemon Command
dumpsys netd #列出当前的所有网络设备

返回结果如下

NetworkController
    Default network: 100

    Networks:
      51 DUMMY dummy0
        No DNS servers defined
        No search domains defined

      99 LOCAL
        No DNS servers defined
        No search domains defined

      100 PHYSICAL eth0
        Required permission: NONE
        No DNS servers defined
        No search domains defined
        DNS parameters: sample validity = 1800s, success threshold = 75%, samples (min, max) = (0, 0)

显示了有多少个网络接口,以及对一个的网络id ,如eth0的id为100

ndc resolver setnetdns 100 "" 8.8.8.8
#解释如下
#ndc: 网络守护进程控制命令
# resolver: ndc中的一个子命令,用于解析dns
# setnetdns: resolver中的一个子命令,用于设置dns
# 100: 对应的eth0的网络id号
# "": dns搜索域的名字,此处为空,即不使用搜索域。dns搜索域补充知识见后面
# 8.8.8.8: dns服务器地址。

补充dns搜索域知识

#在DNS(域名系统)中,搜索域(search domain)是一个用于补全不完全的域名的辅助设置。
#当你查询一个不完整的域名(例如没有顶级域名的短域名)时,搜索域列表中的域名会依次添加到这个不完整的域名后面,然后进行DNS查询,直到找到一个有效的DNS记录或者所有搜索域都尝试完毕。

#搜索域的作用是简化本地网络中的域名解析,使你可以在输入较短的域名时自动补全为完整的域名。

#举个例子,假设你的搜索域设置为`example.com`,当你查询一个不完整的域名`server1`时,DNS解析器会自动将其扩展为`server1.example.com`,然后进行DNS查询。这样,你就可以在输入较短的域名时省略掉顶级域名(例如`.com`)。

#搜索域通常在局域网环境中使用,特别是在企业内部网络中。它们可以通过DHCP(动态主机配置协议)自动分配给连接到网络的设备。你也可以在设备的网络设置中手动配置搜索域。

#需要注意的是,搜索域设置不会影响完整域名的DNS解析。当你查询一个完整的域名(例如`www.example.com`)时,DNS解析器会直接进行查询,而不会使用搜索域进行扩展。

还可以清除DNS,如下

ndc resolver clearnetdns 100

通过hosts文件更新

android的hosts文件在/etc/hosts位置。但是修改这个文件需要root权限,且system分区需要为读写权限。
对于普通使用者,修改这个比较难。
如果是debug系统,可以获得root权限之后,重新挂载system分区,来进行修改。常常用于快速验证

还可以将/etc/hosts文件更换位置。比如/data/data/etc/hosts
修改binonic库的_PATH_HOSTS宏定义将其指向/data/data/etc/hosts

同样的,该文件的权限可以通过init.rc配置文件来配置,让其能够被特定的应用进行修改。
此处涉及更多的SeLinux权限操作,以及init.rc的操作。不在做过多介绍。或许后面有机会记录下来

如果一个rom定制开发者,想要自定义一个/data/data/etc/hosts的文件,此处是一个不错的办法。
当然已经是rom定制开发者了,前面介绍的通过代码修改,或许更加方便

hosts文件的格式

#注释
ip地址   域名1  域名2

ip地址,域名1,域名2之间通过空格隔开
井号后面的为注释

需要注意的是,在bionic库中,hosts文件的解析,高于dns的查询。

猜你喜欢

转载自blog.csdn.net/xiaowanbiao123/article/details/130708659