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

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

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

错误代码:

  public static String getLocalIpAddress() {    try {      for (          Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();          networkInterfaces.hasMoreElements(); ) {        NetworkInterface networkInterface = networkInterfaces.nextElement();        for (Enumeration 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,于是干掉它们,改造后代码如下(仅需要注意注释后面那一句)

  public static String getLocalIpAddress() {    try {      for (          Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces();          networkInterfaces.hasMoreElements(); ) {        NetworkInterface networkInterface = networkInterfaces.nextElement();        for (Enumeration 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地址是否也可以在这里使用。上代码

  public static String getMac() {    try {      for (          Enumeration 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
硬件标识符访问权

更多相关文章

  1. Android: 八种燃爆了的菜单效果
  2. android设备与蓝牙模块之间交互(蓝牙命令,收发)的两种方式,附DEMO下
  3. 安装Android的Eclipse插件ADT遇到错误“requires 'org.eclipse.g
  4. Android(安卓)地址转换为经纬度
  5. android:如何从照片中获取拍摄地址信息
  6. Android(安卓)ADT 离线下载技巧(告别在线安装的麻烦)
  7. Android地图获取详细街道地址信息 精确定位
  8. 终于松了口气,说说这周我调试Sate210 android下DM9000 驱动的吐槽
  9. Android开源项目--工具库篇

随机推荐

  1. C#中关于表达式树的简单介绍
  2. Rest在asp.net MVC下使用的方法介绍
  3. C#异步之APM模式异步程序开发的示例分享
  4. C#中关于Minutes与TotalMinutes的区别
  5. ASP.NET MVC如何使用Bootstrap的实例分析
  6. C#中关于Cookies的读取实例详解
  7. ASP.NET MVC如何正确运用异步编程技术
  8. C#中关于静态与非静态方法的区别介绍
  9. C#如何使用键值对取代Switch...Case语句
  10. C#中关于Dictionary的用法详解