Android Mac地址

android系統(tǒng)6.0之后忠荞,對mac地址的獲取添加了權(quán)限委煤,按照普通的方法獲取如下:

WifiManager  wifiMan = (WifiManager)context.getSystemService(Context.WIFI_SERVICE) ;
WifiInfo wifiInf = wifiMan.getConnectionInfo();
return wifiInf.getMacAddress();

但是這種情況碧绞,獲取到的mac地址都是:02:00:00:00:00:00讥邻,得知通過WifiInfo.getMacAddress()獲取的MAC地址是一個“假”的固定值,官方其實(shí)是有說明的: “保護(hù)用戶隱私數(shù)據(jù)” 系宜。

查詢資料找到了相應(yīng)的解決方法:
使用Java獲取設(shè)備網(wǎng)絡(luò)設(shè)備信息的API——NetworkInterface.getNetworkInterfaces() ——仍然可以間接地獲取到MAC地址鲫惶。

    // 有興趣的朋友可以看下NetworkInterface在Android FrameWork中怎么實(shí)現(xiàn)的
    public static String macAddress() throws SocketException {
            String address = null;
            // 把當(dāng)前機(jī)器上的訪問網(wǎng)絡(luò)接口的存入 Enumeration集合中
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface netWork = interfaces.nextElement();
                // 如果存在硬件地址并可以使用給定的當(dāng)前權(quán)限訪問,則返回該硬件地址(通常是 MAC)欢策。 
                byte[] by = netWork.getHardwareAddress();
                if (by == null || by.length == 0) {
                    continue;
                }
                StringBuilder builder = new StringBuilder();
                for (byte b : by) {
                    builder.append(String.format("%02X:", b));
                }
                if (builder.length() > 0) {
                    builder.deleteCharAt(builder.length() - 1);
                }
                String mac = builder.toString();
                Log.d("mac", "interfaceName="+netWork.getName()+", mac="+mac);
                // 從路由器上在線設(shè)備的MAC地址列表踩寇,可以印證設(shè)備Wifi的 name 是 wlan0
                if (netWork.getName().equals("wlan0")) {
                    Log.d("mac", " interfaceName ="+netWork.getName()+", mac="+mac);
                    address = mac;
                }
            }
            return address;
        } 

運(yùn)行結(jié)果:

05-09 18:39:31.528: D/mac(5132): interfaceName=wlan0, mac=8C:BE:BE:BD:EC:0E
05-09 18:39:31.528: D/mac(5132): interfaceName=wlan0, mac=8C:BE:BE:BD:EC:0E
05-09 18:39:31.528: D/mac(5132): interfaceName=p2p0, mac=8E:BE:BE:BD:EC:0E
05-09 18:39:31.538: D/mac(5132): interfaceName=dummy0, mac=42:35:AD:88:CE:D2
05-09 18:39:31.538: D/mac(5132): interfaceName=usbnet0, mac=52:D9:15:FA:64:1A
05-09 18:39:31.538: D/mac(5132): interfaceName=rmnet0, mac=C6:76:E0:64:56:AC

注意:在使用上述代碼時俺孙,記得添加以下權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />

小心求證

既然NetworkInterface可以正常獲取掷贾,那得好好看看它在 Android framework 中的實(shí)現(xiàn)源碼:

public byte[] getHardwareAddress() throws SocketException {
    try {
        // Parse colon-separated bytes with a trailing newline: "aa:bb:cc:dd:ee:ff\n".
        String s = IoUtils.readFileAsString("/sys/class/net/" + name + "/address");
        byte[] result = new byte[s.length()/3];
        for (int i = 0; i < result.length; ++i) {
            result[i] = (byte) Integer.parseInt(s.substring(3*i, 3*i + 2), 16);
        }
        // We only want to return non-zero hardware addresses.
        for (int i = 0; i < result.length; ++i) {
            if (result[i] != 0) {
                return result;
            }
        }
        return null;
    } catch (Exception ex) {
        throw rethrowAsSocketException(ex);
    }
}

原來MAC地址是直接從"/sys/class/net/" + name + "/address"文件中讀取的想帅!

這個name是什么呢?

繼續(xù)翻源碼:

private static final File SYS_CLASS_NET = new File("/sys/class/net");
...
public static Enumeration<NetworkInterface> getNetworkInterfaces() throws SocketException {
    return Collections.enumeration(getNetworkInterfacesList());
}
private static List<NetworkInterface> getNetworkInterfacesList() throws SocketException {
    String[] interfaceNames = SYS_CLASS_NET.list();
    NetworkInterface[] interfaces = new NetworkInterface[interfaceNames.length];
    String[] ifInet6Lines = readIfInet6Lines();
    for (int i = 0; i < interfaceNames.length; ++i) {
        interfaces[i] = NetworkInterface.getByNameInternal(interfaceNames[i], ifInet6Lines);
        ...
    }
    List<NetworkInterface> result = new ArrayList<NetworkInterface>();
    for (int counter = 0; counter < interfaces.length; counter++) {
        ...
        result.add(interfaces[counter]);
    }
    return result;
}

可以看出/sys/class/net目錄下的一個文件夾即對應(yīng)一個NetworkInterface的name旨剥。

user@android:/$ ls /sys/class/net/
dummy0
lo
p2p0
rev_rmnet0
rev_rmnet1
rev_rmnet2
rev_rmnet3
rmnet0
rmnet1
rmnet2
rmnet3
rmnet_smux0
sit0
wlan0

從路由器上在線設(shè)備的MAC地址列表轨帜,可以印證我這臺設(shè)備Wifi的name是wlan0

那么讀取文件/sys/class/net/wlan0/address就輕松得到了這臺設(shè)備的MAC地址

user@android:/$ cat /sys/class/net/wlan0/address
ac:22:0b:3e:d4

