Android 以太网IP设置

简单记录下 Android 5.1 系统以太网的 IP 设置方法。(到5.1的版本为止,与以太网相关的 API 都是隐藏起来的,有@hide注解,需要通过反射调用,或者引用没有@hide注解的 android.jar 文件,可以使用编译源码生成的 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar)

EthernetManager 获取

EthernetManager 使用单例模式,可以通过 ContextgetSystemService() 方法获取:

1
2
3
private EthernetManager getEthernetManager(Context context) {
return (EthernetManager) context.getSystemService(Context.ETHERNET_SERVICE);
}

Context.ETHERNET_SERVICE 也是有@hide注解的,可以定义一个常量替代:

1
private static final String ETHERNET_SERVICE = "ethernet";

DHCP

以太网的 IP 设置通过 IpConfiguration 来配置,其中的 ipAssignment 变量决定 IP 的分配方式,值可以为 IpAssignment.DHCPIpAssignment.STATICIpAssignment.UNASSIGNED,分别为动态 IP、静态 IP、未指定。IpConfiguration 通过 EthernetManagergetConfiguration() 获取,通过 setConfiguration() 使配置生效。设置以太网使用 DHCP 方式获取 IP 的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/** 设置网线使用DHCP方式获取IP */
public void enableEthernetDHCP(Context context) {
Logger.d("enableEthernetDHCP");

if (context == null) {
Logger.e("invaild argument");
return;
}

EthernetManager em = getEthernetManager(context);
IpConfiguration configuration = em.getConfiguration();
configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
em.setConfiguration(configuration);
}

静态 IP

静态 IP 方式则稍微复杂一点,静态 IP 方式需要指定 IP 地址、子网掩码、网关地址、DNS 服务器地址,这几项信息不对会造成设置失败。这些信息的通过 StaticIpConfiguration 设置,StaticIpConfiguration 通过 IpConfiguration 获取:

1
StaticIpConfiguration staticIpConfiguration = configuration.getStaticIpConfiguration();

首先修改 IpConfigurationipAssignment 值为 IpAssignment.STATIC,然后设置静态 IP 的信息,最后 setConfiguration() 使配置生效,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
* @author cengt 2016/11/1 10:01
* @version v1.0
*/

public class XTHost {
/** IP */
private String ip;

/** 网关 */
private String gateway;

/** 子网掩码 */
private String netmask;

...

}

/** 设置网线使用静态方式获取IP */
public void enableEthernetStaticIP(Context context, XTHost host) {
Logger.d("enableEthernetStaticIP: host = " + host);

if (context == null || host == null) {
Logger.e("invalid argument");
return;
}

EthernetManager em = getEthernetManager(context);
IpConfiguration configuration = em.getConfiguration();
configuration.setIpAssignment(IpConfiguration.IpAssignment.STATIC);

StaticIpConfiguration staticIpConfiguration = configuration.getStaticIpConfiguration();
staticIpConfiguration.gateway = NetworkUtils.numericToInetAddress(host.getGateway());
InetAddress ipAddress = NetworkUtils.numericToInetAddress(host.getIp());
int prefixLength = netmask2PrefixLength(host.getNetmask());
staticIpConfiguration.ipAddress = createLinkAddress(ipAddress, prefixLength);
// 使用网关作为DNS地址
staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(host.getGateway()));

configuration.setStaticIpConfiguration(staticIpConfiguration);
em.setConfiguration(configuration);
}

/** 掩码字符串转为掩码长度 */
private int netmask2PrefixLength(String netmask) {
int sum = 0;
String[] values = netmask.split("\\.");
for (String s : values) {
sum += NetworkUtils.netmaskIntToPrefixLength(Integer.parseInt(s));
}
return sum;
}

