最近接手一个新项目,该项目中一直使用getMacAddress获取设备的mac地址。我在实际测试中发现,小米4的手机该方法一直返回02:00:00:00:00:00。
查阅资料得知,android 6.0之后,官方加强了对访问硬件信息的管控。
To provide users with greater data protection, starting in this release, Android removes programmatic access to the device’s local hardware identifier for apps using the Wi-Fi and Bluetooth APIs. The WifiInfo.getMacAddress() and the BluetoothAdapter.getAddress() methods now return a constant value of 02:00:00:00:00:00.
To access the hardware identifiers of nearby external devices via Bluetooth and Wi-Fi scans, your app must now have the ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permissions.
解决方法
使用Java获取设备网络设备信息的API:NetworkInterface.getNetworkInterfaces() 间接地获取到MAC地址。
具体代码如下:
public static String getLocalMacAddress(Context context) {
String mac;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mac = getMachineHardwareAddress();
} else {
WifiManager wifi = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
WifiInfo info = wifi.getConnectionInfo();
mac = info.getMacAddress().replace(":", "");
}
return mac;
}
/**
* 获取设备的mac地址和IP地址(android6.0以上专用)
* @return
*/
public static String getMachineHardwareAddress(){
Enumeration<NetworkInterface> interfaces = null;
try {
interfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
e.printStackTrace();
}
String hardWareAddress = null;
NetworkInterface iF = null;
while (interfaces.hasMoreElements()) {
iF = interfaces.nextElement();
try {
hardWareAddress = bytesToString(iF.getHardwareAddress());
if(hardWareAddress == null) continue;
} catch (SocketException e) {
e.printStackTrace();
}
}
if(iF != null && iF.getName().equals("wlan0")){
hardWareAddress = hardWareAddress.replace(":","");
}
return hardWareAddress ;
}
/***
* byte转为String
* @param bytes
* @return
*/
private static String bytesToString(byte[] bytes){
if (bytes == null || bytes.length == 0) {
return null ;
}
StringBuilder buf = new StringBuilder();
for (byte b : bytes) {
buf.append(String.format("%02X:", b));
}
if (buf.length() > 0) {
buf.deleteCharAt(buf.length() - 1);
}
return buf.toString();
}
在搜索资料时,还看到一篇博客通过执行shell命令来获取mac地址。可供大家参考,网站地址:
http://blog.csdn.net/zhaohaiyan629/article/details/51122344
但是在实际测试中,出现了一个兼容性问题。在华为荣耀8手机,android 7.0系统运行该代码出现如下异常。
该手机直接执行“adb shell cat /sys/class/net/wlan0/address”提示Permission Denied。找另外的小米手机6.0及7.0均可以执行该命令,且可以通过代码获取到Mac地址。到这里可以总结出因为华为荣耀8的7.0系统修改了要访问的文件权限,导致该错误。而通过Runtime执行su命令也会提示权限不足。
这种场景下,建议通过本文中提到的NetworkInterface.getNetworkInterfaces方法获取Mac地址。
执行shell脚本获取mac的主要代码如下:
/**
* 获取手机的MAC地址
* @return
*/
public String getMac(){
String str="";
String macSerial="";
try {
Process pp = Runtime.getRuntime().exec(
"cat /sys/class/net/wlan0/address ");
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
for (; null != str;) {
str = input.readLine();
if (str != null) {
macSerial = str.trim();// 去空格
break;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
if (macSerial == null || "".equals(macSerial)) {
try {
return loadFileAsString("/sys/class/net/eth0/address")
.toUpperCase().substring(0, 17);
} catch (Exception e) {
e.printStackTrace();
}
}
return macSerial;
}
public static String loadFileAsString(String fileName) throws Exception {
FileReader reader = new FileReader(fileName);
String text = loadReaderAsString(reader);
reader.close();
return text;
}
public static String loadReaderAsString(Reader reader) throws Exception {
StringBuilder builder = new StringBuilder();
char[] buffer = new char[4096];
int readLength = reader.read(buffer);
while (readLength >= 0) {
builder.append(buffer, 0, readLength);
readLength = reader.read(buffer);
}
return builder.toString();
}