非 SDK インターフェースの制限

Android 9(API レベル 28)以降、アプリで使用できる非 SDK インターフェースが制限されています。アプリが非 SDK インターフェースを参照したり、リフレクションや JNI を使用してハンドルの取得を試みたりするたびに、この制限が適用されます。この制限が導入されたことで、ユーザーやデベロッパーのエクスペリエンスが改善されるほか、ユーザーはクラッシュのリスクを、デベロッパーは緊急リリースを配布するリスクを低減できます。この決定について詳しくは、非 SDK インターフェースの使用を減らすことによる安定性の向上に関するブログ記事をご覧ください。

SDK インターフェースと非 SDK インターフェースの違い

公開 SDK インターフェースとは一般に、Android フレームワークのパッケージ インデックスに記述されているインターフェースのことです。非 SDK インターフェースの処理は、その API に抽象化されている詳細を実装するものです。そのため、非 SDK インターフェースは予告なく変更されることがあります。

クラッシュや予期しない動作が発生しないようにするには、アプリで SDK のクラスを使う場合に、正式に文書化されている部分のみを使用するようにします。つまり、リフレクションなどのメカニズムを介してクラスを使用する際、SDK に記述されていないメソッドやフィールドにアクセスしてはなりません。

ブラックリスト、グレーリスト、ホワイトリスト

Android の各リリースでは今後、非 SDK インターフェースの追加は制限されます。この制限によって、アプリのリリース ワークフローに影響が及ぶ可能性があります。そのため、Google では、デベロッパー向けに、非 SDK インターフェースの使用を検出するツールを用意して Google にフィードバックを提供できるようにし、計画を立てて新しいポリシーに適応するための時間を提供したいと考えています。

非 SDK インターフェースの制限が開発ワークフローに及ぼす影響を最小限に抑えるため、対象 API レベルに応じて非 SDK インターフェースの使用をどの程度制限するかを定義するリストを設けて、非 SDK インターフェースを分類しています。次の表で、その各リストについて説明します。

リスト 説明
ブラックリスト アプリの対象 API レベルに関係なく使用できない非 SDK インターフェース。アプリがこのインターフェースのいずれかにアクセスしようとすると、システムによってエラーがスローされます。
グレーリスト アプリの対象 API レベルで制限されていなければ使用できる非 SDK インターフェース。

Android 9(API レベル 28)以降、各 API レベルで使用できる非 SDK インターフェースが制限されます。制限付きグレーリストの API へのアクセスは、アプリの対象 API レベルが制限より低い場合は可能ですが、対象 API レベルで制限されている非 SDK インターフェースにアプリがアクセスしようとすると、システムは API がブラックリストに登録されている場合と同じように動作します。

注: Android 9(API レベル 28)では、制限のないグレーリストの非 SDK インターフェースは「ライトグレー リスト」、制限付きグレーリストの非 SDK インターフェースは「ダークグレー リスト」と呼ばれていました。

ホワイトリスト 正式に文書化されている Android フレームワークのパッケージ インデックスの一部としてサポートされている、自由に使用できるインターフェース。

現時点ではグレーリストに記載されている非 SDK インターフェースの一部(アプリの対象 API レベルによって異なる)を利用できますが、そのまま非 SDK のメソッドやフィールドを使用し続けていると、今後アプリが機能しなくなる可能性が高くなります。アプリが非 SDK インターフェースに依存している場合は、SDK インターフェースまたは別の手段への移行を計画してください。現在のアプリの機能に使用している非 SDK インターフェースの代わりが見つからない場合は、新しい公開 API をリクエストしてください。

インターフェースが属しているリストの特定

非 SDK インターフェースのリストはプラットフォームの一部として作成されています。各 Android リリースでの詳細については下記のセクションをご覧ください。

Android 10

Android 10(API レベル 29)の場合、非 SDK インターフェースと対応するリストについては、すべて次のファイルに記載されています(hiddenapi-flags.csv)。

Android 9

Android 9(API レベル 28)の場合、グレーリストに登録されている制限のない API のリストは次のテキスト ファイルに記載されています(hiddenapi-light-greylist.txt)。

ブラックリストと制限付きグレーリストはビルド時に抽出されます。

AOSP からリストを生成する

