從Android Q即Android 10開(kāi)始,第三方已經(jīng)無(wú)法獲取到手機(jī)的唯一設(shè)備了,包括IMEI和序列號(hào)窖认。
故重新梳理一下相關(guān)
Android Q(Android 10變更)
Android 官網(wǎng)關(guān)于隱私與安全這一節(jié)有詳細(xì)介紹
https://developer.android.com/about/versions/10/privacy/changes
"Starting in Android 10, apps must have the READ_PRIVILEGED_PHONE_STATE privileged permission in order to access the device's non-resettable identifiers, which include both IMEI and serial number."
意思從Android 10開(kāi)始劲藐,為了加強(qiáng)Android安全性(個(gè)人隱私相關(guān)),應(yīng)用必須擁有READ_PRIVILEGED_PHONE_STATE隱私才可以訪問(wèn)設(shè)備唯一標(biāo)識(shí)(包括IMEI和序列號(hào))
而READ_PRIVILEGED_PHONE_STATE這個(gè)權(quán)限的定義
<!-- @SystemApi Allows read access to privileged phone state.
@hide Used internally. -->
<permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
android:protectionLevel="signature|privileged" />
為擁有系統(tǒng)簽名的應(yīng)用或者privileged應(yīng)用(apk內(nèi)置至system-priv目錄)才可以訪問(wèn)陨闹,即第三方應(yīng)用無(wú)法訪問(wèn)
影響到的接口(Android 官網(wǎng))
Affected methods include the following:
-
Build
-
TelephonyManager
查看一下TelephonyManager相關(guān)代碼(如getDeviceId),增加了具體的注釋說(shuō)明:
@Deprecated
@SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236).
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public String getDeviceId() {
try {
ITelephony telephony = getITelephony();
if (telephony == null)
return null;
return telephony.getDeviceId(mContext.getOpPackageName());
} catch (RemoteException ex) {
return null;
} catch (NullPointerException ex) {
return null;
}
}
再看一下具體的權(quán)限控制楞捂,最終調(diào)用到 TelephonyPermissions類進(jìn)行檢查,關(guān)鍵代碼在
checkReadDeviceIdentifiers與reportAccessDeniedToReadIdentifiers兩個(gè)接口趋厉,具體可看代碼寨闹,最終符合
以下規(guī)則 :
- If your app targets Android 10 or higher, a
SecurityException
occurs. - If your app targets Android 9 (API level 28) or lower, the method returns
null
or placeholder data if the app has theREAD_PHONE_STATE
permission. Otherwise, aSecurityException
occurs.
Android ID(Settings.Secure.ANDROID_ID 或 SSAID)
Android ID目前是Android系統(tǒng)提供給應(yīng)用容易訪問(wèn)的設(shè)備ID,也叫SSAID(Settings.Secure.ANDROID_ID縮寫(xiě))君账,這個(gè)ID主要與應(yīng)用/設(shè)備相關(guān)
Android ID最大的變化是從Android8.0開(kāi)始:
https://developer.android.com/about/versions/oreo/android-8.0-changes
它有以下特性:
Privacy
Android 8.0 (API level 26) makes the following privacy-related changes to the platform.
- The platform now handles identifiers differently.
For apps that were installed prior to an OTA to a version of Android 8.0 (API level 26) (API level 26), the value of
[ANDROID_ID]
remains the same unless uninstalled and then reinstalled after the OTA. To preserve values across uninstalls after OTA, developers can associate the old and new values by using Key/Value Backup.
系統(tǒng)OTA升級(jí)至Android 8.0版本后繁堡,這里指的是不清除數(shù)據(jù)升級(jí),應(yīng)用APP獲取到的ANDROID_ID不會(huì)變化乡数,卸載后重新安裝會(huì)導(dǎo)致變化(按照8.0新的邏輯生成 椭蹄,規(guī)則看下一條)For apps installed on a device running Android 8.0, the value of
[ANDROID_ID]
is now scoped per app signing key, as well as per user. The value of[ANDROID_ID])
is unique for each combination of app-signing key, user, and device. As a result, apps with different signing keys running on the same device no longer see the same Android ID (even for the same user).
對(duì)于在Android 8.0上面新安裝的應(yīng)用,將會(huì)按照新的規(guī)則生成ANDROID_ID,生成算法因子包括應(yīng)用簽名净赴,系統(tǒng)用戶ID(這個(gè)用戶ID一般為0绳矩,即主用戶,而且不同手機(jī)的主用戶ID是一樣的為0,訪問(wèn)模式或其它模式的用戶ID為10等其它值)玖翅,設(shè)備翼馆。生成邏輯可以看后面的代碼分析The value of
[ANDROID_ID]
does not change on package uninstall or reinstall, as long as the signing key is the same (and the app was not installed prior to an OTA to a version of Android 8.0).
ANDROID_ID的值在應(yīng)用卸載后安裝/重新安裝后不變,因?yàn)榭瓷厦娴诙l生成規(guī)則便清楚金度,簽名不變即不會(huì)變化The value of
[ANDROID_ID]
does not change even if a system update causes the package signing key to change.
系統(tǒng)OTA升級(jí)后應(yīng)用對(duì)應(yīng)的ANDROID_ID的值也不會(huì)變化应媚,甚至某個(gè)應(yīng)用的簽名變化了也不會(huì)變化,這是為啥猜极?因?yàn)榕cANDROID_ID的存儲(chǔ)與獲取邏輯相關(guān)珍特,存儲(chǔ)的時(shí)候是以包名為key,獲取的時(shí)候發(fā)現(xiàn)之前已經(jīng)存在了就返回了。但是如果這個(gè)應(yīng)用卸載重新安裝了就會(huì)變化了-
On devices shipping with Google Play services and Advertising ID, you must use Advertising ID. A simple, standard system to monetize apps, Advertising ID is a unique, user-resettable ID for advertising. It is provided by Google Play services.
最佳實(shí)踐相關(guān)魔吐,官方提供標(biāo)準(zhǔn)的Advertising ID方案Other device manufacturers should continue to provide
[ANDROID_ID]
.
根據(jù)上述的規(guī)則扎筒,來(lái)看一下代碼邏輯是如何實(shí)現(xiàn)的:
public Setting generateSsaidLocked(PackageInfo callingPkg, int userId) {
// Read the user's key from the ssaid table.
Setting userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
if (userKeySetting == null || userKeySetting.isNull()
|| userKeySetting.getValue() == null) {
// Lazy initialize and store the user key.
generateUserKeyLocked(userId);
userKeySetting = getSettingLocked(SETTINGS_TYPE_SSAID, userId, SSAID_USER_KEY);
if (userKeySetting == null || userKeySetting.isNull()
|| userKeySetting.getValue() == null) {
throw new IllegalStateException("User key not accessible");
}
}
final String userKey = userKeySetting.getValue();
// Convert the user's key back to a byte array.
final byte[] keyBytes = ByteStringUtils.fromHexToByteArray(userKey);
// Validate that the key is of expected length.
// Keys are currently 32 bytes, but were once 16 bytes during Android O development.
if (keyBytes == null || (keyBytes.length != 16 && keyBytes.length != 32)) {
throw new IllegalStateException("User key invalid");
}
final Mac m;
try {
m = Mac.getInstance("HmacSHA256");
m.init(new SecretKeySpec(keyBytes, m.getAlgorithm()));
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("HmacSHA256 is not available", e);
} catch (InvalidKeyException e) {
throw new IllegalStateException("Key is corrupted", e);
}
// Mac each of the developer signatures.
for (int i = 0; i < callingPkg.signatures.length; i++) {
byte[] sig = callingPkg.signatures[i].toByteArray();
m.update(getLengthPrefix(sig), 0, 4);
m.update(sig);
}
// Convert result to a string for storage in settings table. Only want first 64 bits.
final String ssaid = ByteStringUtils.toHexString(m.doFinal()).substring(0, 16)
.toLowerCase(Locale.US);
// Save the ssaid in the ssaid table.
final String uid = Integer.toString(callingPkg.applicationInfo.uid);
final SettingsState ssaidSettings = getSettingsLocked(SETTINGS_TYPE_SSAID, userId);
final boolean success = ssaidSettings.insertSettingLocked(uid, ssaid, null, true,
callingPkg.packageName);
if (!success) {
throw new IllegalStateException("Ssaid settings not accessible");
}
return getSettingLocked(SETTINGS_TYPE_SSAID, userId, uid);
}
以上生成ANDROID_ID(SSAID)主要分為三個(gè)步驟:
1.userKey生成
這里說(shuō)一下,userkey生成對(duì)上述說(shuō)到生成規(guī)則的用戶ID和設(shè)備相關(guān)酬姆,用戶ID為0,設(shè)備ID根據(jù)邏輯來(lái)說(shuō)其實(shí)是一個(gè)隨機(jī)數(shù)生成 嗜桌,這個(gè)隨機(jī)數(shù)保證同一個(gè)應(yīng)用在不同的設(shè)備生成的ANDROID_ID是不一樣的
2.Hmac算法根據(jù)userkey,應(yīng)用簽名生成ssid
- 截取16位與存儲(chǔ)
解決辦法?如何獲取唯一標(biāo)識(shí)辞色?
Android官網(wǎng)給出了最佳實(shí)踐骨宠,沒(méi)有仔細(xì)研究(跟google套件相關(guān)或海外更適合?)
https://developer.android.com/training/articles/user-data-ids
國(guó)內(nèi)目前由工信部牽頭實(shí)現(xiàn)了一套移動(dòng)智能終端補(bǔ)充標(biāo)識(shí)體系,大多數(shù)國(guó)內(nèi)手機(jī)廠商已經(jīng)支持
层亿,但是對(duì)于第三方來(lái)說(shuō)桦卒,也無(wú)法獲取唯一ID了,這樣對(duì)于開(kāi)發(fā)者的各類功能/業(yè)務(wù)來(lái)說(shuō)挑戰(zhàn)不小
http://msalliance.icoc.bz/col.jsp?id=120