Android81.从软件层固定wifi mac地址方法
最近做一个Android8.1机器,客户懒惰得不想用工具写WIFI mac地址,又不希望每次重启WIFI mac地址都会变化,想要固定下来。网上搜索了一圈,没找到一个有效可行的方法,还是自己老实跟代码吧。
思路:找到系统是在哪里读写mac地址,然后将第一次开机随机生成的地址写入nvram,应该就能实现固定。
所以首先得找到系统怎么获取显示随机mac地址的:
首先知道设置里关于手机---状态信息里有WIFI MAC地址显示这个是在Status.java里具体实现如下:
private void setWifiStatus() {
WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
boolean hasMacAddress = wifiInfo != null && wifiInfo.hasRealMacAddress();
//这里获取了一个WiFiInfo对象,然后getMacAddress.
String macAddress = hasMacAddress ? wifiInfo.getMacAddress() : null;
mWifiMacAddress.setSummary(!TextUtils.isEmpty(macAddress)
? mExt.customizeMacAddressString(macAddress, mUnavailable)
: mUnavailable);
}
查看WiFiInfo.java
/**
* Record the MAC address of the WLAN interface
* @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
* @hide
*/
public void setMacAddress(String macAddress) { //mac地址从设置方法传入的,搜索wifi模块setMacAddress方法
this.mMacAddress = macAddress;
}
public String getMacAddress() {
return mMacAddress;
}
找到是在WifiStateMachine.java中有设置,找到有好几个地方有设置,不过其他地方都是设置默认的一个地址,唯独这里:
case WifiMonitor.SUP_CONNECTION_EVENT:
if (mVerboseLoggingEnabled) log("Supplicant connection established");
mSupplicantRestartCount = 0;
/* Reset the supplicant state to indicate the supplicant
* state is not known at this time */
mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
/* Initialize data structures */
mLastBssid = null;
mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
mLastSignalLevel = -1;
///可以看到是从WiFiNative里获取设置进去的。
mWifiInfo.setMacAddress(mWifiNative.getMacAddress());
initializeWpsDetails();
sendSupplicantConnectionChangedBroadcast(true);
///M: ALPS03503585 Disable EAP-SIM AP if modem is not ready
MtkEapSimUtility.disableSimConfigWhenSimNotLoaded();
transitionTo(mSupplicantStartedState);
查看WifiNative.java:
public String getMacAddress() {
return mSupplicantStaIfaceHal.getMacAddress();
}
SupplicantStaIfaceHal.java
public String getMacAddress() {
synchronized (mLock) {
final String methodStr = "getMacAddress";
if (!checkSupplicantStaIfaceAndLogFailure(methodStr)) return null;
Mutable<String> gotMac = new Mutable<>();
try {
mISupplicantStaIface.getMacAddress((SupplicantStatus status,
byte[/* 6 */] macAddr) -> {
if (checkStatusAndLogFailure(status, methodStr)) {
gotMac.value = NativeUtil.macAddressFromByteArray(macAddr);
}
});
} catch (RemoteException e) {
handleRemoteException(e, methodStr);
}
return gotMac.value;
}
}
Native 最终跟到的接口在:
vendor\mediatek\proprietary\hardware\interfaces\nvram\1.0\INvram.hal中分别定义了
interface INvram {
readFileByName(string filename, uint32_t size)
generates (string data);
writeFileByNamevec(string filename, uint32_t size, vec<uint8_t> data)
generates (int8_t retval);
};
读写的地址是 /vendor/nvdata/APCFG/APRDEB/WIFI 。
所以直接操作NVRam:
从NvRam读mac地址方法:
private String getMacAddrFromNvram() {
StringBuffer nvramBuf = new StringBuffer();
try {
int i = 0;
String buff = null;
INvram agent = INvram.getService(); //获取INvram的客户端对象
if (agent == null) {
Log.e(TAG+":"+"NvRam", "NvRAMAgent is null");
return ""; }
try {
buff = agent.readFileByName(MAC_ADDRESS_FILENAME, MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
} catch (Exception e) {
e.printStackTrace();
return "";
}
Log.i(TAG, "Raw data:" + buff);
if (buff.length() < 2 * (MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS)) {
return "";
}
// Remove the \0 special character.
int macLen = buff.length() - 1;
for (i = MAC_ADDRESS_OFFSET * 2; i < macLen; i += 2) {
if ((i + 2) < macLen) {
nvramBuf.append(buff.substring(i, i + 2));
nvramBuf.append(":");
} else {
nvramBuf.append(buff.substring(i));
}
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
return nvramBuf.toString();
}
向NvRam中写入wifi mac地址:
private int updateWifiMacToNvram(String mac){//mac 00:08:22:11:06:06
try {
int i = 0;
INvram agent = INvram.getService();
byte[] macAddr = new byte[MAC_ADDRESS_DIGITS];
if (agent == null) {
Log.e(TAG+":"+"NvRam", "NvRAMAgent is null");
return 0;
}
//parse mac address firstly
StringTokenizer txtBuffer = new StringTokenizer(mac, ":");
while (txtBuffer.hasMoreTokens()) {
macAddr[i] = (byte) Integer.parseInt(txtBuffer.nextToken(), 16);
i++;
}
if(i != MAC_ADDRESS_DIGITS){
Log.e(TAG+":"+"NvRam", "Wrong length of macAddr:" + i);
return 0;
}
String buff = null;
try {
buff = agent.readFileByName(MAC_ADDRESS_FILENAME, MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
byte[] buffArr = HexDump.hexStringToByteArray(buff.substring(0, buff.length() - 1));
for (i = 0; i < MAC_ADDRESS_DIGITS; i ++) {
buffArr[i + 4] = macAddr[i];
}
ArrayList<Byte> dataArray = new ArrayList<Byte>(MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS);
for (i = 0; i < MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS; i++) {
dataArray.add(i, new Byte(buffArr[i]));
}
int flag = 0;
try {
flag = agent.writeFileByNamevec(MAC_ADDRESS_FILENAME,MAC_ADDRESS_OFFSET + MAC_ADDRESS_DIGITS,dataArray);
Log.e(TAG+":"+"NvRam", "write mac to nvram compeleted flag=" + flag);
return flag;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}catch (Exception e) {
e.printStackTrace();
}
return 0;
}
还有一个随机生成有效mac地址的方法:
private String getRandomMacAddress(){
short[] mRandomMacAddr = new short[MAC_ADDRESS_DIGITS];
StringBuilder sb = new StringBuilder();
Random rand = new Random();
NumberFormat formatter = new DecimalFormat("00");
int end1 = rand.nextInt(100);
int end2 = rand.nextInt(100);
//int end3 = rand.nextInt(100);
String num1 = formatter.format(end1);
String num2 = formatter.format(end2);
//String num3 = formatter.format(end3);
sb.append("00:08:22:11:");
sb.append(num1).append(":").append(num2);
return sb.toString();
}
有这几个方法,可以将这些方法写到一个服务里, 找到一个开机会走到的地方,比如监听开机广播,我的是在systemui:
KeyguardUpdateMonitor.java中case MSG_BOOT_COMPLETED:中添加启动一个服务器类,在服务中判断即可。
然后读取当前的mac地址,如果是00:00:00:00:00:00则随机生成一个mac地址写入nvram即可。需要注意的是读到的mac地址00:00:00:00:00:00末尾会有一个\0,所以用equals方法去判断的时候需要注意。最好直接用contains比如:
if(mac.equals("") || mac.contains("00:00:00:00:00:00")){
wifimac = getRandomMacAddress();
Log.e(TAG+":"+"NvRam"," start write wifimac =" + wifimac);
mHandler.postDelayed(updateMacToNvRam, 1000);
}
另外方法中用到的常量,也贴出来:
private static final int MAC_ADDRESS_OFFSET = 4;
private static final int MAC_ADDRESS_DIGITS = 6;
private static final String MAC_ADDRESS_FILENAME = "/vendor/nvdata/APCFG/APRDEB/WIFI";
需要导入的方法:
import java.util.StringTokenizer;
import vendor.mediatek.hardware.nvram.V1_0.INvram;
import com.android.internal.util.HexDump;
import java.text.NumberFormat;
import java.util.Random;
import java.text.DecimalFormat;
import java.util.ArrayList;
亲测可用的方案,需注意第一次开机后,写入了,但要开关一下wifi才能在设置状态信息里看到mac地址。
本文地址:https://blog.csdn.net/u013560831/article/details/107917840
上一篇: Flutter 获取设备屏幕的宽高