AOSP を使用する場合は、非 SDK のすべてのインターフェースと対応するリストを含む hiddenapi-flags.csv ファイルを生成できます。生成するには、AOSP ソースをダウンロードして、次のコマンドを実行します。

m out/soong/hiddenapi/hiddenapi-flags.csv

ファイルは次の場所に生成されます。

out/soong/hiddenapi/hiddenapi-flags.csv

制限付きの非 SDK インターフェースにアクセスした場合に想定される動作

次の表に、ブラックリストに登録されている非 SDK インターフェースにアプリがアクセスしようとした場合に想定される動作を示します。

アクセス方法 結果
Dalvik 命令によるフィールドの参照 NoSuchFieldError がスローされる
Dalvik 命令によるメソッドの参照 NoSuchMethodError がスローされる
Class.getDeclaredField() または Class.getField() によるリフレクション NoSuchFieldException がスローされる
Class.getDeclaredMethod()Class.getMethod() によるリフレクション NoSuchMethodException がスローされる
Class.getDeclaredFields()Class.getFields() によるリフレクション 非 SDK メンバーが結果に含まれない
Class.getDeclaredMethods()Class.getMethods() によるリフレクション 非 SDK メンバーが結果に含まれない
env->GetFieldID() による JNI の呼び出し NULL が返され、NoSuchFieldError がスローされる
env->GetMethodID() による JNI の呼び出し NULL が返され、NoSuchMethodError がスローされる

非 SDK インターフェースのテスト

アプリ内の非 SDK インターフェースのテスト方法をいくつか紹介します。

デバッグ可能なアプリを使用したテスト

デバッグ可能なアプリを作成して、Android 9(API レベル 28)以上が稼働するデバイスまたはエミュレータで実行することにより、非 SDK インターフェースをテストできます。使用するデバイスまたはエミュレータを、アプリの対象 API レベルと一致させるようにします。

テストの実行中にアプリが特定の非 SDK インターフェースにアクセスすると、システムによってログメッセージが出力されます。このログメッセージで以下の詳細情報を確認できます。

  • 宣言しているクラス、名前、型(Android ランタイムで使用されている形式)。
  • アクセス手段(リンク、リフレクション、JNI のいずれか)。
  • 非 SDK インターフェースが属しているリスト。

adb logcat を使用すると、これらのログ メッセージにアクセスできます(実行中のアプリの PID の下に表示されます)。ログ内のエントリの例を次に示します。

Accessing hidden field Landroid/os/Message;->flags:I (light greylist, JNI)

StrictMode API を使用したテスト

StrictMode API を使用して非 SDK インターフェースをテストすることもできます。この API を有効にするには、detectNonSdkApiUsage メソッドを使用します。StrictMode API を有効にすると、カスタム処理を実装可能な penaltyListener を使用して、非 SDK インターフェースを使用するたびにコールバックを受信できるようになります。コールバックで提供される ViolationThrowable からの派生オブジェクトで、埋め込まれているスタック トレースから使用状況がわかります。

veridex ツールを使用したテスト

APK の静的解析ツール veridex を実行することもできます。veridex ツールは APK のコードベース(サードパーティ ライブラリを含む)全体をスキャンして、検出した非 SDK インターフェースの使用状況をレポートします。

veridex ツールには以下の制限事項があります。

  • JNI を介した呼び出しは検出できません。
  • リフレクションを介した呼び出しのサブセットのみを検出できます。
  • 非アクティブなコードパスの解析は API レベルのチェックに制限されています。
  • SSE4.2 命令と POPCNT 命令をサポートするマシンでのみ実行できます。

Windows

ネイティブ Windows バイナリは提供されていませんが、Linux 用 Windows サブシステム(WSL)を使用して Linux バイナリを実行することで、Windows で veridex ツールを実行できます。WSL をインストールして、Linux ディストリビューションとして Ubuntu を選択してから、このセクションの手順を行ってください。

Ubuntu をインストール後、Ubuntu ターミナルを起動し、以下の手順を行います。

  1. ビルド済みの Android ランタイム リポジトリから veridex ツールをダウンロードします。
  2. appcompat.tar.gz ファイルの内容を抽出します。
  3. 抽出したフォルダ内で veridex-linux.zip ファイルを見つけて解凍します。
  4. 解凍したフォルダに移動して、次のコマンドを実行します。your-app.apk には、テストする APK を指定します。

    ./appcompat.sh --dex-file=your-app.apk
    