不出所料蚌父!
進(jìn)而毛萌,問題又變成如何獲取設(shè)備的Wifi的interface name朝聋?

探尋 Wifi interface name

回到開頭冀痕,我們是通過context.getSystemService(Context.WIFI_SERVICE) 獲取的WifiManager。

而WifiManager肯定是與遠(yuǎn)程系統(tǒng)服務(wù)的IBinder在交互言蛇,而系統(tǒng)服務(wù)都是在SystemServer.run()中被啟動的。

在SystemServer.java中搜索關(guān)鍵字”WIFI_SERVICE”吨拗,很容易便找到mSystemServiceManager.startService(WIFI_SERVICE_CLASS);

順藤摸瓜劝篷,又找到系統(tǒng)服務(wù)實(shí)現(xiàn)類com.android.server.wifi.WifiService娇妓,WifiService中的邏輯很簡單,構(gòu)造真正的實(shí)現(xiàn)類com.android.server.wifi.WifiServiceImpl對象并注冊到系統(tǒng)服務(wù)中:

WifiService.java

public final class WifiService extends SystemService {
    private static final String TAG = "WifiService";
    final WifiServiceImpl mImpl;
    public WifiService(Context context) {
        super(context);
        mImpl = new WifiServiceImpl(context);
    }
    @Override
    public void onStart() {
        Log.i(TAG, "Registering " + Context.WIFI_SERVICE);
        publishBinderService(Context.WIFI_SERVICE, mImpl);
    }
    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
            mImpl.checkAndStartWifi();
        }
    }
}

打開WifiServiceImpl.java,從構(gòu)造方法處志群,一眼就看到了關(guān)鍵代碼:mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
如此這般終于找到定義設(shè)備的Wifi的interface name的地方:SystemProperties
通過adb可以很容易得到這個屬性值:adb shell getprop wifi.interface
那么在我們應(yīng)用里可以通過Java的反射獲取SystemProperties锌云,進(jìn)而調(diào)用靜態(tài)方法get即可拿到Wifi的interface name。

結(jié)論

縱然Google掩耳盜鈴似的把API返回值篡改了子漩,但終究是沒有改底層的實(shí)現(xiàn)石洗,通過閱讀源碼的方式還是很容易找到解決辦法的讲衫。

通過反射SystemProperties獲取屬性wifi.interface的值
讀取/sys/class/net/+interfaceName+/address文件的第一行即是Wifi MAC地址
請注意:

個別設(shè)備可能會拒絕你訪問/sys目錄
/sys/class/net/+interfaceName+/address文件不一定存在,請?jiān)谧x取之前確認(rèn)設(shè)備的Wifi已經(jīng)打開或已連接
未免廠商實(shí)現(xiàn)不一的風(fēng)險招驴,請先嘗試用NetworkInterface.getByName(interfaceName)來間接獲取Wifi MAC地址

參考:獲取android手機(jī)mac地址

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末别厘,一起剝皮案震驚了整個濱河市触趴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冗懦,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颈畸,死亡現(xiàn)場離奇詭異眯娱,居然都是意外死亡食零,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門娜搂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來百宇,“玉大人秘豹,你說我怎么就攤上這事既绕。” “怎么了誓军?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵昵时,是天一觀的道長壹甥。 經(jīng)常有香客問我,道長句柠,這世上最難降的妖魔是什么溯职? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任缸榄,我火速辦了婚禮甚带,結(jié)果婚禮上鹰贵,老公的妹妹穿的比我還像新娘碉输。我一直安慰自己亭珍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布阻荒。 她就那樣靜靜地躺著侨赡,像睡著了一般羊壹。 火紅的嫁衣襯著肌膚如雪齐婴。 梳的紋絲不亂的頭發(fā)上油猫,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天眨攘,我揣著相機(jī)與錄音鲫售,去河邊找鬼该肴。 笑死匀哄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的阱州。 我是一名探鬼主播苔货,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼夜惭!你這毒婦竟也來了姻灶?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤诈茧,失蹤者是張志新(化名)和其女友劉穎产喉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體敢会,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曾沈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了走触。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晦譬。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖互广,靈堂內(nèi)的尸體忽然破棺而出像樊,到底是詐尸還是另有隱情,我是刑警寧澤涂滴,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響霸琴,放射性物質(zhì)發(fā)生泄漏梧乘。R本人自食惡果不足惜辑莫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一揭蜒、第九天 我趴在偏房一處隱蔽的房頂上張望屉更。 院中可真熱鬧,春花似錦萨脑、人聲如沸瘫俊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至揩瞪,卻和暖如春李破,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工仙粱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人负溪。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓崖堤,卻偏偏與公主長得像,于是被迫代替她去往敵國和親昧廷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 今天在擼代碼時發(fā)現(xiàn)速挑,之前能獲取mac地址的方法在nexus 6上返回了“02:00:00:00:00:00”姥宝,進(jìn)入...
    Viking_Den閱讀 16,035評論 5 11
  • 第九章 無線攻擊 作者:Willie L. Pritchett, David De Smet 譯者:飛龍 協(xié)議:C...
    布客飛龍閱讀 5,040評論 1 99
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器壁公,智...
    卡卡羅2017閱讀 134,659評論 18 139
  • 買醉春光的那抹紅 不負(fù)韶華的紅 低到塵埃里的紅 傲然孤寂的紅 柔情蜜意的紅 分外妖嬈的紅 恬淡從容的紅 嬌嫩欲滴的...
    fengxinzi閱讀 341評論 0 1
  • 我們可以通過命令:git checkout -b mdr_branch 來創(chuàng)建分支并切換到該分支上笔呀。當(dāng)然凿可,使用 g...
    Midorra閱讀 3,550評論 0 1