Android 11 では、product
パーティションの分離を行い、system
パーティションと vendor
パーティションから独立させています。この変更の一環として、product
パーティションからネイティブ インターフェースと Java インターフェースへのアクセスを制御できるようになりました(これは vendor
パーティションに対するインターフェース適用の仕組みに似ています)。
ネイティブ インターフェースを適用する
ネイティブ インターフェースの適用を有効にするには、PRODUCT_PRODUCT_VNDK_VERSION
を current
に設定します(ターゲットの出荷時の API レベルが 29 を超えると、バージョンは自動的に current
に設定されます)。この適用によって以下が可能になります。
product
パーティションのネイティブ モジュールが以下のリンクを行えるようになります。product
パーティション内にあり、静的ライブラリ、共有ライブラリ、ヘッダー ライブラリを含む他のモジュールへの静的または動的なリンク。system
パーティション内の VNDK ライブラリへの動的なリンク。
product
パーティション内のバンドルされていない APK の JNI ライブラリへのリンク(これは、NDK ライブラリとは別に、/product/lib
または/product/lib64
のライブラリにリンクするため)。
この適用によって、product
パーティション以外のパーティションへのリンクが許可されることはありません。
ビルド時の適用(Android.bp)
Android 11 では、システム モジュールはコアイメージとベンダー イメージのバリアントに加えて、プロダクト イメージのバリアントを作成できます。ネイティブ インターフェースの適用が有効になっている場合(PRODUCT_PRODUCT_VNDK_VERSION
が current
に設定されている場合)、以下のようになります。
product
パーティションのネイティブ モジュールは、コアバリアントではなくプロダクト バリアント内に存在。Android.bp
ファイルにproduct_available: true
を含むモジュールは、プロダクト バリアントによって使用可能。product_specific: true
を指定するライブラリまたはバイナリは、Android.bp
ファイル内のproduct_specific: true
またはproduct_available: true
を指定する他のライブラリにリンクできます。product
バイナリが VNDK ライブラリにリンクできるようにするには、VNDK ライブラリのAndroid.bp
ファイル内でproduct_available: true
となっている必要があります。
次の表は、イメージ バリアントの作成に使用される Android.bp
プロパティをまとめたものです。
Android.bp のプロパティ | 作成するバリアント | |
---|---|---|
適用前 | 適用後 | |
デフォルト(なし) | コア ( /system 、/system_ext 、/product を含む) |
コア ( /system と /system_ext を含むが /product は含まれない) |
system_ext_specific: true |
コア | コア |
product_specific: true |
コア | プロダクト |
vendor: true |
ベンダー | ベンダー |
vendor_available: true |
コア、ベンダー | コア、ベンダー |
product_available: true |
なし | コア、プロダクト |
vendor_available: true と product_available:
true |
なし | コア、プロダクト、ベンダー |
system_ext_specific: true と vendor_available:
true |
コア、ベンダー | コア、ベンダー |
product_specific: true と vendor_available:
true |
コア、ベンダー | プロダクト、ベンダー |
ビルド時の適用(Android.mk)
ネイティブ インターフェースの適用が有効になっている場合、product
パーティションにインストールされているネイティブ モジュールのリンクタイプは native:product
で、他の native:product
モジュールや native:vndk
モジュールのみにリンクできますが、これら以外のモジュールにリンクしようとすると、ビルドシステムがリンクタイプのチェックエラーを生成します。
ランタイムの適用
ネイティブ インターフェースの適用が有効になっている場合、bionic リンカーのリンカー構成では、システム プロセスによる product
ライブラリの使用を許可せず、product
パーティション外のライブラリにリンクできない product
プロセス用の product
セクションを作成します(ただし、このようなプロセスは VNDK ライブラリにリンクできます)。ランタイム リンク構成に違反しようとすると、プロセスが失敗し、CANNOT LINK EXECUTABLE
エラー メッセージが生成されます。
Java インターフェースを適用する
Java インターフェースの適用を有効にするには、PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE
を true
に設定します(ターゲットの出荷時の API レベルが 29 を超えると、この値は自動的に true
に設定されます)。有効にすると、次のアクセスが許可または禁止されます。
API | /system | /system_ext | /product | /vendor | /data |
---|---|---|---|---|---|
公開 API | |||||
@SystemApi | |||||
@hide API |
vendor
パーティションと同様に、product
パーティションのアプリまたは Java ライブラリは、公開 API とシステム API のみを使用できます。非表示 API を使用するライブラリにはリンクできません。この制限は、ビルド時のリンクやランタイムのリフレクションも含まれます。
ビルド時の適用
ビルド時、Make と Soong は、platform_apis
フィールドと sdk_version
フィールドを確認することで、product
パーティションの Java モジュールが非表示 API を使用しないことを確認します。product
パーティション内のアプリの sdk_version
には、current
、system_current
、または数値形式の API が入力されている必要があります。platform_apis
フィールドは空である必要があります。
ランタイムの適用
Android ランタイムは、product
パーティションのアプリがリフレクションなどの非表示 API を使用していないことを確認します。詳細については、非 SDK インターフェースの制限をご覧ください。
プロダクト インターフェースの適用を有効にする
プロダクト インターフェースの適用を有効にするには、このセクションのステップを実行します。
ステップ | タスク | 必須 |
---|---|---|
1 | system パーティションのパッケージを指定する独自のシステムの makefile を定義し、device.mk でアーティファクト パスの要件チェックを設定します(システム以外のモジュールが system パーティションにインストールするのを防止するため)。 |
× |
2 | 許可リストをクリーンアップします。 | × |
3 | ネイティブ インターフェースを適用し、ランタイム リンクの失敗を特定します(Java の適用と並行して実行できます)。 | ○ |
4 | Java インターフェースを適用して、ランタイムの動作を確認します(ネイティブの適用と並行して実行できます)。 | ○ |
5 | ランタイムの動作を確認します。 | ○ |
6 | プロダクト インターフェースの適用で device.mk を更新します。 |
○ |
ステップ 1: makefile を作成してアーティファクト パスのチェックを有効にする
このステップでは、system
makefile を定義します。
system
パーティションのパッケージを定義する makefile を作成します。たとえば、次のようにoem_system.mk
ファイルを作成します。$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system.mk) # Applications PRODUCT_PACKAGES += \ CommonSystemApp1 \ CommonSystemApp2 \ CommonSystemApp3 \ # Binaries PRODUCT_PACKAGES += \ CommonSystemBin1 \ CommonSystemBin2 \ CommonSystemBin3 \ # Libraries PRODUCT_PACKAGES += \ CommonSystemLib1 \ CommonSystemLib2 \ CommonSystemLib3 \ PRODUCT_SYSTEM_NAME := oem_system PRODUCT_SYSTEM_BRAND := Android PRODUCT_SYSTEM_MANUFACTURER := Android PRODUCT_SYSTEM_MODEL := oem_system PRODUCT_SYSTEM_DEVICE := generic # For system-as-root devices, system.img should be mounted at /, so we # include ROOT here. _my_paths := \ $(TARGET_COPY_OUT_ROOT)/ \ $(TARGET_COPY_OUT_SYSTEM)/ \ $(call require-artifacts-in-path, $(_my_paths),)
device.mk
ファイルで、system
パーティションの共通の makefile を継承し、アーティファクト パスの要件チェックを有効にします。次に例を示します。$(call inherit-product, $(SRC_TARGET_DIR)/product/oem_system.mk) # Enable artifact path requirements checking PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := strict
アーティファクト パスの要件について
PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
が true
または strict
に設定されている場合、ビルドシステムは、他の makefile で定義されたパッケージが require-artifacts-in-path
で定義されたパスにインストールされるのを防ぎ、なおかつ現在の makefile で定義されたパッケージが require-artifacts-in-path
で定義されたパスの外部でアーティファクトをインストールするのを防ぎます。
上記の例では、PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS
を strict
に設定しており、oem_system.mk
外の makefile が root
パーティションまたは system
パーティションにインストールされているモジュールを含めることはできません。これらのモジュールを含めるには、oem_system.mk
ファイル自体またはそれに含まれている makefile 内でモジュールを定義する必要があります。許可されていないパスにモジュールをインストールしようとすると、ビルドが中断されます。中断を修正するには、次のいずれかを行います。
オプション 1:
oem_system.mk
に含まれる makefile にシステム モジュールを含めます。これにより、アーティファクトのパス要件が満たされ(含まれる makefile 内にモジュールが存在するようになったため)、require-artifacts-in-path 内の一連のパスにインストールできるようになります。オプション 2:
system_ext
パーティションまたはproduct
パーティションにモジュールをインストールします(system
パーティションにモジュールをインストールしないでください)。オプション 3:
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
にモジュールを追加します。これには、インストールが許可されているモジュールがリストされます。
ステップ 2: 許可リストを空にする
このステップでは、PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST
を空にして、oem_system.mk
を共有するすべてのデバイスが単一の system
イメージも共有できるようにします。許可リストを空にするには、リスト内のすべてのモジュールを system_ext
パーティションまたは product
パーティションに移動するか、system
makefile に追加します。共通の system
イメージの定義はプロダクト インターフェースの適用を有効にするために必須ではないため、このステップは省略できます。ただし、許可リストを空にすると、system
の境界を system_ext
で定義できます。
ステップ 3: ネイティブ インターフェースを適用する
このステップでは、PRODUCT_PRODUCT_VNDK_VERSION := current
を設定し、ビルドとランタイムのエラーを見つけて解決します。デバイスの起動とログを確認し、ランタイム リンクの失敗を見つけて修正するには、以下のステップを実行します。
PRODUCT_PRODUCT_VNDK_VERSION := current
を設定します。デバイスをビルドして、ビルドエラーを探します。プロダクト バリアントやコアバリアントが存在しないために、ビルドが中断されることがあります。一般的な中断には、次のものがあります。
product_specific: true
を含むhidl_interface
モジュールで、システム モジュールを使用できない。これを修正するには、product_specific: true
をsystem_ext_specific: true
に置き換えます。- プロダクト モジュールに必要なプロダクト バリアントがモジュールに存在しないことがある。これを修正するには、
product_available: true
を設定してproduct
パーティションで使用できるようにするか、product_specific: true
を設定してモジュールをproduct
パーティションに移動します。
ビルドエラーを解決し、デバイスが正常にビルドされることを確認します。
イメージをフラッシュし、デバイスの起動とログでランタイム エラーを探します。
- テストケース ログの
linker
タグにCANNOT LINK EXECUTABLE
メッセージが表示された場合は、makefile に依存関係がありません(ビルド時にキャプチャされていない)。 - ビルドシステムから確認するには、必要なライブラリを
shared_libs:
フィールドまたはrequired:
フィールドに追加します。
- テストケース ログの
上記のガイダンスに沿って、欠落している依存関係を解決します。
ステップ 4: Java インターフェースを適用する
このステップでは、PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true
を設定し、結果のビルドエラーを見つけて修正します。次の 2 種類のエラーを探します。
リンクエラー。このエラーは、アプリよりリンク先の Java モジュールの
sdk_version
の範囲のほうが広いことを示します。修正するには、アプリのsdk_version
の範囲を広げるか、ライブラリのsdk_version
を制限します。エラーの例:error: frameworks/base/packages/SystemUI/Android.bp:138:1: module "SystemUI" variant "android_common": compiles against system API, but dependency "telephony-common" is compiling against private API.Adjust sdk_version: property of the source or target module so that target module is built with the same or smaller API set than the source.
シンボルエラー。このエラーは、シンボルが非表示 API に含まれているため、検出できないことを示します。この問題を解決するには、公開されている(非表示ではない)API を使用するか、別の方法を見つけます。エラーの例:
frameworks/opt/net/voip/src/java/com/android/server/sip/SipSessionGroup.java:1051: error: cannot find symbol ProxyAuthenticate proxyAuth = (ProxyAuthenticate)response.getHeader( ^ symbol: class ProxyAuthenticate location: class SipSessionGroup.SipSessionImpl
ステップ 5: ランタイムの動作を確認する
このステップでは、ランタイムの動作が想定どおりであることを確認します。デバッグ可能なアプリの場合、StrictMode.detectNonSdkApiUsage
(アプリで非表示 API を使用するとログを生成)を使用して、非表示 API の使用状況をログで確認できます。または、veridex 静的解析ツールを使用して、使用量の種類(リンクまたはリフレクション)、制限レベル、コールスタックを取得することもできます。
Veridex の構文:
./art/tools/veridex/appcompat.sh --dex-file={apk file}
veridex の結果の例:
#1: Linking greylist-max-o Landroid/animation/AnimationHandler;-><init>()V use(s): Lcom/android/systemui/pip/phone/PipMotionHelper;-><init>(Landroid/content/Context;Landroid/app/IActivityManager;Landroid/app/IActivityTaskManager;Lcom/android/systemui/pip/phone/PipMenuActivityController;Lcom/android/internal/policy/PipSnapAlgorithm;Lcom/android/systemui/statusbar/FlingAnimationUtils;)V #1332: Reflection greylist Landroid/app/Activity;->mMainThread use(s): Landroidx/core/app/ActivityRecreator;->getMainThreadField()Ljava/lang/reflect/Field;
veridex の使用について詳しくは、veridex ツールを使用したテストをご覧ください。
ステップ 6: device.mk を更新する
すべてのビルドとランタイムの問題を修正し、ランタイムの動作が期待どおりであることを確認したら、device.mk
で次のように設定します。
PRODUCT_PRODUCT_VNDK_VERSION := current
PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true