当前位置: 代码迷 >> 综合 >> Android81.从软件层固定wifi mac地址方法
  详细解决方案

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

热度:58   发布时间:2024-02-09 05:10:43.0

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

  相关解决方案