古詩詞大全網 - 藝術簽名 - 漫談唯壹設備ID

漫談唯壹設備ID

設備ID,簡單來說就是壹串符號(或者數字),映射現實中硬件設備。

如果這些符號和設備是壹壹對應的,可稱之為“唯壹設備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