最近,因公司產(chǎn)品及客戶(hù)需要,領(lǐng)導(dǎo)讓我研究免存儲(chǔ)設(shè)備ID,以及給出一個(gè)設(shè)備ID最佳的方案(可與存儲(chǔ)相結(jié)合)。在研究過(guò)瀏覽器的fingerprient2js后,頗有心得,并且看網(wǎng)上似乎木有完美的解決方案,于是寫(xiě)了這篇文章,僅供需要的開(kāi)發(fā)者參考。(該算法暫未進(jìn)行驗(yàn)證,這里先給出一個(gè)jar包,后期我會(huì)在SDK中加入調(diào)查接口,分兩個(gè)jar包(測(cè)試版和正式版),希望開(kāi)發(fā)者能支持測(cè)試版,穩(wěn)定后使用正式版。)
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)公司!專(zhuān)注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶(hù)創(chuàng)新互聯(lián)還提供了鐘山免費(fèi)建站歡迎大家使用!
在產(chǎn)品中,首先肯定要盡量避免權(quán)限,其次考慮卸載APP后ID不一致的問(wèn)題,再就是盡量結(jié)合存儲(chǔ),降低卸載或重裝app時(shí),設(shè)備ID改變的概率。最后,設(shè)計(jì)出合理方案,對(duì)造成不利的因素進(jìn)行列舉。
A.android_id:
什么是android_id呢?當(dāng)設(shè)備在第一次啟動(dòng)時(shí),系統(tǒng)會(huì)隨機(jī)產(chǎn)生一個(gè)64位的數(shù)字,然后以16進(jìn)制的形式保存在設(shè)備上,且API提供了獲取這一參數(shù)的方法:
這就是android_id,當(dāng)設(shè)備重新初始化或者刷機(jī)的時(shí)候,會(huì)被重置。
除此以外,android_id還有其他的bug,比如:
1.不同的設(shè)備可能會(huì)產(chǎn)生相同的android_id。
2.有的廠商設(shè)備無(wú)法獲取android_id,會(huì)返回null。
3.對(duì)于CDMA的設(shè)備,ANDROID_ID和TelephonyManager.getDeviceId() 的值相同。
4.不同的android系統(tǒng)版本穩(wěn)定性不同。
B.硬件序列號(hào)(SERIAL)
API給的解釋是:
A hardware serial number, if available.(一個(gè)硬件的序列碼,如果有效的話)
so,雖然我沒(méi)有用幾百臺(tái)手機(jī)測(cè)試,也能知道這個(gè)值有時(shí)候是無(wú)效的,說(shuō)的這么隱晦。
C.指紋
fingerprint:設(shè)備的唯一標(biāo)識(shí)。由設(shè)備的多個(gè)信息拼接合成。
也是在JavaScript才接觸到這個(gè)單詞”fingerprint“,這個(gè)詞也很生動(dòng),意思是能大概的標(biāo)識(shí)一個(gè)設(shè)備,像指紋一樣,但不排除重復(fù)的可能性。
理論上講用這個(gè)屬性是可以標(biāo)識(shí)一個(gè)設(shè)備的,但是其實(shí)并不是,兩臺(tái)一摸一樣的新手機(jī),這個(gè)值相同的可能性是很多的。為了更加進(jìn)一步的精確,后面還會(huì)介紹幾個(gè)屬性,并把幾個(gè)屬性結(jié)合在一起,生成一個(gè)接近100%的UUID。
D.android系統(tǒng)提供了獲取android系統(tǒng)版本號(hào),生產(chǎn)廠商,固件版本推出時(shí)間的API.
E.android系統(tǒng)提供了當(dāng)前android設(shè)備是12或24小時(shí)制顯示時(shí)間的API,
F.android系統(tǒng)提供了當(dāng)前android設(shè)備的修訂版本列表,顯示屏,主板等等參數(shù)。
G.可以允許用戶(hù)根據(jù)需求用自定義字符串去為FP做貢獻(xiàn),比如IP地址等
方案:
在不需要用戶(hù)權(quán)限的前提下,網(wǎng)上最完美的方案是將android_id和硬件序列號(hào),如果其中任意一種失效就使用另外一種。受FingerPrint2js的啟發(fā),我看了Android獲取系統(tǒng)硬件相關(guān)的API,將所有不經(jīng)常變化且能代表一定用戶(hù)群體的屬性都取出來(lái)進(jìn)行MD5運(yùn)算,包含但不限于依據(jù)中所述的信息。準(zhǔn)確性還需進(jìn)一步驗(yàn)證,但理論上要比FingerPrint2js準(zhǔn)確性高,也在網(wǎng)上給出的比較好的方案基礎(chǔ)上進(jìn)一步縮小了FP可能重復(fù)的概率。
1.第一次進(jìn)入APP時(shí),獲取系統(tǒng)相關(guān)配置信息生成FP,存入SP。
2.每次訪問(wèn),先從SP取,沒(méi)有再通過(guò)相關(guān)配置信息生成FP,存入SP。
3.封裝成jar,只給用戶(hù)暴露出獲取ID的接口、傳遞自定義信息構(gòu)建FP的接口以及第一次安裝時(shí)間戳的接口(或設(shè)置標(biāo)簽調(diào)用的接口)
單純對(duì)于FP而言,有兩個(gè)主要問(wèn)題需要解決,一是FP重復(fù)的問(wèn)題,相同配置的新設(shè)備重復(fù)可能性極大,增多給FP貢獻(xiàn)的因素的數(shù)量,可以有效降低重復(fù)率。二是FP改變的問(wèn)題,貢獻(xiàn)FP的生成因素的其中一個(gè)如果改變,F(xiàn)P就會(huì)改變。所以如果FP的貢獻(xiàn)因素?cái)?shù)量過(guò)多,導(dǎo)致FP改變的概率也就變大,所以說(shuō)客戶(hù)要在兩者之間做一個(gè)很好的平衡。
對(duì)比:
為android FP做貢獻(xiàn)的各配置參數(shù):(示例以6.0的華為榮耀8為例)
1.Android_ID:Settings.System.getString(context.getContentResolver(), Settings.System.ANDROID_ID) //低版本穩(wěn)定,高版本不穩(wěn)定 示例:295a4fbf716094ee
2.Build.SERIAL 設(shè)備序列號(hào)(有的設(shè)備無(wú)法獲?。? 示例:WTK7N16923005607
3.Build.FINGERPRINT 設(shè)備指紋(同樣的新設(shè)備該值應(yīng)該是一樣的) 示例:honor/FRD-AL00/HWFRD:6.0/HUAWEIFRD-AL00/C00B171:user/release-keys
4.Build.TIME 固件推出日期 示例:1477442228000
5.Build.VERSION.INCREMENTAL 源碼控制版本號(hào) 示例: C00B171
6.Build.getRadioVersion() 獲取無(wú)線電固件版本 示例:21.210.03.00.031,21.210.03.00.031
7.Build.HARDWARE 硬件名稱(chēng) 示例:hi3650
8.Build.VERSION.SECURITY_PATCH 用戶(hù)可見(jiàn)安全補(bǔ)丁level(這里我得到的是日期,可能是補(bǔ)丁修復(fù)的時(shí)間)示例:2016-10-01
9.當(dāng)前設(shè)備是12/24時(shí)制:Settings.System.getString(context.getContentResolver(), Settings.System.TIME_12_24) 示例:null(有的手機(jī)可以獲取)
10.Build.VERSION.SDK_INT SDK版本號(hào) (一般講是與系統(tǒng)版本號(hào)一一對(duì)應(yīng)的) 示例:23
11.Build.SUPPORTED_32_BIT_ABIS 支持32位ABIs的列表(數(shù)值)示例:[armeabi-v7a,armeabi]
12.Build.SUPPORTED_64_BIT_ABIS 支持64位ABIs的列表(數(shù)值)示例:[arm64-v8a]
13.Build.BOOTLOADER 系統(tǒng)啟動(dòng)程序版本號(hào) 示例:unknown
14.Build.VERSION.RELEASE 用戶(hù)可見(jiàn)版本 示例: 6.0
16.Build.BOARD 主板 示例:FRD-AL00
17.Build.BRAND 系統(tǒng)定制商 示例:honor
21.Build.HOST 示例:huawei-RH2288H-V2-12L
23.Build.MANUFACTURER 產(chǎn)品/硬件的制造商 示例:HUAWEI
25.Build.PRODUCT 產(chǎn)品的名稱(chēng) 示例:FRD-AL00
26.Build.TAGS 描述Build的標(biāo)簽(Comma-separated tags describing the build, like "unsigned,debug".) 示例:release-keys
28.Build.USER 描述Build的USER 示例:jslave
31.Build.VERSION.BASE_OS 基帶版本 The base OS build the product is based on. 示例:空值
32.自定義字符串或自定義數(shù)組
<strong>黑閾權(quán)限說(shuō)明</strong>
1.INTERNET 擁有完全的網(wǎng)絡(luò)訪問(wèn)權(quán)限。黑閾需要本地網(wǎng)絡(luò)權(quán)限訪問(wèn)“黑閾服務(wù)”。
2.ACCESS_NETWORK_STATE 查看網(wǎng)絡(luò)連接,檢查網(wǎng)絡(luò)狀態(tài),必要時(shí)提供“更新網(wǎng)絡(luò)檢測(cè)”去除網(wǎng)絡(luò)嘆號(hào)。
3.RECEIVE_BOOT_COMPLETED 開(kāi)機(jī)啟動(dòng),開(kāi)機(jī)以后提示“黑閾沒(méi)有啟動(dòng)”,有 Root 時(shí)直接啟動(dòng)。
4. com.android.vending.BILLING Google Play 結(jié)算服務(wù)提供 Google Play 支付。
5.FOREGROUND_SERVICE 運(yùn)行前臺(tái)服務(wù)Android O 起,開(kāi)機(jī)自啟需要使用“前臺(tái)服務(wù)”;Android P 起,需要申請(qǐng)?jiān)摍?quán)限。
6.com.android.launcher.permission.INSTALL_SHORTCUT創(chuàng)建桌面快捷方式,用于臨時(shí)啟用,GCM 應(yīng)用等。
能,但是需要說(shuō)明一下:
1. DEVICE_ID
假設(shè)我們確實(shí)需要用到真實(shí)設(shè)備的標(biāo)識(shí),可能就需要用到DEVICE_ID。在以前,我們的Android設(shè)備是手機(jī),這個(gè)DEVICE_ID可以同通過(guò)TelephonyManager.getDeviceId()獲取,它根據(jù)不同的手機(jī)設(shè)備返回IMEI,MEID或者ESN碼,但它在使用的過(guò)程中會(huì)遇到很多問(wèn)題:
非手機(jī)設(shè)備: 如果只帶有Wifi的設(shè)備或者音樂(lè)播放器沒(méi)有通話的硬件功能的話就沒(méi)有這個(gè)DEVICE_ID
權(quán)限: 獲取DEVICE_ID需要READ_PHONE_STATE權(quán)限,但如果我們只為了獲取它,沒(méi)有用到其他的通話功能,那這個(gè)權(quán)限有點(diǎn)大才小用
bug:在少數(shù)的一些手機(jī)設(shè)備上,該實(shí)現(xiàn)有漏洞,會(huì)返回垃圾,如:zeros或者asterisks的產(chǎn)品
2. MAC ADDRESS
我們也可以通過(guò)手機(jī)的Wifi或者藍(lán)牙設(shè)備獲取MAC ADDRESS作為DEVICE ID,但是并不建議這么做,因?yàn)椴⒉皇撬械脑O(shè)備都有Wifi,并且,如果Wifi沒(méi)有打開(kāi),那硬件設(shè)備無(wú)法返回MAC ADDRESS.
3. Serial Number
在Android 2.3可以通過(guò)android.os.Build.SERIAL獲取,非手機(jī)設(shè)備可以通過(guò)該接口獲取。
4. ANDROID_ID
ANDROID_ID是設(shè)備第一次啟動(dòng)時(shí)產(chǎn)生和存儲(chǔ)的64bit的一個(gè)數(shù),當(dāng)設(shè)備被wipe后該數(shù)重置
ANDROID_ID似乎是獲取Device ID的一個(gè)好選擇,但它也有缺陷:
它在Android =2.1 or Android =2.3的版本是可靠、穩(wěn)定的,但在2.2的版本并不是100%可靠的
在主流廠商生產(chǎn)的設(shè)備上,有一個(gè)很經(jīng)常的bug,就是每個(gè)設(shè)備都會(huì)產(chǎn)生相同的ANDROID_ID:9774d56d682e549c
5. Installtion ID : UUID
以上四種方式都有或多或少存在的一定的局限性或者bug,在這里,有另外一種方式解決,就是使用UUID,該方法無(wú)需訪問(wèn)設(shè)備的資源,也跟設(shè)備類(lèi)型無(wú)關(guān)。
這種方式是通過(guò)在程序安裝后第一次運(yùn)行后生成一個(gè)ID實(shí)現(xiàn)的,但該方式跟設(shè)備唯一標(biāo)識(shí)不一樣,它會(huì)因?yàn)椴煌膽?yīng)用程序而產(chǎn)生不同的ID,而不是設(shè)備唯一ID。因此經(jīng)常用來(lái)標(biāo)識(shí)在某個(gè)應(yīng)用中的唯一ID(即Installtion ID),或者跟蹤應(yīng)用的安裝數(shù)量。很幸運(yùn)的,Google Developer Blog提供了這樣的一個(gè)框架:
1、打開(kāi)撥號(hào)界面。
2、點(diǎn)擊電話,啟動(dòng)撥號(hào)界面,然后輸入“*#*#8255#*#*”即可進(jìn)入GTalkServiceMonitor界面了。
擴(kuò)展資料:
AndroidID用于唯一識(shí)別一部設(shè)備的一次刷機(jī)行為,雖然不能完全確定該設(shè)備的唯一性(真的唯一性是用IMEI號(hào)的),但是可以很大程度上過(guò)濾重復(fù)設(shè)備。
這是移動(dòng)互聯(lián)網(wǎng)廣告行業(yè),尤其是CPI廣告的基礎(chǔ)。設(shè)備ID非常重要。CPI廣告是根據(jù)實(shí)際安裝數(shù)量計(jì)費(fèi)的,廣告商可以使用androidid來(lái)排除重復(fù)安裝。傳遞一個(gè)AndroidID直接影響到某些廣告是否會(huì)被放置在某些位置。
在RTB行業(yè)中,有很多提供數(shù)據(jù)的第三方公司,他們根據(jù)AndroidID買(mǎi)賣(mài)數(shù)據(jù),將各種數(shù)據(jù)對(duì)應(yīng)給用戶(hù)。
(1)DEVICE_ID
Android系統(tǒng)為開(kāi)發(fā)者提供的用于標(biāo)識(shí)手機(jī)設(shè)備的串號(hào)
TelephoneManager tm=TelephoneManager.getSystemService(Context.TELEPHONE_SERVICE);
tm.getDeviceId();
缺陷:
(1)非手機(jī)設(shè)備
(2)權(quán)限問(wèn)題
(2)ANDROID_ID
在設(shè)備首次啟動(dòng)時(shí),系統(tǒng)會(huì)隨機(jī)生成一個(gè)64位的數(shù)字,并把這個(gè)數(shù)字以16進(jìn)制字符串的形式保存下來(lái),這個(gè)16進(jìn)制的字符串就是ANDROID_ID
String ANDROID_ID=Setting.System.getString(getContentResolver(),Setting.System.ANDROID_ID);
缺陷:
(1)廠商Bug:不同的設(shè)備可能會(huì)產(chǎn)生相同的ANDROID_ID
(2)廠商Bug:有些設(shè)備返回的值為null
(3)Serial Number
Android系統(tǒng)2.3版本以上可以通過(guò)下面的方法得到Serial Number,且非手機(jī)設(shè)備也可以通過(guò)該接口獲取。
String serial=Andorid.os.Build.SERIAL;
Android設(shè)備不同類(lèi)型的識(shí)別設(shè)備ID。
· 唯一編號(hào)(IMEI,MEID,ESN,IMSI)
· MAC地址
· 序列號(hào)
· ANDROID_ID
唯一編號(hào)(IMEI,MEID,ESN,IMSI)
說(shuō)明在以前,當(dāng)Android設(shè)備均作為電話使用時(shí),尋找唯一標(biāo)識(shí)號(hào)比較簡(jiǎn)單:()可用于找到(取決于網(wǎng)絡(luò)技術(shù))手機(jī)硬件唯一的IMEI,MEID,ESN和IMSI編號(hào)。
TelephonyManager.getDeviceId
IMEI,MEID,ESN,IMSI的定義如下:
?IMEI(國(guó)際移動(dòng)設(shè)備識(shí)別碼)唯一編號(hào),用于識(shí)別 GSM,WCDMA手機(jī)以及一些衛(wèi)星電話(移動(dòng)設(shè)備識(shí)別碼)全球唯一編號(hào),用于識(shí)別CDMA移動(dòng)電臺(tái)設(shè)備的物理硬件,MEID出現(xiàn)的目的是取代ESN號(hào)段(電子序列號(hào))(電子序列號(hào))唯一編號(hào),用于識(shí)別CDMA手機(jī)(國(guó)際移動(dòng)用戶(hù)識(shí)別碼)與所有GSM和UMTS網(wǎng)絡(luò)手機(jī)用戶(hù)相關(guān)聯(lián)的唯一識(shí)別編號(hào)如需要檢索設(shè)備的ID,在項(xiàng)目中要使用以下代碼:
?MEID
?ESN
?IMSI
import android.telephony.TelephonyManager;
import android.content.Context;
String imeistring = null;
String imsistring = null;
{
TelephonyManager telephonyManager;
telephonyManager =
( TelephonyManager )getSystemService( Context.TELEPHONY_SERVICE );
/*
* getDeviceId() function Returns the unique device ID.
* for example,the IMEI for GSM and the MEID or ESN for CDMA phones.
*/
imeistring = telephonyManager.getDeviceId();
/*
* getSubscriberId() function Returns the unique subscriber ID,
* for example, the IMSI for a GSM phone.
*/
imsistring = telephonyManager.getSubscriberId();
}