天天看點

Android裝置唯一碼的擷取Android裝置唯一碼的擷取

https://www.cnblogs.com/maohai/p/6454013.html

Android裝置唯一碼的擷取

UTDID是集團無線裝置統一ID方案,目的是給每一台裝置一個ID,作為唯一辨別。UTDID由用戶端生成,并在裝置中各個用戶端之間共享。UTDID的生成中包含時間戳和随機數等,是以重新生成的UTDID值一定是會改變的,UTDID的穩定性強依賴于手機存儲,UTDID方案是一個重在持久化存儲的方案。

1.老UTDID方案面臨的問題

1.1 權限問題

utdid開發手冊中是強制需要下面3個權限的:

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

但是随着Android6.0的釋出和targetSDK使用23編譯,很多權限問題都暴露了出來:

1.WRITE_SETTINGS

targetSDK使用23以後,即使申請了WRITE_SETTINGS權限,再想寫settings中的資料會抛出了如下異常。 

IllegalArgumentException: You cannot keep your settings in the secure settings. 

想寫Setting.System中的資料也已經沒有權限了,即使想修改Settings.Global也需要是系統應用。

2.READ_PHONE_STATE

targetSDK使用23以後,需要手動授權。imei作為utdid生成字段的其中一部分,在生成utdid時,如果無法擷取就使用随機值代替。這個即使不授權問題也不大。

3.WRITE_EXTERNAL_STORAGE

targetSDK使用23以後,需要手動授權。在settings中沒有寫入utdid的情況下,如果沒有WRITE_EXTERNAL_STORAGE權限,應用外部的utdid是無法獲得的,内部也沒有的情況下,那麼utdid勢必會重新生成了。

1.2 内、外存儲可靠性

如果settings不能儲存,那麼應用外就需要寄希望于sdcard的存儲。除了權限問題會導緻sdcard中的資料無法取得外,三方的手機管理工具也會對sdcard中的資料做清除。(utdid外存儲目錄用AAA,BBB表示)

以360手機衛士為例:

360深度空間清理時,在可安全清理項->廣告垃圾->将AAA和BBB識别為淘寶應用緩存和阿裡網頁廣告,導緻被删除掉。AAA和BBB檔案夾下的是以檔案和檔案夾被會被删除。

個人感覺這些檔案的識别是依賴360某些配置的下發,因為在斷網并清除360緩存的情況下,是識别不到AAA和BBB的。(360安裝封包件被混淆,網絡資料被加密,很難知道真正的實作。)

應用内的存儲可靠性就更不用說了,解除安裝應用或清除應用緩存就直接沒有了。

2.Android中一些其它的唯一辨別

Android系統中并沒有可靠擷取所有廠商裝置唯一ID的方法,各個方法都有自己的使用範圍和局限性,這也是目前流行的Android系統版本過多,裝置也是來自不同廠商,且沒有統一标準等原因造成的。

2.1 通過Android SDk擷取辨別

DEVICE_ID

假設我們确實需要用到真實裝置的辨別,可能就需要用到DEVICE_ID。

在以前,我們的Android裝置是手機,這個DEVICE_ID可以同通過getSystemService(Context.TELEPHONY_SERVICE).getDeviceId()擷取,它根據不同的手機裝置傳回IMEI,MEID或者ESN碼,但它在使用的過程中會遇到很多問題:

非手機裝置:最開始搭載Android系統都手機裝置,而現在也出現了非手機裝置:如平闆電腦、電子書、電視、音樂播放器等。這些裝置沒有通話的硬體功能,系統中也就沒有TELEPHONY_SERVICE,自然也就無法通過上面的方法獲得DEVICE_ID。當裝置為手機時,傳回裝置的唯一ID。手機制式為 GSM 時,傳回手機的 IMEI 。手機制式為 CDMA 時,傳回手機的 MEID 或 ESN 。非電話裝置或者 Device ID 不可用時,傳回 null .

權限問題:擷取DEVICE_ID需要READ_PHONE_STATE權限,如果隻是為了擷取DEVICE_ID而沒有用到其他的通話功能,申請這個權限一來大才小用,二來部分使用者會懷疑軟體的安全性。 (Android 6.0 以上需要使用者手動賦予該權限)

廠商定制系統中的Bug:少數手機裝置上,由于該實作有漏洞,會傳回垃圾,如:zeros或者asterisks

MAC ADDRESS

wifi mac擷取方法:

WifiManager wifiManager=(WifiManager) getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo=wifiManager.getConnectionInfo();
String mac=wifiInfo.getMacAddress();
           

這種方法比較通用,但是最近在Android 6.0系統上,這個方法失效了,傳回了”02:00:00:00:00:00”的常量。這并不是一個BUG,在google的部落格中找到如下一段話:

Most notably, Local WiFi and Bluetooth MAC addresses are no longer available. The getMacAddress() method of a WifiInfo object and the BluetoothAdapter.getDefaultAdapter().getAddress() method will both return 02::::: from now on.
           

