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 := currentPRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE := true