デバイス ID

Android 10 でデバイス ID の権限が変更され、すべてのデバイス ID が READ_PRIVILEGED_PHONE_STATE 権限によって保護されるようになりました。Android 10 より前は、永続的なデバイス ID(IMEI / MEID、IMSI、SIM、ビルドシリアル)が READ_PHONE_STATE ランタイム権限で保護されていました。READ_PRIVILEGED_PHONE_STATE 権限は、プラットフォーム キーで署名されたアプリと特権システムアプリにのみ付与されます。

新しい権限の要件について詳しくは、TelephonyManager.javaBuild.java の Javadoc ページをご覧ください。

この変更は、次の API に影響します。

  • TelephonyManager#getDeviceId
  • TelephonyManager#getImei
  • TelephonyManager#getMeid
  • TelephonyManager#getSimSerialNumber
  • TelephonyManager#getSubscriberId
  • Build#getSerial

READ_PRIVILEGED_PHONE_STATE 権限のない携帯通信会社アプリのアクセス

READ_PRIVILEGED_PHONE_STATE 権限の資格のないプリロード済み携帯通信会社アプリでは、下記の表に示すオプションのいずれかを実装できます。

オプション 説明 制限
UICC 携帯通信会社権限 Android プラットフォームは、UICC に格納されている証明書を読み込み、それらの証明書で署名されているアプリに対して、特別なメソッドを呼び出す権限を付与します。 従来からの携帯通信会社の場合、設定済みの SIM の数が多く、更新は簡単ではありません。また、新しい SIM を作成する権限を持たない携帯通信会社(たとえば、MNO が発行する SIM を使用する MVNO)の場合、SIM に対して証明書を追加、更新できません。
OEM 許可リスト登録 OEM は許可リストに登録されている携帯通信会社のアプリに対し、OP_READ_DEVICE_IDENTIFIER を使用してデバイス ID を提供できます。 このソリューションを拡張できない携帯通信会社もあります。
タイプ割り当てコード(TAC) Android 10 で導入された getTypeAllocationCode メソッドを使用して、メーカーと機種に関する情報を返す TAC を公開します。 TAC 内の情報は、個々のデバイスを識別するには不十分です。
MSISDN 携帯通信会社は、PHONE 権限グループが設定された TelephonyManager を通じて電話番号(MSISDN)を使用することで、バックエンド システム上で IMEI を検索できます。 この場合、携帯通信会社には多大な投資が必要です。IMSI を使用してネットワーク キーをマッピングしている携帯通信会社が MSISDN に切り替える場合、大規模な技術リソースが必要です。

すべての携帯通信会社アプリは、そのアプリの署名証明書ハッシュを使用して CarrierConfig.xml ファイルを更新すれば、デバイス ID にアクセスできます。携帯通信会社アプリが特権情報を読み込むメソッドを呼び出すと、プラットフォームがそのアプリの署名証明書ハッシュ(証明書の SHA-1 または SHA-256 署名)に一致するものを CarrierConfig.xml ファイル内で検索します。一致するものが見つかれば、リクエストされた情報が返されます。一致するものが見つからない場合、セキュリティ例外が返されます。

このソリューションを実装するには、携帯通信会社は以下の手順を行う必要があります。

  1. 携帯通信会社アプリの署名証明書ハッシュを使用して CarrierConfig.xml を更新し、パッチを提出します。
  2. QPR1 以上(推奨)、または必要なプラットフォーム パッチと更新済みの CarrierConfig.xml ファイル(上記のステップ 1 を参照)を含むパッチを使用してビルドを更新するよう、OEM にリクエストします。

実装

特権の許可リストを更新して、デバイス ID にアクセスする必要がある特権アプリに READ_PRIVILEGED_PHONE_STATE 権限を付与します。

許可リストへの登録の詳細については、特権の許可リストへの登録をご覧ください。

上記の API を呼び出すアプリは、次のいずれかの要件を満たしている必要があります。

  • プリロードされた特権アプリの場合、AndroidManifest.xml で READ_PRIVILEGED_PHONE_STATE 権限が宣言されている必要があります。また、許可リストにこの特権を登録する必要もあります。
  • Google Play で配信されるアプリには携帯通信会社の権限が必要です。 携帯通信会社の権限の付与については、UICC の携帯通信会社の権限をご覧ください。
  • デバイス オーナー アプリまたはプロファイル オーナー アプリの場合、READ_PHONE_STATE 権限が付与されている必要があります。

これらの要件のいずれも満たしていないアプリは、次のように動作します。

  • Q より前のバージョンをターゲットとするアプリに READ_PHONE_STATE 権限が付与されていない場合は、SecurityException がトリガーされます。現時点では、この権限がなければ上記の API を呼び出すことができません。
  • Q より前をターゲットとし、READ_PHONE_STATE 権限が付与されているアプリで上記の TelephonyManager API を呼び出すと null 値を受け取り、Build#getSerial メソッドを呼び出すと Build.UNKNOWN を受け取ります。
  • Android 10 以降をターゲットとするアプリが新しい要件をいずれも満たしていない場合は、SecurityException を受け取ります。

検証とテスト

互換性テストスイート(CTS)には、携帯通信会社の権限を持つアプリ、デバイス オーナー アプリ、プロファイル オーナー アプリ、デバイス ID にアクセスできないアプリを対象に、想定されるデバイス ID へのアクセス動作を検証するためのテストが用意されています。

次の 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

よくある質問

1 つの(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>

デバイス ID にアクセスする際、携帯通信会社の SIM をデバイスに挿入する必要がありますか?

使用される CarrierConfig.xml は、現在挿入されている SIM に基づいて決定されます。つまり、携帯通信会社 Y の SIM が挿入されているときに、携帯通信会社 X のアプリがアクセス権限を取得しようとすると、デバイスはハッシュに一致するものが見つからず、セキュリティ例外を返します。

マルチ SIM デバイスでは、携帯通信会社 1 には SIM 1 のみへのアクセス権限があります(その逆も同様)。

携帯通信会社がアプリの署名証明書をハッシュに変換するにはどうすればよいですか?

署名証明書をハッシュに変換してから CarrierConfig.xml に追加するには、次の手順を行います。

  1. toByteArray を使用して、署名証明書の署名をバイト配列に変換します。
  2. MessageDigest を使用して、バイト配列を byte[] 型のハッシュに変換します。
  3. ハッシュを byte[] から 16 進文字列形式に変換します。例として、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()));
    }
    
  4. certHashes が、サイズが 2 で値が 1234554321 の配列である場合は、キャリア構成ファイルに以下を追加します。

    <string-array name="carrier_certificate_string_array" num="2">
        <item value="12345"/>
        <item value="54321"/>
    </string-array>