可以考慮使用NetworkInterface.getHardwareAddress。其原理和cat /sys/class/net/wlan0/address是一模一樣的,但是這個是上層API,不需要自己處理底層資料,在Android 6.0上測試通過。

NetworkInterface networkInterface = NetworkInterface.getByName("wlan0");
byte[] mac = networkInterface.getHardwareAddress();
           

問題:

1.如果重新開機手機後,Wifi沒有打開過,是無法擷取其Mac位址的。(可以考慮授予CHANGE_WIFI_STATE權限,開關一次wifi刷一下。)

2.有一些定制系統的目錄并不一樣。 例如三星的目錄為"cat /sys/class/net/eth0/address",是以是否對是以機型都有效有待驗證。(需要适配)

3.網上也有反映mac變更問題,是不是刷mac或者wifi故障導緻,也不确定。

4.并不是所有的裝置都有Wifi硬體,硬體不存在自然也就得不到這一資訊。(這個還好)

5.需要 ACCESS_WIFI_STATE 權限。(這個還好)

裝置序列号(Serial Number, SN)

擷取辦法:

String serialNum = android.os.Build.SERIAL;

裝有SIM卡的裝置擷取辦法:getSystemService(Context.TELEPHONY_SERVIEC).getSimSerialNumber();

注意對CDMA裝置,傳回的是一個空值。

在Android 2.3可以通過android.os.Build.SERIAL擷取,非手機裝置可以通過該接口擷取。

在少數的一些裝置上,會傳回垃圾資料。對于沒有通話功能的裝置,它可能會傳回一個固定的值。

ANDROID_ID

在裝置首次啟動時,系統會随機生成一個64位的數字,并把這個數字以16進制字元串的形式儲存下來,這個16進制的字元串就是ANDROID_ID,當裝置被恢複出廠設定後該值會被重置。可以通過下面的方法擷取:

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

ANDROID_ID可以作為裝置辨別,但需要注意:

它在Android <=2.1 or Android >=2.3的版本是可靠、穩定的,但在2.2的版本并不是100%可靠的

廠商定制系統的Bug:不同的裝置可能會産生相同的ANDROID_ID:9774d56d682e549c。(摩托羅拉好像出現過這個問題)

廠商定制系統的Bug:有些裝置傳回的值為null。

裝置差異:對于CDMA裝置,ANDROID_ID和TelephonyManager.getDeviceId() 傳回相同的值。

并且,如果某個Andorid手機被Root過的話,這個ID也可以被改變。

2.2 通過Linux指令擷取辨別

cpu号

檔案路徑:/proc/cpuinfo

通過Adb shell 檢視:adb shell cat /proc/cpuinfo

但是在Nexus4的Android6.0手機上Serial為0000000000000000

rickydeMacBook-Pro:bin ricky$ adb shell cat /proc/cpuinfo
Processor   : ARMv7 Processor rev 2 (v7l)
processor   : 0
BogoMIPS    : 13.53

processor   : 1
BogoMIPS    : 13.53

processor   : 2
BogoMIPS    : 13.53

processor   : 3
BogoMIPS    : 13.53

Features    : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt 
CPU implementer : 0x51
CPU architecture: 7
CPU variant : 0x0
CPU part    : 0x06f
CPU revision    : 2

Hardware    : QCT APQ8064 MAKO
Revision    : 000b
Serial      : 0000000000000000
           

mac位址

檔案路徑: /sys/class/net/wlan0/address

通過Adb shell 檢視:adb shell cat /sys/class/net/wlan0/address

rickydeMacBook-Pro:bin ricky$ adb shell  cat /sys/class/net/wlan0/address
10:68:3f:49:93:7d
           

3.Android擷取手機制作商,系統版本等

我們有時候會需要擷取目前手機的系統版本來進行判斷,或者需要擷取一些目前手機的硬體資訊。

3.1 android.os.Build類

android.os.Build類中,包括了這樣的一些資訊。我們可以直接調用 而不需要添加任何的權限和方法。

