Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android設備唯一碼的獲取

Android設備唯一碼的獲取

編輯:關於Android編程

UTDID是集團無線設備統一ID方案,目的是給每一台設備一個ID,作為唯一標識。UTDID由客戶端生成,並在設備中各個客戶端之間共享。UTDID的生成中包含時間戳和隨機數等,因此重新生成的UTDID值一定是會改變的,UTDID的穩定性強依賴於手機存儲,UTDID方案是一個重在持久化存儲的方案。

1.老UTDID方案面臨的問題

1.1 權限問題

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

 
 

但是隨著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:00:00:00:00:00 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:獲取系統版本字符串。如4.1.2 或2.2 或2.3等
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 6.0 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年 11月 23日 星期一 17:03:23 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文件是否存在,可以參考下面代碼的檢索路徑:

"/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su","/system/bin/failsafe/su", "/data/local/su", "/su/bin/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地址等。如果可以的話,還可以參考手機號碼、業務登錄賬號等。目前只是一些初步想法,可行性還有待實際數據驗證,方案還在探索階段。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved