Android之獲取手機UDID

lvxiangan發表於2018-11-21

最近做的一個專案中需要用到Android裝置唯一碼(UUID)來標識一臺裝置,Android中裝置唯一碼有很多,如:MAC地址、IMEI號(DeviceId)、IMSI號、ANDROID_ID、序列號(SerialNumber)等,但並不是所有裝置上都能穩定獲取到這些值。

最後專案中採用的是MAC地址。

先總結一些搜尋得知的各種值的缺點,再說說最後採用MAC地址的解決方案吧。
 

1.MAC地址:
Mac是裝置網路卡的唯一設別碼,該碼全球唯一,一般稱為實體地址,硬體地址用來定義裝置的位置。

獲取MAC地址的方法有兩種:

(1). 通過Linux命令查詢

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

public String getMacAddress() {

    String macAddress = null;

    String str = "";

    try {

        //linux下查詢網路卡mac地址的命令

        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) {

                macAddress = str.trim();// 去空格

                break;

            }

        }

    } catch (IOException ex) {

        ex.printStackTrace();

    }

    return macAddress;

}

缺點:

在當前沒開啟WiFi的情況下獲取得到的MAC地址值為空,即使在執行這段程式碼前是有開啟過WiFi,而執行這段程式碼時WiFi狀態是關閉的,也不能獲取到MAC地址。

 

(2). 通過Android官方的WifiManager類獲取

1

2

3

4

5

6

7

8

9

10

11

public String getMacAddress() {

 

    String macAddress = null;

         

    WifiManager wifiManager =

        (WifiManager)MyApplication.getContext().getSystemService(Context.WIFI_SERVICE);

    WifiInfo info = (null == wifiManager ? null : wifiManager.getConnectionInfo());

         

    macAddress = info.getMacAddress();

    return macAddress;

}

需要加入許可權

1

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

缺點:

這種方法雖然能在當前Wifi狀態為關閉的情況下獲取到MAC地址,但前提是在手機開機後要開啟過一次Wifi,如果在某次開機後沒開啟過Wifi就呼叫這段程式碼,獲取地址也是為空。

網上給出的解釋是:WiFi的Mac address是一個被動資訊。一般在開機後,不會主動上報到系統裡。要待WiFi硬體啟動後,才會把有關Mac address資料記載入系統去。

在Android6.0以後 google 為了執行時許可權,對geMacAddress()作出修改。通過該方法得到的mac地址永遠是一樣的, 但是可以其他途徑獲取。

/*
        獲取mac地址有一點需要注意的就是android 6.0版本後,以下注釋方法不再適用,
 不管任何手機都會返回"02:00:00:00:00:00"這個預設的mac地址,
 這是googel官方為了加強許可權管理而禁用了getSYstemService(Context.WIFI_SERVICE)方法來獲得mac地址。
         */
        // String macAddress= "";
        // WifiManager wifiManager = (WifiManager) MyApp.getContext().getSystemService(Context.WIFI_SERVICE);
        // WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        // macAddress = wifiInfo.getMacAddress();
        // return macAddress;
        
        String macAddress = null;
        StringBuffer buf = new StringBuffer();
        NetworkInterface networkInterface = null;
        try {
            networkInterface = NetworkInterface.getByName("eth1");
            if (networkInterface == null) {
                networkInterface = NetworkInterface.getByName("wlan0");
            }
            if (networkInterface == null) {
                return "02:00:00:00:00:02";
            }
            byte[] addr = networkInterface.getHardwareAddress();
            for (byte b : addr) {
                buf.append(String.format("%02X:", b));
            }
            if (buf.length() > 0) {
                buf.deleteCharAt(buf.length() - 1);
            }
            macAddress = buf.toString();
        } catch (SocketException e) {
            e.printStackTrace();
            return "02:00:00:00:00:02";
        }
        return macAddress;
    }

 

 

2.IMEI號(DeviceId)、IMSI號:

1

2

3

TelephonyManager mTelephonyMgr = (TelephonyManager) getSystemServic(Context.TELEPHONY_SERVICE);

String imsi = mTelephonyMgr.getSubscriberId();  //獲取IMSI號

String imei = mTelephonyMgr.getDeviceId();  //獲取IMEI號

需要加入許可權

1

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>

缺點:

