Android正确获取IP及MAC

每个产品都会统计用户终端信息。稍不注意就会经我们的手造成公司得到的数据错误,造成分析甚至战略错误。(假装是程序猿缔造了世界)

设备:Oppo R9s、vivo X9
系统:Android 6.0.2
网络:4G
错误IP和MAC分别为


fe80::188c:24ff:fe49:8e54%dummy0
02:00:00:00:00:00

错误代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static String getLocalIpAddress() {
try {
for (
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
networkInterfaces.hasMoreElements(); ) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
for (Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
inetAddresses.hasMoreElements(); ) {
InetAddress inetAddress = inetAddresses.nextElement();
if (!inetAddress.isLoopbackAddress()) {
String ipAddress = inetAddress.getHostAddress().toUpperCase();
return ipAddress;
}
}
}
} catch (SocketException e) {
}
return null;
}

然后根据搜索结果认为,直接将“%dummy0%2”去掉结果即为ipv6地址。不管你信不信反正当时我是信了,后来脑海里有一个声音时不时告诉我这不对,世界不是这样的。(程序猿后遗症)

上面的代码中我们屏蔽了Loopback address。并且获得的fe80::188c:24ff:fe49:8e54确为IPv6,但是前缀为fe80::/64都是链路本地地址,不是我们真正想要的ip。IPv6几种地址类型、概念,详细见IPv6

解锁获取IP正确姿势

经过上面度娘普及姿势得知Link-local address、Loopback address不是我们想要的IP,于是干掉它们,改造后代码如下(仅需要注意注释后面那一句)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static String getLocalIpAddress() {
try {
for (
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
networkInterfaces.hasMoreElements(); ) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
for (Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
inetAddresses.hasMoreElements(); ) {
InetAddress inetAddress = inetAddresses.nextElement();
//过滤Loopback address, Link-local address
if (!inetAddress.isLoopbackAddress() && !inetAddress.isLinkLocalAddress()) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
}
return null;
}

OPPO R9s Android 6.0.1运行结果:

类型 IP
wifi 172.27.35.3
流量 100.58.248.64

扩展-解锁6.0后获取正确MAC姿势

dummy0、lo、p2p0、rev_rmnet0、rev_rmnet1、rev_rmnet2、rev_rmnet3、rmnet0、rmnet1、rmnet2、rmnet3、rmnet_smux0、sit0、wlan0等等,我们发现有很多这样的名称。我的理解是除了wlan0是与电脑一样的真实无线网卡外,其它全是虚拟网卡,用于内部通信。
在6.0之后,Android 移除了对设备本地硬件标识符的编程访问权。使用WifiInfo.getMacAddress() 、BluetoothAdapter.getAddress() 返回值为常量02:00:00:00:00:00。有了上面通过Java api获取IP地址的经验,获取mac地址是否也可以在这里使用。上代码

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
public static String getMac() {
try {
for (
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
networkInterfaces.hasMoreElements(); ) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
if ("wlan0".equals(networkInterface.getName())) {
byte[] hardwareAddress = networkInterface.getHardwareAddress();
if (hardwareAddress == null || hardwareAddress.length == 0) {
continue;
}
StringBuilder buf = new StringBuilder();
for (byte b : hardwareAddress) {
buf.append(String.format("%02X:", b));
}
if (buf.length() > 0) {
buf.deleteCharAt(buf.length() - 1);
}
String mac = buf.toString();
return mac;
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}

OPPO R9s Android 6.0.1运行结果:

代码类型 MAC 权限
WifiInfo 02:00:00:00:00:00 android.permission.ACCESS_WIFI_STATE
NetworkInterface B8:37:65:2D:23:ED android.permission.INTERNET

这里面涉及到挺多网络知识,现在还是比较懵逼
dummy0、lo、p2p0、rev_rmnet0、rev_rmnet1、rev_rmnet2、rev_rmnet3、rmnet0、rmnet1、rmnet2、rmnet3、rmnet_smux0、sit0到底咋回事儿?

特别鸣谢

大牛老张,前公司上司,全栈。最近搞了一个有些性感的产品图交
硬件玩家,网络牛人莽哥

参考:

IPv6
InetAddress API
硬件标识符访问权