プロダクト パーティション インターフェースの適用

Android 11 では、product パーティションの分離を行い、system パーティションと vendor パーティションから独立させています。この変更の一環として、product パーティションからネイティブ インターフェースと Java インターフェースへのアクセスを制御できるようになりました(これは vendor パーティションに対するインターフェース適用の仕組みに似ています)。

ネイティブ インターフェースの適用

ネイティブ インターフェースの適用を有効にするには、PRODUCT_PRODUCT_VNDK_VERSIONcurrent に設定します(ターゲットの出荷時の API レベルが 29 を超えると、バージョンは自動的に current に設定されます)。この適用によって以下が可能になります。

  • product パーティションのネイティブ モジュールが以下のリンクを行えるようになります。
    • product パーティション内にあり、静的ライブラリ、共有ライブラリ、ヘッダー ライブラリを含む他のモジュールへの静的または動的なリンク。
    • system パーティション内の VNDK ライブラリへの動的なリンク。
  • product パーティション内のバンドルされていない APK の JNI ライブラリへのリンク(これは、NDK ライブラリとは別に、/product/lib または /product/lib64 のライブラリにリンクするため)。

この適用によって、product パーティション以外のパーティションへのリンクが許可されることはありません。

ビルド時の適用(Android.bp)

Android 11 では、システム モジュールはコアイメージとベンダー イメージのバリアントに加えて、プロダクト イメージのバリアントを作成できます。ネイティブ インターフェースの適用が有効になっている場合(PRODUCT_PRODUCT_VNDK_VERSIONcurrent に設定されている場合)、以下のようになります。

  • product パーティションのネイティブ モジュールは、コアバリアントではなくプロダクト バリアント内に存在。

  • Android.bp ファイルに vendor_available: true を含むモジュールは、プロダクト バリアントとベンダー バリアントによって使用可能。

  • product_specific: true を指定するライブラリまたはバイナリは、Android.bp ファイル内の product_specific: true または vendor_available: true を指定する他のライブラリにリンクできます。

  • product バイナリが VNDK ライブラリにリンクできるようにするには、VNDK ライブラリの Android.bp ファイル内で vendor_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 コア、ベンダー コア、プロダクト、ベンダー
system_ext_specific: truevendor_available: true コア、ベンダー コア、プロダクト、ベンダー
product_specific: truevendor_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_INTERFACEtrue に設定します。(ターゲットの出荷時の 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 には、currentsystem_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 を定義します。

  1. 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),)
    
  2. 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_REQUIREMENTStrue または strict に設定されている場合、ビルドシステムは、他の makefile で定義されたパッケージが require-artifacts-in-path で定義されたパスにインストールされるのを防ぎ、なおかつ現在の makefile で定義されたパッケージが require-artifacts-in-path で定義されたパスの外部でアーティファクトをインストールするのを防ぎます。

上記の例では、PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTSstrict に設定しており、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 を設定し、ビルドとランタイムのエラーを見つけて解決します。デバイスの起動とログを確認し、ランタイム リンクの失敗を見つけて修正するには、以下のステップを実行します。

  1. PRODUCT_PRODUCT_VNDK_VERSION := current を設定します。

  2. デバイスをビルドして、ビルドエラーを探します。プロダクト バリアントやコアバリアントが存在しないために、ビルドが中断されることがあります。一般的な中断には、次のものがあります。

    • product_specific: true を含む hidl_interface モジュールで、システム モジュールを使用できない。これを修正するには、product_specific: truesystem_ext_specfic: true に置き換えます。
    • プロダクト モジュールに必要なプロダクト バリアントがモジュールに存在しないことがある。これを修正するには、vendor_available: true を設定して product パーティションで使用できるようにするか、product_specific: true を設定してモジュールを product パーティションに移動します。
  3. ビルドエラーを解決し、デバイスが正常にビルドされることを確認します。

  4. イメージをフラッシュし、デバイスの起動とログでランタイム エラーを探します。

    • テストケース ログの linker タグに CANNOT LINK EXECUTABLE メッセージが表示された場合は、makefile に依存関係がありません(ビルド時にキャプチャされていない)。
    • ビルドシステムから確認するには、必要なライブラリを shared_libs: フィールドまたは required: フィールドに追加します。
  5. 上記のガイダンスに沿って、欠落している依存関係を解決します。

ステップ 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