android.os.Build.BOARD:擷取裝置基闆名稱
android.os.Build.BOOTLOADER:擷取裝置引導程式版本号
android.os.Build.BRAND:擷取裝置品牌
android.os.Build.CPU_ABI:擷取裝置指令集名稱(CPU的類型)
android.os.Build.CPU_ABI2:擷取第二個指令集名稱
android.os.Build.DEVICE:擷取裝置驅動名稱
android.os.Build.DISPLAY:擷取裝置顯示的版本包(在系統設定中顯示為版本号)和ID一樣
android.os.Build.FINGERPRINT:裝置的唯一辨別。由裝置的多個資訊拼接合成。
android.os.Build.HARDWARE:裝置硬體名稱,一般和基闆名稱一樣(BOARD)
android.os.Build.HOST:裝置主機位址
android.os.Build.ID:裝置版本号。
android.os.Build.MODEL :擷取手機的型号 裝置名稱。
android.os.Build.MANUFACTURER:擷取裝置制造商
android:os.Build.PRODUCT:整個産品的名稱
android:os.Build.RADIO:無線電固件版本号,通常是不可用的 顯示unknown
android.os.Build.TAGS:裝置标簽。如release-keys 或測試的 test-keys 
android.os.Build.TIME:時間
android.os.Build.TYPE:裝置版本類型  主要為"user" 或"eng".
android.os.Build.USER:裝置使用者名 基本上都為android-build
android.os.Build.VERSION.RELEASE:擷取系統版本字元串。如. 或 或等
android.os.Build.VERSION.CODENAME:裝置目前的系統開發代号,一般使用REL代替
android.os.Build.VERSION.INCREMENTAL:系統源代碼控制值,一個數字或者git hash值
android.os.Build.VERSION.SDK:系統的API級别 一般使用下面大的SDK_INT 來檢視
android.os.Build.VERSION.SDK_INT:系統的API級别 數字表示
           

3.2 build.prop中擷取目前系統屬性

在Android系統中,/system/build.prop中含有大量系統相關的資訊:

rickydeMacBook-Pro:bin ricky$ adb shell cat /system/build.prop

# begin build properties
# autogenerated by buildinfo.sh
ro.build.id=MDB08M
ro.build.display.id=cm_mako-userdebug  MDB08M c28ecd9956 test-keys
ro.build.version.incremental=c28ecd9956
ro.build.version.sdk=23
ro.build.version.preview_sdk=0
ro.build.version.codename=REL
ro.build.version.all_codenames=REL
ro.build.version.release=6.0
ro.build.version.security_patch=2015-11-01
ro.build.version.base_os=
ro.build.date=2015年 月 日 星期一 :: CST
ro.build.date.utc=1448269403
ro.build.type=userdebug
ro.build.user=moonlight
ro.build.host=moonlight-roms
ro.build.tags=test-keys
ro.build.flavor=cm_mako-userdebug
ro.product.brand=google
ro.product.name=occam
ro.product.board=MAKO
... ...
           

如果有root權限,修改/system/build.prop檔案内容,系統相關的資訊就會被改變(總會有那麼些無聊的人)。是以裝置資訊的安全級别不高,但是可以作為參考。此外,可以上傳一些手機root相關資訊做參考(雖然也不是100%有效)。

3.3 關于手機root相關資訊采集

可以提供手機root相關資訊,作為build.prop有效性的一個參考值。

3.3.1 build.prop中的字段描述

在/system/build.prop中的Build.TAGS字段裝置标簽。如release-keys 或測試的 test-keys。

test-keys為root手機或第三方ROM

ro.build.type字段裝置版本類型。如:user或userdebug等。

3.3.2 su檔案是否存在

檢視su檔案是否存在,可以參考下面代碼的檢索路徑:

3.3.3 apk檢查

檢視/system/app/ 下是否存root後常用軟體。Kinguser.apk、Superuser.apk等,如:

private boolean hasSuperuserApk() {
    return new File("/system/app/Superuser.apk").exists();
}
           

3.3.4 執行su指令

執行su指令,推薦使用new ProcessBuilder().command("su").start() 代替Runtime.getRuntime().exec()實作。Runtime.getRuntime().exec()的執行會有些bug,可以參考(Determine if running on a rooted device),記得要Process.destroy()。執行su指令,會喚起root授權對話框,在資料上報的場景是不建議使用,體驗非常不好。

4.總結

裝置唯一辨別碼還是以utdid做辨別,但是在Android6.0+系統上,外存儲權限越來越難擷取和越來越不可靠的情況下,除考慮加入LocalSocket和Broadcast等機制做多應用間的utdid同步(問題也很明顯)外,必須依賴網絡,建構裝置ID庫來提升裝置辨別的可靠性。

是以需要考慮在伺服器上建立utdid與各裝置資料間的對應關系,通過做大規模的适配和資料上報,來解決問題。通過可獲得手機參數做伺服器請求,伺服器的utdid與各裝置資料間的對應關系表來尋找最比對utdid值。

可以考慮的資料關系體系是以Wifi Mac位址、裝置序列号、ANDROID_ID為主要基準,配合android.os.Build中手機基本資訊為參考(用手機root相關資訊采集做修正),DEVICE_ID(用READ_PHONE_STATE權限做修正),常用ip位址等。如果可以的話,還可以參考手機号碼、業務登入賬号等。目前隻是一些初步想法,可行性還有待實際資料驗證,方案還在探索階段。

參考文檔:

擷取Android裝置唯一辨別碼

Android 手機上擷取實體唯一辨別碼

Getting Unique Device ID of an Android Smartphone 

android 利用反射擷取目前系統sdk版本等屬性

Android6.0 動态權限申請步驟以及需要注意的一些坑

分享一些Android裝置root檢測的思路

Android裝置硬體層次跟蹤