private LinkAddress createLinkAddress(InetAddress ipAddress, int prefixLength) {
try {
Class<?> clazz = Class.forName("android.net.LinkAddress");
Class<?>[] parTypes = new Class<?>[2];
parTypes[0] = InetAddress.class;
parTypes[1] = int.class;
Constructor<?> constructor = clazz.getConstructor(parTypes);

Object[] pars = new Object[2];
pars[0] = ipAddress;
pars[1] = prefixLength;
return (LinkAddress) constructor.newInstance(pars);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

其中子网掩码是通过二进制形式1的位数设置的,比如: 255.255.255.0 则设置为24,LinkAddress 类通过反射方式获取实例。

判断以太网线是否插入

可以通过 /sys/class/net/eth0/operstate 这个文件判断以太网是否插入,插入网线的情况下这个文件的值为 up,未插入情况下为 down,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/** 网线是否插入 */
@SuppressWarnings("ResultOfMethodCallIgnored")
public boolean isEthernetPlugin() {
FileInputStream fin = null;
try {
fin = new FileInputStream(ETHERNET_OPERSTATE_PATH);
byte[] buff = new byte[fin.available()];
fin.read(buff);
String str = new String(buff, StandardCharsets.UTF_8);
return "UP".equalsIgnoreCase(str.trim());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

return false;
}

获取以太网的连接信息

通过 ConnectivityManagergetLinkProperties() 方法获取,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/** 获取网线连接信息 */
public XTHost getEthernetInfo(Context context) {
ConnectivityManager cm = getConnectivityManager(context);
Network ethernetNetwork = getEthernetNetwork(context);
if (ethernetNetwork != null) {
LinkProperties activedLinkProperties = cm.getLinkProperties(ethernetNetwork);
if (activedLinkProperties != null) {
XTHost host = new XTHost();

// 查找IP和子网掩码
List<LinkAddress> addresses = activedLinkProperties.getLinkAddresses();
if (addresses.size() > 0) {
LinkAddress addr = addresses.get(0);
host.setIp(addr.getAddress().getHostAddress());
host.setNetmask(prefixLength2Netmask(addr.getPrefixLength()));
} else {
Logger.e("linkaddresses size <= 0");
}

// 查找默认的网关
boolean foundDefaultRoute = false;
List<RouteInfo> routes = activedLinkProperties.getRoutes();
for (RouteInfo r : routes) {
if (r.isDefaultRoute()) {
host.setGateway(r.getGateway().getHostAddress());
foundDefaultRoute = true;
break;
}
}
// 没找到默认的网关则使用第一个
if (!foundDefaultRoute && routes.size() > 0) {
host.setGateway(routes.get(0).getGateway().getHostAddress());
}

return host;
}
} else {
Logger.e("error get ethernet network");
}
return null;
}

private Network getEthernetNetwork(Context context) {
ConnectivityManager cm = getConnectivityManager(context);
for (Network network : cm.getAllNetworks()) {
NetworkInfo info = cm.getNetworkInfo(network);
if (info != null && info.getType() == ConnectivityManager.TYPE_ETHERNET) {
return network;
}
}
return null;
}

完整源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
package com.xingtel.xtalk.manager;

import android.content.Context;
import android.net.ConnectivityManager;
import android.net.EthernetManager;
import android.net.IpConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StaticIpConfiguration;

import com.orhanobut.logger.Logger;
import com.xingtel.xtalk.model.XTHost;

import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Locale;

import static android.content.Context.CONNECTIVITY_SERVICE;

/**
* @author cengt 2016/11/14 12:18
* @version v1.0
*/

public class XTNetWorkManager {
private static final String ETHERNET_SERVICE = "ethernet";

private static final String ETHERNET_OPERSTATE_PATH = "/sys/class/net/eth0/operstate";

public static XTNetWorkManager getInstance() {
return SingletonHolder.INSTANCE;
}

/** 网络是否正常(包括所有网络) */
public boolean isNetWorkOn(Context context) {
if (context != null) {
ConnectivityManager cm = getConnectivityManager(context);
NetworkInfo activedNetworkInfo = cm.getActiveNetworkInfo();
return (activedNetworkInfo != null && activedNetworkInfo.isConnected());
}
return false;
}

/** 网线是否插入 */
@SuppressWarnings("ResultOfMethodCallIgnored")
public boolean isEthernetPlugin() {
FileInputStream fin = null;
try {
fin = new FileInputStream(ETHERNET_OPERSTATE_PATH);
byte[] buff = new byte[fin.available()];
fin.read(buff);
String str = new String(buff, StandardCharsets.UTF_8);
return "UP".equalsIgnoreCase(str.trim());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fin != null) {
try {
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

return false;
}

public boolean isEthernetConnected(Context context) {
Network network = getEthernetNetwork(context);
if (network != null) {
NetworkInfo info = getConnectivityManager(context).getNetworkInfo(network);
return info != null && info.isConnected();
}
return false;
}

/** 网线是否使用DHCP方式获取IP */
public boolean isEthernetUseDHCP(Context context) {
if (context == null) {
Logger.e("invaild argument");
return false;
}

EthernetManager em = getEthernetManager(context);
return em != null && em.getConfiguration().getIpAssignment() == IpConfiguration.IpAssignment.DHCP;
}

/** 设置网线使用DHCP方式获取IP */
public void enableEthernetDHCP(Context context) {
Logger.d("enableEthernetDHCP");

if (context == null) {
Logger.e("invaild argument");
return;
}

EthernetManager em = getEthernetManager(context);
IpConfiguration configuration = em.getConfiguration();
configuration.setIpAssignment(IpConfiguration.IpAssignment.DHCP);
em.setConfiguration(configuration);
}

/** 设置网线使用静态方式获取IP */
public void enableEthernetStaticIP(Context context, XTHost host) {
Logger.d("enableEthernetStaticIP: host = " + host);

if (context == null || host == null) {
Logger.e("invaild argument");
return;
}

EthernetManager em = getEthernetManager(context);
IpConfiguration configuration = em.getConfiguration();
configuration.setIpAssignment(IpConfiguration.IpAssignment.STATIC);

StaticIpConfiguration staticIpConfiguration = configuration.getStaticIpConfiguration();
staticIpConfiguration.gateway = NetworkUtils.numericToInetAddress(host.getGateway());
InetAddress ipAddress = NetworkUtils.numericToInetAddress(host.getIp());
int prefixLength = netmask2PrefixLength(host.getNetmask());
staticIpConfiguration.ipAddress = createLinkAddress(ipAddress, prefixLength);
// 使用网关作为DNS地址
staticIpConfiguration.dnsServers.add(NetworkUtils.numericToInetAddress(host.getGateway()));

configuration.setStaticIpConfiguration(staticIpConfiguration);
em.setConfiguration(configuration);
}

/** 获取网线连接信息 */
public XTHost getEthernetInfo(Context context) {
ConnectivityManager cm = getConnectivityManager(context);
Network ethernetNetwork = getEthernetNetwork(context);
if (ethernetNetwork != null) {
LinkProperties activedLinkProperties = cm.getLinkProperties(ethernetNetwork);
if (activedLinkProperties != null) {
XTHost host = new XTHost();

// 查找IP和子网掩码
List<LinkAddress> addresses = activedLinkProperties.getLinkAddresses();
if (addresses.size() > 0) {
LinkAddress addr = addresses.get(0);
host.setIp(addr.getAddress().getHostAddress());
host.setNetmask(prefixLength2Netmask(addr.getPrefixLength()));
} else {
Logger.e("linkaddresses size <= 0");
}

// 查找默认的网关
boolean foundDefaultRoute = false;
List<RouteInfo> routes = activedLinkProperties.getRoutes();
for (RouteInfo r : routes) {
if (r.isDefaultRoute()) {
host.setGateway(r.getGateway().getHostAddress());
foundDefaultRoute = true;
break;
}
}
// 没找到默认的网关则使用第一个
if (!foundDefaultRoute && routes.size() > 0) {
host.setGateway(routes.get(0).getGateway().getHostAddress());
}

return host;
}
} else {
Logger.e("error get ethernet network");
}
return null;
}

private XTNetWorkManager() {}

@SuppressWarnings("WrongConstant")
private EthernetManager getEthernetManager(Context context) {
return (EthernetManager) context.getSystemService(ETHERNET_SERVICE);
}

private ConnectivityManager getConnectivityManager(Context context) {
return (ConnectivityManager) context.getSystemService(CONNECTIVITY_SERVICE);
}

private Network getEthernetNetwork(Context context) {
ConnectivityManager cm = getConnectivityManager(context);
for (Network network : cm.getAllNetworks()) {
NetworkInfo info = cm.getNetworkInfo(network);
if (info != null && info.getType() == ConnectivityManager.TYPE_ETHERNET) {
return network;
}
}
return null;
}

/** 掩码长度转为掩码字符串 */
private String prefixLength2Netmask(int prefixLength) {
int value = 0xFFFFFFFF << (32 - prefixLength);
int a = 0xFF & (value >> 24);
int b = 0xFF & (value >> 16);
int c = 0xFF & (value >> 8);
int d = 0xFF & value;
return String.format(Locale.getDefault(), "%d.%d.%d.%d", a, b, c, d);
}

/** 掩码字符串转为掩码长度 */
private int netmask2PrefixLength(String netmask) {
int sum = 0;
String[] values = netmask.split("\\.");
for (String s : values) {
sum += NetworkUtils.netmaskIntToPrefixLength(Integer.parseInt(s));
}
return sum;
}

private LinkAddress createLinkAddress(InetAddress ipAddress, int prefixLength) {
try {
Class<?> clazz = Class.forName("android.net.LinkAddress");
Class<?>[] parTypes = new Class<?>[2];
parTypes[0] = InetAddress.class;
parTypes[1] = int.class;
Constructor<?> constructor = clazz.getConstructor(parTypes);

Object[] pars = new Object[2];
pars[0] = ipAddress;
pars[1] = prefixLength;
return (LinkAddress) constructor.newInstance(pars);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

private static class SingletonHolder {
private static final XTNetWorkManager INSTANCE = new XTNetWorkManager();
}
}