Android 10 變更了裝置識別碼的權限,以便所有裝置識別碼現在都受到READ_PRIVILEGED_PHONE_STATE
權限的保護。在 Android 10 之前,持久性裝置識別碼(IMEI/MEID、IMSI、SIM 和建置序列)受到READ_PHONE_STATE
執行時間權限的保護。 READ_PRIVILEGED_PHONE_STATE
權限僅授予使用平台金鑰簽署的應用程式和特權系統應用程式。
有關新權限要求的更多資訊可以在TelephonyManager.java和Build.java的 Javadoc 頁面中找到。
此更改影響以下 API:
- TelephonyManager#getDeviceId
- 電話管理器#getImei
- 電話管理器#getMeid
- TelephonyManager#getSimSerialNumber
- TelephonyManager#getSubscriberId
- 構建#getSerial
無需 READ_PRIVILEGED_PHONE_STATE 權限即可存取營運商應用
不符合READ_PRIVILEGED_PHONE_STATE
權限的預先載入營運商應用程式可以實現下表中的選項之一。
選項 | 描述 | 限制 |
---|---|---|
UICC 運營商特權 | Android 平台載入儲存在 UICC 上的證書,並授予由這些證書簽署的應用程式呼叫特殊方法的權限。 | 傳統業者擁有大量已建立的 SIM 卡,且不易更新。此外,沒有新 SIM 卡創作權的業者(例如,擁有 MNO 頒發的 SIM 卡的 MVNO)無法在 SIM 卡上新增或更新憑證。 |
OEM 許可名單 | OEM 可以使用OP_READ_DEVICE_IDENTIFIER 向列入白名單的業者應用程式提供設備識別碼。 | 該解決方案並非適用於所有營運商。 |
類型分配代碼 (TAC) | 使用 Android 10 中引入的getTypeAllocationCode 方法公開傳回製造商和型號資訊的 TAC。 | TAC 中的資訊不足以識別特定設備。 |
MSISDN | 業者可以使用電話號碼 (MSISDN)(在具有PHONE 權限群組的TelephonyManager 下可用)在其後端系統上尋找 IMEI。 | 這需要營運商進行大量投資。使用IMSI映射網路金鑰的營運商需要大量的技術資源才能切換到MSISDN 。 |
所有營運商應用程式都可以透過使用營運商應用的簽章憑證雜湊更新CarrierConfig.xml
檔案來存取裝置識別碼。當營運商應用呼叫方法來讀取特權資訊時,平台會在CarrierConfig.xml
檔案中尋找應用程式的簽章憑證雜湊(憑證的 SHA-1 或 SHA-256 簽章)的匹配項。如果找到匹配項,則傳回所要求的資訊。如果未找到匹配項,則傳回安全性異常。
要實施此解決方案,運營商必須遵循以下步驟:
- 使用運營商應用程式的簽名證書哈希更新
CarrierConfig.xml
並提交補丁。 - 請 OEM 使用 QPR1+(建議)或這些必要的平台修補程式以及包含上述步驟 1 中更新的
CarrierConfig.xml
檔案的補丁來更新其版本。
執行
更新您的特權權限允許列表,以向那些需要存取裝置識別碼的特權應用程式授予READ_PRIVILEGED_PHONE_STATE
權限。
要了解有關白名單的更多信息,請參閱特權權限白名單。
要呼叫受影響的 API,應用程式必須滿足以下要求之一:
- 如果應用程式是預先載入的特權應用程序,則需要在 AndroidManifest.xml 中聲明
READ_PRIVILEGED_PHONE_STATE
權限。該應用程式還需要將此特權權限列入白名單。 - 透過 Google Play 交付的應用程式需要運營商權限。在UICC 運營商特權頁面上了解有關授予運營商特權的更多資訊。
- 已被授予
READ_PHONE_STATE
權限的裝置或設定檔擁有者應用。
不滿足任何這些要求的應用程式會出現以下行為:
- 如果應用程式面向 pre-Q 且沒有授予
READ_PHONE_STATE
權限,則會觸發SecurityException
。這是目前的 pre-Q 行為,因為呼叫這些 API 需要此權限。 - 如果應用程式面向 pre-Q 並且確實已授予
READ_PHONE_STATE
權限,則它會收到所有 TelephonyManager API 的 null 值和Build#getSerial
方法的Build.UNKNOWN
值。 - 如果應用程式面向 Android 10 或更高版本,且不符合任何一項新要求,則會收到 SecurityException。
驗證和測試
相容性測試套件 (CTS)包括用於驗證具有運營商權限的應用程式、裝置和設定檔擁有者以及預計無權存取裝置識別碼的應用程式的預期裝置識別碼存取行為的測試。
以下 CTS 測試專門針對此功能。
cts-tradefed run cts -m CtsCarrierApiTestCases -t android.carrierapi.cts.CarrierApiTest
cts-tradefed run cts -m CtsTelephonyTestCases -t android.telephony.cts.TelephonyManagerTest
cts-tradefed run cts -m CtsTelephony3TestCases
cts-tradefed run cts -m CtsPermissionTestCases -t android.permission.cts.TelephonyManagerPermissionTest
cts-tradefed run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testDeviceOwnerCanGetDeviceIdentifiers
cts-tradefed run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.ManagedProfileTest#testProfileOwnerCanGetDeviceIdentifiers
cts-tradefed run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.ManagedProfileTest#testProfileOwnerCannotGetDeviceIdentifiersWithoutPermission
cts-tradefed run cts -m CtsDevicePolicyManagerTestCases -t com.android.cts.devicepolicy.DeviceOwnerTest#testDeviceOwnerCannotGetDeviceIdentifiersWithoutPermission
常見問題解答
對於給定(MCC、MNC), CarrierConfig.xml
中可以將多少個應用程式列入許可名單?
數組中包含的證書哈希的數量沒有限制。
我需要使用CarrierConfig.xml
中的哪些 CarrierConfig 參數才能將應用程式列入白名單?
使用您正在設定的 AOSP 選項的特定CarrierConfig.xml
中的以下頂級配置項目:
<string-array name="carrier_certificate_string_array" num="2"> <item value="BF02262E5EF59FDD53E57059082F1A7914F284B"/> <item value="9F3868A3E1DD19A5311D511A60CF94D975A344B"/> </string-array>
有沒有我可以使用的基本 CarrierConfig 模板?
使用以下模板。這應該添加到相關資產中。
<?xml version="1.0" encoding="utf-8" standalone="yes"?> <carrier_config> <string-array name="carrier_certificate_string_array" num="1"> <item value="CERTIFICATE_HASH_HERE"/> </string-array> </carrier_config>
設備中是否必須有運營商 SIM 卡才能存取設備識別碼?
使用的CarrierConfig.xml
根據目前插入的 SIM 決定。這意味著,如果運營商 X 的應用程式在插入運營商 Y 的 SIM 卡時嘗試獲取存取權限,則設備將找不到哈希匹配項並傳回安全性異常。
在多 SIM 卡裝置上,電信商 #1 僅具有 SIM #1 的存取權限,反之亦然。
運營商如何將應用程式的簽名證書轉換為哈希值?
若要在將簽章憑證新增至CarrierConfig.xml
之前將其轉換為雜湊值,請執行下列操作:
- 使用
toByteArray
將簽章憑證的簽章轉換為位元組數組。 - 使用
MessageDigest
將位元組數組轉換為 byte[] 類型的雜湊。 將雜湊值從 byte[] 轉換為十六進位字串格式。有關範例,請參閱
IccUtils.java
。List<String> certHashes = new ArrayList<>(); PackageInfo pInfo; // Carrier app PackageInfo MessageDigest md = MessageDigest.getInstance("SHA-256"); for (Signature signature : pInfo.signatures) { certHashes.add(bytesToHexString(md.digest(signature.toByteArray())); }
如果
certHashes
是大小為2
且值為12345
和54321
的數組,請將下列內容新增至運營商設定檔。<string-array name="carrier_certificate_string_array" num="2"> <item value="12345"/> <item value="54321"/> </string-array>