如果這些符號和設備是壹壹對應的,可稱之為“唯壹設備ID(Unique Device Identifier)”
不幸的是,對於Android平臺而言,沒有穩定的API可以讓開發者獲取到這樣的設備ID。
開發者通常會遇到這樣的困境:
隨著項目的演進, 越來越多的地方需要用到設備ID;
然而隨著Android版本的升級,獲取設備ID卻越來越難了。
加上Android平臺碎片化的問題,獲取設備ID之路,可以說是步履維艱。
關於設備ID的作用,大概可以分為下面幾點:
獲取設備標識的API屈指可數,而且都或多或少有壹些問題。
常規的API有以下這些:
IMEI本該最理想的設備ID,具備唯壹性,恢復出廠設置不會變化(真正的設備相關)。
然而,獲取IMEI需要 READ_PHONE_STATE 權限,估計大家也知道這個權限有多麻煩了。
尤其是Android 6.0以後, 這類權限要動態申請,很多用戶可能會選擇拒絕授權。
我們看到,有的APP不授權這個權限就無法使用, 這可能會降低用戶對APP的好感度。
而且,Android 10.0 將徹底禁止第三方應用獲取設備的IMEI, 即使申請了 READ_PHONE_STATE 權限。
所以,如果是新APP,不建議用IMEI作為設備標識;
如果已經用IMEI作為標識,要趕緊做兼容工作了,尤其是做新設備標識和IMEI的映射。
通過 android.os.Build.SERIAL 獲得,由廠商提供。
如果廠商比較規範的話,設備序列號+Build.MANUFACTURER應該能唯壹標識設備。
但現實是並非所有廠商都按規範來,尤其是早期的設備。
最致命的是,Android 8.0 以上,android.os.Build.SERIAL 總返回 “unknown”;
若要獲取序列號,可調用Build.getSerial() ,但是需要申請 READ_PHONE_STATE 權限。
到了Android 10.0以上,則和IMEI壹樣,也被禁止獲取了。
總體來說,設備序列號有點雞肋:食之無味,棄之可惜。
獲取MAC地址也是越來越困難了,
Android 6.0以後通過 WifiManager 獲取到的mac將是固定的:02:00:00:00:00:00 ,
再後來連讀取 /sys/class/net/wlan0/address 也獲取不到了。
如今只剩下面這種方法可以獲取(沒有開啟wifi也可以獲取到):
再往後說不準這種方法也行不通了,且用且珍惜~
Android ID 是獲取門檻最低的,不需要任何權限,64bit 的取值範圍,唯壹性算是很好的了。
但是不足之處也很明顯:
1、刷機、root、恢復出廠設置等會使得 Android ID 改變;
2、Android 8.0之後,Android ID的規則發生了 變化 :
兩個規則導致的結果就是:
第壹,如果用戶安裝APP設備是8.0以下,後來卸載了,升級到8.0之後又重裝了應用,Android ID不壹樣;
第二,不同簽名的APP,獲取到的Android ID不壹樣。
其中第二點可能對於廣告聯盟之類的有所影響。
筆者之前寫過壹篇文章 《Android設備唯壹標識的獲取和構造》 , 文中提到設備ID的兩個概念:唯壹性和穩定性。
唯壹性:兩臺不同的設備獲取到的設備ID不相同;
穩定性:同壹臺設備在不同的時間, 獲取到設備ID相同。
分析唯壹性,我們可以從ID的分配來入手:
左邊第壹欄是bit數量,第二欄是對應的取值範圍,再後面是元素個數以及對應的沖突概率。
例如,假如有50000個32bit的隨機數,則這些隨機數中,至少有兩個相同數字的概率為25%;
換壹種說法,就是假如有四組數,每組都有50000個隨機數,則大約其中壹組會有重復的數字。
32bit有43億的取值範圍,怎麽50000個隨機數就有這麽高的出現重復的概率?
如果對此感到困惑,可以了解壹下 生日悖論 。
Android ID是長度為16的十六進制字符串,其實就是64bit,我們來分析壹下其重復的概率:
假如APP累計激活量達到50億的APP,則每兩個這樣的APP就大約有壹個會有重復的Android ID。
不過這裏的“重復”不是大量的重復,而是“至少有兩個相同”,也就是,如果設備激活量有50億(很多APP達不到-_-),那麽有可能會有少量的重復的Android ID。
總體而言, Android ID的唯壹性還是不錯的。
JDK的randomUUID,大致可以認為是128bit的隨機數(其中有6bit是固定的),即使到達200億的數量,有重復的概率也僅僅是10的負18次方,微乎其微。
穩定性有兩個層面:
無論是統計需求,還是業務需求,都要求設備ID是唯壹的,穩定的。
如果設備ID有重復,則活躍統計,用戶畫像,定向推送等統統都不準確了;
其中,影響最深是定向推送,送錯快遞還有可能追回,推送錯了就不好說了,如果推送內容又比較重要,後果不堪設想。
如果設備ID不穩定(ID變化),會影響到活躍統計(會認為是新用戶),對用戶畫像也有較大影響(之前的ID關聯的行為數據無法跟蹤了)。
為此,有必要設計壹套方案,提供相對定穩定的,唯壹的設備ID。
首先要明確兩個前提條件:
前面分析的設備ID中,在可用的前提下,出現重復的概率較小;
如果壹定的頻率去觀察,比如說每天,總體而言,觀察到和昨天不壹樣的概率也是較小的。
如何在本來就較小的概率的前提下,繼續降低概率呢?
壹種方案是組合設備ID(直接拼接,或者拼接後計算摘要)。
舉個例子,假如出現重復的概率和發生變化的的概率都是千分之壹,
則對於兩臺不同設備,兩個設備ID同時重復的概率是百萬分之壹,兩個設備ID至少有壹個發生變化約為千分之二。
也就是,拼接ID的效果是大大提高唯壹性,但是壹定程度上降低穩定性(只要其中壹個要素變化,拼接的ID就變了)。
但事實上,如今能拿到的設備ID,最突出的矛盾是不穩定,所以,我們不能為了提高唯壹性而犧牲穩定性。
要提高穩定性,可以引入容錯方案。
容錯方案有很多,比如網絡傳輸,用checksum去校驗報文,如果出錯了則重發;
再如磁盤陣列,數據寫入兩個磁盤,只有當兩個磁盤同時出錯時才會丟失數據,從而大大降低丟失數據的概率。
但是對於設備ID,以上兩種方案都不合適,因為上面的方案需要通過checksum來確認原信息是否被修改,設備ID沒有這樣的條件。
所以,可以引入類似虛擬貨幣用到的"拜占庭容錯"方案。
簡單地說,就是要采集三個設備ID到雲端,如果有兩個(包括兩個以上)的設備ID和之前的記錄相同,則認為是同壹臺設備。
同樣假設出現重復的概率和發生變化的的概率都是千分之壹,則:
同壹臺設備的兩次采集,認不出是同壹臺設備的條件為“至少兩個設備ID都和上次不壹樣”,概率約為百萬分之三。
兩臺不同的設備,認為是同壹臺的條件是為“三個設備ID中,至少有兩個設備ID和另壹臺設備相同”,概率同樣約為百萬分之三。
所以,用此方案,唯壹性和穩定性都能得到提高。
基本思想是:服務端有壹張設備 ID 的表,核心的屬性(Column)有:
id | did_1 | did_2 | did_3
客戶請求時,上傳三個設備 ID,服務端檢索:
如果檢索到記錄,其中至少兩個did和上傳的相同,則返回 id;
否則,插入上傳的三個設備 ID,並將新插入記錄的 id 返回。
通常情況下,服務端表的主鍵為自增序列(為了確保插入的有序性),
所以我們不能直接返回表的主鍵,否則容易被他人推測其他的設備 ID,以及知曉用戶數量。
因此,在主鍵 ID 之外,我們需要另外壹個唯壹 ID。
有兩種思路:
然後就是,需要三個設備ID……
那麽,如果在沒有 READ_PHONE_STATE 權限的情況下,以及Android 10.0之後,如何處理?
首先,設備序列號還是要采集的,畢竟還有部分舊版本的設備可以獲取到,能區分壹點是壹點;
然後,采集壹些設備相關的信息,機型,硬件信息等(相同的機型,可能有多種配置,所以同時也采集壹下硬件信息)。
最終匹配規則如下:
如果沒有匹配的設備,則認為是新設備;
此時,生成新的udid返回,同時插入新設備的相關信息(設備ID,硬件信息)。
關於硬件信息,需滿足壹個要求:在設備重啟、恢復出廠設置等操作之後,不會變化。
常規信息有CPU核心數,RAM/ROM大小(以Gb為單位采集,而不是精確到比特,否則容易變化),屏幕分辨率和dpi等,結合機型,保守估計有上千甚至上萬種可能性,相對Android ID 的 2^64 當然相差很遠了,但是仍可作為輔助的參考信息。
試想在設備序列號獲取不到,Android ID 和 MAC 地址其中壹個發生變化時,檢索到的都是只有Android ID 或者 MAC 其中壹個匹配的記錄,茫茫機海,說不準就有壹兩臺的Android ID 或 MAC是相同的。
這時候選哪壹個呢? 再加上設備信息,或許就區分開了。
常規的設備信息容易遭到篡改,所以,在常規信息之外,我們可以挖掘壹些冷門的設備特征,比如 NetworkInterface 和 傳感器 的相關信息。
當常規信息被篡改時,如果冷門的設備信息還沒變,仍可識別出是同壹臺設備。
至於如何挖掘,那就各顯神通了,通常做手機硬件或者ROM的朋友可能會知道更多的API。
為了方便檢索,我們可以用 MurmurHash 將信息壓縮到64bit(Long的長度)。
再者,在獲取到udid之後,可以定時(比如每隔兩天)就上傳udid和設備信息給雲端,雲端比較壹下存儲的信息和上傳的信息,不相同則更新,這樣可以提高udid的穩定性。
比方說,用戶在設備是Android 7.0 的時候卸載了APP,在Android 8.0之後安裝回來,這時候Android ID 是變化了的,但是憑著MAC和設備信息我們可以認出這臺設備,同時更新其 Android ID;
如果哪壹天輪到MAC獲取不到了,這時候我們仍可以根據 Android ID和設備信息識別出這臺設備。
本文介紹了設備ID的用途,現狀,並分析了現有設備ID的特性,最後提出了壹套設備ID的構造方案。
按照這幾年的趨勢,各種設備ID的API或許還會越收越緊,僅從客戶端去構造可靠的設備ID是比較困難的,而基於信息采集和雲端綜合計算則相對容易。
具體實現,筆者編寫了壹個Demo,已發布的到github,謹供參考。
項目地址:
https://en.wikipedia.org/wiki/Birthday_attack