一探究竟:Android M 如何获取 Wifi MAC地址(1)
引子
1 | WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); |
早在 Android M 预览版发布时就有人发现,通过WifiInfo.getMacAddress()
获取的MAC地址是一个“假”的固定值,其值为 “02:00:00:00:00:00”。对于这个,官方的说法当然不外乎“保护用户隐私数据”。
大胆假设
不知是有意还是一时不查,Google却忘了Java获取设备网络设备信息的API——NetworkInterface.getNetworkInterfaces()
——仍然可以间接地获取到MAC地址。
1 | Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); |
输出如下:
1 | interfaceName=dummy0, mac=e6:f9:44:3c:ee:da |
顾名思义,猜想wlan0
对应的mac地址应该就是我们要找的。
小心求证
既然NetworkInterface
可以正常获取,那得好好看看它在 Android framework 中的实现源码:
1 | public byte[] getHardwareAddress() throws SocketException { |
原来MAC地址是直接从"/sys/class/net/" + name + "/address"
文件中读取的!
这个name
是什么呢?
继续翻源码:
1 | private static final File SYS_CLASS_NET = new File("/sys/class/net"); |
可以看出/sys/class/net
目录下的一个文件夹即对应一个NetworkInterface
的name
。
1 | user@android:/$ ls /sys/class/net/ |
从路由器上在线设备的MAC地址列表,可以印证我这台设备Wifi的name
是wlan0
那么读取文件/sys/class/net/wlan0/address
就轻松得到了这台设备的MAC地址
1 | user@android:/$ cat /sys/class/net/wlan0/address |
不出所料!
进而,问题又变成如何获取设备的Wifi的interface name
?
探寻 Wifi interface name
回到开头,我们是通过context.getSystemService(Context.WIFI_SERVICE)
获取的WifiManager
。
而WifiManager
肯定是与远程系统服务的IBinder
在交互,而系统服务都是在SystemServer.run()
中被启动的。
在SystemServer.java
中搜索关键字”WIFI_SERVICE”,很容易便找到mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
顺藤摸瓜,又找到系统服务实现类com.android.server.wifi.WifiService
,WifiService
中的逻辑很简单,构造真正的实现类com.android.server.wifi.WifiServiceImpl
对象并注册到系统服务中:
1 | public final class WifiService extends SystemService { |
打开WifiServiceImpl.java
,从构造方法处,一眼就看到了关键代码:mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
如此这般终于找到定义设备的Wifi的interface name
的地方:SystemProperties
通过adb
可以很容易得到这个属性值:adb shell getprop wifi.interface
那么在我们应用里可以通过Java的反射获取SystemProperties
,进而调用静态方法get
即可拿到Wifi的interface name
。
结论
纵然Google掩耳盗铃似的把API返回值篡改了,但终究是没有改底层的实现,通过阅读源码的方式还是很容易找到解决办法的。
- 通过反射
SystemProperties
获取属性wifi.interface
的值 - 读取
/sys/class/net/+interfaceName+/address
文件的第一行即是Wifi MAC地址
请注意:
- 个别设备可能会拒绝你访问
/sys
目录 /sys/class/net/+interfaceName+/address
文件不一定存在,请在读取之前确认设备的Wifi已经打开或已连接- 未免厂商实现不一的风险,请先尝试用
NetworkInterface.getByName(interfaceName)
来间接获取Wifi MAC地址
结束了?
这么早下结论其实不是我的风格。
本文只是探讨了如何绕过官方设置的障碍,从“黑科技”的角度获取MAC地址。那么官方推荐的“正道”该如何走呢?且看下回分解~