android 如何更新Dns
android 更新DNS的方式有如下几种。
- 通过代码更新,这里又分为有线网络和无线网络。
- 通过命令行更新
- 通过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的查询。
完