IMEI號(國際移動裝置身份碼)、IMSI號(國際移動裝置識別碼)這兩個是有電話功能的移動裝置才具有,也就是說某些沒有電話功能的平板是獲取不到IMEI和IMSI號的。且在某些裝置上getDeviceId()會返回垃圾資料。

 

3.ANDROID_ID:

ANDROID_ID 是裝置首次啟動時由系統隨機生成的一串64位的十六進位制數字。

1

String ANDROID_ID = Settings.System.getString(getContentResolver(), Settings.System.ANDROID_ID);

缺點:

①.裝置刷機wipe資料或恢復出廠設定時ANDROID_ID值會被重置。

②.現在網上已有修改裝置ANDROID_ID值的APP應用。

③.某些廠商定製的系統可能會導致不同的裝置產生相同的ANDROID_ID。

④.某些廠商定製的系統可能導致裝置返回ANDROID_ID值為空。

⑤.CDMA裝置,ANDROID_ID和DeviceId返回的值相同

 

4.序列號SerialNumber:

從Android 2.3開始,通過android.os.Build.SERIAL方法可獲取到一個序列號。沒有電話功能的裝置也都需要上給出此唯一的序列號。

1

String SerialNumber = android.os.Build.SERIAL;

缺點:

在某些裝置上此方法會返回垃圾資料。


 

1.5 UniquePsuedoID

具體稱呼不明, 但是從以下的情況看出是一些列硬體資訊拼裝獲取到的內容。

1.5.1 具體的獲取方式
 

public static String getUniquePsuedoID()  
{ 
    String m_szDevIDShort = "35" + (Build.BOARD.length() % 10) + (Build.BRAND.length() % 10) + (Build.CPU_ABI.length() % 10) + (Build.DEVICE.length() % 10) + (Build.MANUFACTURER.length() % 10) + (Build.MODEL.length() % 10) + (Build.PRODUCT.length() % 10);  
  
    // Thanks to @Roman SL!  
    // http://stackoverflow.com/a/4789483/950427  
    // Only devices with API >= 9 have android.os.Build.SERIAL  
    // http://developer.android.com/reference/android/os/Build.html#SERIAL  
    // If a user upgrades software or roots their phone, there will be a duplicate entry  
    String serial = null;  
    try  
    {  
        serial = android.os.Build.class.getField("SERIAL").get(null).toString();  
  
        // Go ahead and return the serial for api => 9  
        return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();  
    }  
    catch (Exception e)  
    {  
        // String needs to be initialized  
        serial = "serial"; // some value  
    }  
  
    // Thanks @Joe!  
    // http://stackoverflow.com/a/2853253/950427  
    // Finally, combine the values we have found by using the UUID class to create a unique identifier  
    return new UUID(m_szDevIDShort.hashCode(), serial.hashCode()).toString();  
}

缺點:

由於是與裝置資訊直接相關,如果是同一批次出廠的的裝置有可能出現生成的內容可能是一樣的。(通過模擬器實驗過,開啟兩個完全一樣的模擬器,生成的內容是完全一下),所以如果單獨使用該方法也是不能用於生成唯一識別符號的。



 

解決方案:

一種比較折衷的辦法,在獲取MAC地址之前先判斷當前WiFi狀態,若開啟了Wifi,則直接獲取MAC地址,若沒開啟Wifi,則用程式碼開啟Wifi,然後馬上關閉,再獲取MAC地址。

目前此方法測試成功,無論在哪種狀態下都能正確取得裝置的MAC地址(包括開機後未啟動過Wifi的狀態下),且在未開啟Wifi的狀態下,用程式碼開啟Wifi並馬上關閉,過程極短,不會影響到使用者操作。

程式碼如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public String getMacAddress() {

 

    String macAddress = null;

    WifiManager wifiManager =

        (WifiManager)MyApplication.getContext().getSystemService(Context.WIFI_SERVICE);

    WifiInfo info = (null == wifiManager ? null : wifiManager.getConnectionInfo());

         

    if (!wifiManager.isWifiEnabled())

    {

        //必須先開啟,才能獲取到MAC地址

        wifiManager.setWifiEnabled(true);

        wifiManager.setWifiEnabled(false);

    }

    if (null != info) {

        macAddress = info.getMacAddress();

    }

    return macAddress;

}

需要加入如下許可權

1

2

3

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"> </uses-permission>

<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"> </uses-permission>

<uses-permission android:name="android.permission.WAKE_LOCK"> </uses-permission>

轉自:http://www.goteny.com/develop/android/201412/452.html




 

相關文章