欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Android81.从软件层固定wifi mac地址方法

程序员文章站 2022-03-15 10:34:46
最近做一个Android8.1机器,客户懒惰得不想用工具写WIFI mac地址,又不希望每次重启WIFI mac地址都会变化,想要固定下来。网上搜索了一圈,没找到一个有效可行的方法,还是自己老实跟代码吧。 思路:找到系统是在哪里读写mac地址,然后将第一次开机随机生成的地址写入nvram,应该就能实现固定。 所以首先得找到系统怎么获取显示随机mac地址的: 首先知道设置里关于手机---状态信息里有WIFI MAC地址显示这个是在Status.java里具体实现......

最近做一个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