macOS

Mac OS で veridex ツールを実行する方法は次のとおりです。

  1. ビルド済みの Android ランタイム リポジトリから veridex ツールをダウンロードします。
  2. appcompat.tar.gz ファイルの内容を抽出します。
  3. 抽出したフォルダ内で veridex-mac.zip ファイルを見つけて解凍します。
  4. 解凍したフォルダに移動して、次のコマンドを実行します。/path-from-root/your-app.apk には、テストする APK について、システムのルート ディレクトリからのパスを指定します。

    ./appcompat.sh --dex-file=/path-from-root/your-app.apk
    

Linux

Linux で veridex ツールを実行する方法は次のとおりです。

  1. ビルド済みの Android ランタイム リポジトリから veridex ツールをダウンロードします。
  2. appcompat.tar.gz ファイルの内容を抽出します。
  3. 抽出したフォルダ内で veridex-linux.zip ファイルを見つけて解凍します。
  4. 解凍したフォルダに移動して、次のコマンドを実行します。your-app.apk には、テストする APK を指定します。

    ./appcompat.sh --dex-file=your-app.apk
    

Android Studio の lint ツールを使用したテスト

Android Studio でアプリをビルドするたびに、lint ツールはコードに問題がないかどうか検査します。非 SDK インターフェースを使用しているアプリでは、そのインターフェースがブラックリストまたはグレーリストに含まれているかどうかに応じて、ビルドエラーや警告が表示されます。

コマンドラインから lint ツールを実行したり、特定のプロジェクト、フォルダ、ファイルで検査を手動で実行したりすることもできます。

Play Console を使用したテスト

アプリを Play Console のテストトラックにアップロードすると、自動的にテストが行われて問題がないかどうかチェックされ、リリース前レポートが生成されます。非 SDK インターフェースを使用しているアプリでは、そのインターフェースがブラックリストまたはグレーリストに含まれているかどうかに応じて、エラーや警告が表示されます。

詳しくは、リリース前レポートを使って問題を特定する記事での Android の互換性に関する説明をご覧ください。

新しい公開 API のリクエスト

アプリの機能に使用している非 SDK インターフェースの代わりが見つからない場合は、Issue Tracker で機能リクエストを作成して、新しい公開 API をリクエストできます。

機能リクエストを作成する場合、以下の情報を提供してください。

  • グレーリストに登録されていて、現在使用している API(Accessing hidden ... logcat メッセージに表示される完全な記述子を含める)。
  • その API を使用する必要がある理由(低レベルの詳細情報だけでなく、API が必要な機能の高レベルの詳細情報を含める)。
  • 関連する公開 SDK API では目的を果たせない理由。
  • 試してみた他の方法と、それらがうまくいかなかった理由。

機能リクエストでこれらの詳細情報を提供すると、新しい公開 API が認められる可能性が高くなります。

その他の質問

このセクションでは、デベロッパーからよく寄せられるその他の質問に対する回答を紹介します。

一般的な質問

Google では今後、あらゆるアプリに関するニーズを Issue Tracker で収集するそうですが、それはどのようにして行うのですか?

Google では、以下の方法によって強化されたアプリの静的解析を利用して、Android 9(API レベル 28)向けの最初のリストを作成しました。

  • 主要な Play アプリと Play 以外のアプリの手動によるテスト
  • 内部レポート
  • 内部ユーザーからの自動データ収集
  • デベロッパーのプレビュー レポート
  • 誤検出が増えないように設計されたその他の静的解析

Google では新しいリリースごとにリストを評価しており、その際、Issue Tracker からのデベロッパー フィードバックだけでなく、API の使用状況も考慮します。

非 SDK インターフェースへのアクセスを有効にするにはどうすればよいですか?

adb コマンドを使用して API 適用ポリシーを変更することで、開発デバイスでの非 SDK インターフェースへのアクセスを有効にすることができます。使用するコマンドは、API レベルに応じて異なります。こうしたコマンドの実行に、ユーザーに root 権限のあるデバイスは必要ありません。

Android 10(API レベル 29)

アクセスを有効にするには、次の adb コマンドを使用します。

adb shell settings put global hidden_api_policy  1

API 適用ポリシーをデフォルト設定に戻すには、次のコマンドを使用します。

adb shell settings delete global hidden_api_policy
Android 9(API レベル 28)

アクセスを有効にするには、次の adb コマンドを使用します。

adb shell settings put global hidden_api_policy_pre_p_apps  1
adb shell settings put global hidden_api_policy_p_apps 1

API 適用ポリシーをデフォルトの設定に戻すには、次のコマンドを使用します。

adb shell settings delete global hidden_api_policy_pre_p_apps
adb shell settings delete global hidden_api_policy_p_apps

API 適用ポリシーの整数は以下のいずれかの値に設定できます。

  • 0: 非 SDK インターフェースの検出をすべて無効にします。この設定にすると、非 SDK インターフェースの使用を示すログ メッセージがすべて無効になり、StrictMode API を使用したアプリのテストができなくなります。この設定は推奨されません。
  • 1: すべての非 SDK インターフェースへのアクセスが有効になりますが、非 SDK インターフェースの使用について警告するログ メッセージが出力されます。この設定であれば、StrictMode API を使用してアプリをテストすることもできます。
  • 2: ブラックリストに登録されている非 SDK インターフェースと、グレーリストに登録されてかつ対象 API レベルで制限されている非 SDK インターフェースを使用できないようにします。

非 SDK インターフェースのリストに関する質問

ブラックリストとグレーリストはシステム イメージのどこにありますか?

これらのリストは、プラットフォームの dex ファイル内にあるフィールドとメソッドのアクセス フラグビットにエンコードされています。システム イメージ内にこれらのリストを含む個別のファイルはありません。

異なる OEM デバイスでも、Android バージョンが同じであれば、ブラックリストとグレーリストは同じですか?

OEM は独自のインターフェースをブラックリストに追加できますが、AOSP のブラックリストまたはグレーリストからインターフェースを削除することはできません。CDD はこうした変更を防止しています。Android ランタイムがリストを適用しているかどうかは CTS テストで確認できます。

ネイティブ コードには、非 NDK インターフェースに関する制限はありますか?

Android SDK には Java インターフェースが含まれています。Android 7(API レベル 26)以降、ネイティブ C / C++ コードの非 NDK インターフェースへのアクセスは制限されるようになりました。詳しくは、Android N でのプライベート C / C++ シンボルの制限による安定性の向上に関するブログ記事をご覧ください。

dex2oat または DEX ファイルの操作を制限する計画はありますか?

dex2oat バイナリへのアクセスを制限するアクティブな計画はありませんが、Dalvik 実行可能形式で公に規定されている部分以外に、DEX ファイル形式を安定版または公開版のインターフェースにする予定はありません。Google は、dex2oat と、DEX 形式の未指定の部分をいつでも変更または削除する権利を保有します。また、dex2oat によって生成された ODEX(別名 OAT)、VDEX、CDEX などの派生ファイルはすべて、未指定の形式であることに注意してください。

重要なサードパーティ SDK(難読化ツールなど)で非 SDK インターフェースを使用する必要がある場合、今後 Android バージョンとの互換性を維持するにはどうすればよいですか?この場合、Android の互換性要件を放棄できますか?

SDK ごとに互換性要件を放棄する予定はありません。SDK の互換性を維持できるかどうかの現在の基準が、グレーリストに登録されているインターフェースのみに依存している場合、SDK デベロッパーは SDK インターフェース、または別の手段への移行を計画してください。また、非 SDK インターフェースを使用しない代替方法が見つからない場合は、新しい公開 API をリクエストしてください。

非 SDK インターフェースの制限はすべてのアプリ(サードパーティのアプリだけでなく、システムアプリやファースト パーティのアプリを含む)に適用されますか?

はい、すべてのアプリに適用されます。ただし、プラットフォーム鍵で署名されたアプリは除きます。また、一部のシステム イメージ アプリを対象としたパッケージ ホワイトリストも用意しています。この除外は、システム イメージに含まれているアプリ(または更新対象のシステム イメージ アプリ)にのみ適用されます。このリストは、SDK API ではなく、プライベート プラットフォーム API(LOCAL_PRIVATE_PLATFORM_APIS := true)に対してビルドされるアプリのみを対象にしています。