HALのAIDL

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

Android 11 では、Android で HAL に AIDL を使用する機能が導入されています。これにより、HIDL を使用せずに Android の一部を実装できます。可能であれば、HAL を移行して AIDL のみを使用します(上流の HAL が HIDL を使用する場合は、HIDL を使用する必要があります)。

system.imgなどのフレームワーク コンポーネントと vendor.img などのハードウェア コンポーネント間の通信に AIDL を使用する HAL は、安定したvendor.imgを使用する必要があります。ただし、たとえばある HAL から別の HAL へなど、パーティション内で通信する場合、使用する IPC メカニズムに制限はありません。

動機

AIDL は HIDL よりも長く使用されており、Android フレームワーク コンポーネント間やアプリ内など、他の多くの場所で使用されています。 AIDL が安定性をサポートするようになったので、単一の IPC ランタイムでスタック全体を実装できます。 AIDL には、HIDL よりも優れたバージョン管理システムもあります。

  • 単一の IPC 言語を使用するということは、学習、デバッグ、最適化、および保護することが 1 つだけであることを意味します。
  • AIDL は、インターフェースの所有者向けのインプレース バージョニングをサポートしています。
    • 所有者は、インターフェイスの最後にメソッドを追加したり、Parcelable にフィールドを追加したりできます。これは、何年にもわたってコードをバージョン管理することが容易になり、また、年々のコストが小さくなることを意味します (型はインプレースで修正でき、インターフェイス バージョンごとに追加のライブラリは必要ありません)。
    • 拡張インターフェイスは、型システムではなく実行時にアタッチできるため、下流の拡張機能を新しいバージョンのインターフェイスにリベースする必要はありません。
  • 既存の AIDL インターフェースは、その所有者が安定化することを選択したときに直接使用できます。以前は、HIDL でインターフェース全体のコピーを作成する必要がありました。

AIDL HAL インターフェースの作成

システムとベンダーの間で AIDL インターフェイスを使用するには、インターフェイスに次の 2 つの変更が必要です。

  • すべての型定義に@VintfStabilityのアノテーションを付ける必要があります。
  • aidl_interface宣言には、 stability: "vintf", .

これらの変更を行うことができるのは、インターフェースの所有者だけです。

これらの変更を行う場合、機能するためにはインターフェイスがVINTF マニフェストに含まれている必要があります。 VTS テストvts_treble_vintf_vendor_testを使用して、これ (およびリリースされたインターフェイスが凍結されていることの確認などの関連要件) をテストします。 NDK バックエンドでAIBinder_forceDowngradeToLocalStability 、C++ バックエンドでandroid::Stability::forceDowngradeToLocalStability 、またはバインダー オブジェクトの送信前に Java バックエンドでandroid.os.Binder#forceDowngradeToSystemStabilityを呼び出すことにより、これらの要件なしで@VintfStabilityインターフェースを使用できます。別のプロセスに。すべてのアプリはシステム コンテキストで実行されるため、サービスをベンダー安定性にダウングレードすることは Java ではサポートされていません。

さらに、コードの移植性を最大限に高め、不要なライブラリの追加などの潜在的な問題を回避するために、CPP バックエンドを無効にします。

3 つのバックエンド (Java、NDK、および CPP) があるため、以下のコード例でのbackendsの使用は正しいことに注意してください。以下のコードは、CPP バックエンドを具体的に選択して無効にする方法を示しています。

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL インターフェースの検索

HAL 用の AOSP 安定 AIDL インターフェースは、HIDL インターフェースと同じベース ディレクトリのaidlフォルダにあります。

  • ハードウェア/インターフェース
  • フレームワーク/ハードウェア/インターフェース
  • システム/ハードウェア/インターフェース

拡張インターフェイスは、 vendorまたはhardwareの他のhardware/interfacesサブディレクトリに配置する必要があります。

拡張インターフェイス

Android には、リリースごとに一連の公式 AOSP インターフェースがあります。 Android パートナーがこれらのインターフェースに機能を追加したい場合、これらを直接変更しないでください。これは、Android ランタイムが AOSP Android ランタイムと互換性がないことを意味するためです。 GMS デバイスの場合、これらのインターフェイスの変更を避けることは、GSI イメージが引き続き機能することを保証するものでもあります。

拡張機能は、次の 2 つの方法で登録できます。

  • 実行時に、添付された拡張機能を参照してください。
  • スタンドアロンで、グローバルに登録され、VINTF に登録されています。

ただし、拡張機能が登録されている場合、ベンダー固有の (上流の AOSP の一部ではないことを意味する) コンポーネントがインターフェイスを使用する場合、マージの競合が発生する可能性はありません。ただし、上流の AOSP コンポーネントに対して下流の変更が行われると、マージの競合が発生する可能性があるため、次の戦略をお勧めします。

  • インターフェイスの追加は、次のリリースで AOSP にアップストリームできます
  • マージの競合なしで、さらなる柔軟性を可能にするインターフェイスの追加は、次のリリースでアップストリームすることができます

拡張 Parcelables: ParcelableHolder

ParcelableHolderは、別のParcelableを含むことができるParcelableです。 ParcelableHolder の主な使用例は、 ParcelableHolderを拡張可能にすることParcelable 。たとえば、デバイスの実装者が、AOSP で定義されたParcelableであるAospDefinedParcelableを拡張して、付加価値機能を含めることができると期待しているイメージ。

以前はParcelableHolderがなければ、フィールドを追加するとエラーになるため、デバイス実装者は AOSP 定義の安定した AIDL インターフェイスを変更できませんでした。

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

前のコードに見られるように、Android の次のリリースで Parcelable が改訂されたときに、デバイスの実装者によって追加されたフィールドが競合する可能性があるため、このプラクティスは破られています。

ParcelableHolderを使用すると、Parcelable の所有者はParcelableの拡張ポイントを定義できます。

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

次に、デバイスの実装者は、拡張用に独自のParcelableを定義できます。

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

最後に、 ParcelableHolderフィールドを介して、新しいParcelableを元のParcelableにアタッチできます。


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL ランタイムに対するビルド

AIDL には、Java、NDK、CPP の 3 つの異なるバックエンドがあります。 Stable AIDL を使用するには、常にsystem/lib*/libbinder.soにある libbinder のシステム コピーを使用し、 /dev/binderで話す必要があります。ベンダー イメージのコードの場合、これはlibbinder (VNDK から) を使用できないことを意味します。このライブラリには、不安定な C++ API と不安定な内部が含まれています。代わりに、ネイティブ ベンダー コードは AIDL の NDK バックエンドを使用し、 libbinder_ndk (システムlibbinder.soによってサポートされている) に対してリンクし、 aidl_interfaceエントリによって作成された-ndk_platformライブラリに対してリンクする必要があります。

AIDL HAL サーバー インスタンス名

慣例により、AIDL HAL サービスのインスタンス名は$package.$type/$instanceの形式になります。たとえば、バイブレーター HAL のインスタンスはandroid.hardware.vibrator.IVibrator/defaultとして登録されます。

AIDL HAL サーバーの作成

@VintfStabilityサーバーは、VINTF マニフェストで宣言する必要があります。たとえば、次のようになります。

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

それ以外の場合は、通常どおり AIDL サービスを登録する必要があります。 VTS テストを実行する場合、宣言されたすべての AIDL HAL が利用可能であることが期待されます。

AIDL クライアントの作成

AIDL クライアントは、たとえば次のように、互換性マトリックスで自分自身を宣言する必要があります。

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

既存の HAL を HIDL から AIDL に変換する

hidl2aidlツールを使用して、HIDL インターフェースを AIDL に変換します。

hidl2aidl機能:

  • 指定されたパッケージの.halファイルに基づいて.aidlファイルを作成します
  • すべてのバックエンドを有効にして、新しく作成された AIDL パッケージのビルド ルールを作成する
  • HIDL 型から AIDL 型に変換するための変換メソッドを Java、CPP、NDK バックエンドで作成する
  • 必要な依存関係を持つ翻訳ライブラリのビルド ルールを作成する
  • 静的アサートを作成して、HIDL および AIDL 列挙子が CPP および NDK バックエンドで同じ値を持つようにします

次の手順に従って、.hal ファイルのパッケージを .aidl ファイルに変換します。

  1. system/tools/hidl/hidl2aidlにあるツールをビルドします。

    このツールを最新のソースからビルドすると、最も完全なエクスペリエンスが提供されます。最新バージョンを使用して、以前のリリースから古いブランチのインターフェイスを変換できます。

    m hidl2aidl
    
  2. 出力ディレクトリの後に変換するパッケージを指定してツールを実行します。

    オプションで、 -l引数を使用して、新しいライセンス ファイルの内容を生成されたすべてのファイルの先頭に追加します。必ず正しいライセンスと日付を使用してください。

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    例えば:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. 生成されたファイルを読み、変換に関する問題を修正します。

    • conversion.logには、最初に修正する未処理の問題が含まれています。
    • 生成され.aidlファイルには、対処が必要な警告や提案が含まれている場合があります。これらのコメントは//で始まります。
    • この機会に、パッケージをクリーンアップして改善してください。
    • toStringequalsなど、必要になる可能性がある機能については、 @JavaDeriveアノテーションを確認してください。
  4. 必要なターゲットだけをビルドします。

    • 使用しないバックエンドを無効にします。 CPP バックエンドよりも NDK バックエンドを優先します。「 runtime の選択」を参照してください。
    • 使用されない翻訳ライブラリまたはその生成コードを削除します。
  5. AIDL と HIDL の主な違いを参照してください。

    • 通常、AIDL の組み込みStatusと例外を使用すると、インターフェースが改善され、別のインターフェース固有のステータス タイプが不要になります。
    • メソッドの AIDL インターフェース引数は、HIDL のようにデフォルトで@nullableではありません。

AIDL HAL のセポリシー

ベンダー コードに表示される AIDL サービス タイプには、 hal_service_type属性が必要です。それ以外の場合、sepolicy 構成は他の AIDL サービスと同じです (ただし、HAL には特別な属性があります)。以下は、HAL サービス コンテキストの定義例です。

    type hal_foo_service, service_manager_type, hal_service_type;

プラットフォームで定義されたほとんどのサービスでは、正しいタイプのサービス コンテキストがすでに追加されています(たとえば、 android.hardware.foo.IFoo/defaultはすでにhal_foo_serviceとしてマークされています)。ただし、フレームワーク クライアントが複数のインスタンス名をサポートしている場合は、追加のインスタンス名をデバイス固有のservice_contextsファイルに追加する必要があります。

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

新しいタイプの HAL を作成するときは、HAL 属性を追加する必要があります。特定の HAL 属性が複数のサービス タイプに関連付けられている場合があります (前述のように、それぞれが複数のインスタンスを持つ場合があります)。 HAL fooの場合、 hal_attribute(foo)があります。このマクロは、属性hal_foo_clientおよびhal_foo_serverを定義します。特定のドメインに対して、 hal_client_domainおよびhal_server_domainマクロは、ドメインを特定の HAL 属性に関連付けます。たとえば、この HAL のクライアントであるシステム サーバーは、ポリシーhal_client_domain(system_server, hal_foo)対応します。同様に、HAL サーバーにはhal_server_domain(my_hal_domain, hal_foo)が含まれます。通常、特定の HAL 属性に対して、参照用またはサンプル HAL 用にhal_foo_defaultのようなドメインも作成します。ただし、一部のデバイスはこれらのドメインを独自のサーバーに使用します。複数のサーバーのドメインを区別することは、同じインターフェースを提供し、実装で異なる権限セットを必要とする複数のサーバーがある場合にのみ問題になります。これらすべてのマクロで、 hal_fooは実際には sepolicy オブジェクトではありません。代わりに、このトークンはこれらのマクロによって使用され、クライアント サーバー ペアに関連付けられた属性のグループを参照します。

ただし、これまでのところ、 hal_foo_servicehal_foo ( hal_attribute(foo)の属性ペア) を関連付けていません。 HAL 属性は、 hal_attribute_serviceマクロを使用して AIDL HAL サービスに関連付けられます(HIDL HAL はhal_attribute_hwserviceマクロを使用します)。たとえば、 hal_attribute_service(hal_foo, hal_foo_service)です。これは、 hal_foo_clientプロセスが HAL を取得でき、 hal_foo_serverプロセスが HAL を登録できることを意味します。これらの登録規則の適用は、コンテキスト マネージャー ( servicemanager ) によって行われます。サービス名が常に HAL 属性に対応しているとは限らないことに注意してください。たとえば、 hal_attribute_service(hal_foo, hal_foo2_service)が表示される場合があります。ただし、一般に、これはサービスが常に一緒に使用されることを意味するため、hal_foo2_service をhal_foo_serviceして、すべてのサービス コンテキストにhal_foo2_serviceを使用することを検討できます。複数のhal_attribute_serviceを設定するほとんどの HAL は、元の HAL 属性名が十分に一般的ではなく、変更できないためです。

これらをすべてまとめると、HAL の例は次のようになります。

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

付属の拡張インターフェース

拡張機能は、サービス マネージャーに直接登録された最上位のインターフェイスであるか、サブインターフェイスであるかに関係なく、任意のバインダー インターフェイスに接続できます。拡張機能を取得するときは、拡張機能のタイプが期待どおりであることを確認する必要があります。拡張子は、バインダーを提供するプロセスからのみ設定できます。

拡張機能が既存の HAL の機能を変更する場合は常に、添付された拡張機能を使用する必要があります。まったく新しい機能が必要な場合は、このメカニズムを使用する必要はなく、拡張インターフェイスをサービス マネージャーに直接登録できます。接続された拡張インターフェイスは、サブインターフェイスに接続されている場合に最も意味があります。これらの階層は深いか、複数インスタンス化されている可能性があるためです。グローバル拡張機能を使用して別のサービスのバインダー インターフェース階層をミラーリングすると、直接接続された拡張機能と同等の機能を提供するために大規模な簿記が必要になります。

バインダーに拡張機能を設定するには、次の API を使用します。

  • NDK バックエンド: AIBinder_setExtension
  • Java バックエンド: android.os.Binder.setExtension
  • CPP バックエンド: android::Binder::setExtension

バインダーで拡張機能を取得するには、次の API を使用します。

  • NDK バックエンド: AIBinder_getExtension
  • Java バックエンド: android.os.IBinder.getExtension
  • CPP バックエンド: android::IBinder::getExtension

これらの API の詳細については、対応するバックエンドのgetExtension関数のドキュメントを参照してください。拡張機能の使用例は、 hardware/interfaces/tests/extension/vibratorにあります。

AIDL と HIDL の主な違い

AIDL HAL または AIDL HAL インターフェースを使用する場合は、HIDL HAL の記述との違いに注意してください。

  • AIDL 言語の構文は Java に近いです。 HIDL 構文は C++ に似ています。
  • すべての AIDL インターフェースには、組み込みのエラー ステータスがあります。カスタム ステータス タイプを作成する代わりに、インターフェイス ファイルで定数ステータス int を作成し、CPP/NDK バックエンドでEX_SERVICE_SPECIFICを使用し、Java バックエンドでServiceSpecificExceptionを使用します。エラー処理を参照してください。
  • AIDL は、バインダー オブジェクトが送信されたときにスレッドプールを自動的に開始しません。それらは手動で開始する必要があります (スレッド管理を参照)。
  • AIDL は、チェックされていないトランスポート エラーで中止されません(HIDL Returnは、チェックされていないエラーで中止されます)。
  • AIDL は、ファイルごとに 1 つのタイプのみを宣言できます。
  • AIDL 引数は、出力パラメーターに加えて、in/out/inout として指定できます (「同期コールバック」はありません)。
  • AIDL は、ハンドルの代わりに fd をプリミティブ型として使用します。
  • HIDL は、互換性のない変更にはメジャー バージョンを使用し、互換性のある変更にはマイナー バージョンを使用します。 AIDL では、下位互換性のある変更が適切に行われます。 AIDL には、メジャー バージョンの明確な概念はありません。代わりに、これはパッケージ名に組み込まれます。たとえば、AIDL はパッケージ名bluetooth2を使用する場合があります。
  • デフォルトでは、AIDL はリアルタイムの優先度を継承しません。 setInheritRt関数をバインダーごとに使用して、リアルタイムの優先順位の継承を有効にする必要があります。

Android 11 では、Android で HAL に AIDL を使用する機能が導入されています。これにより、HIDL を使用せずに Android の一部を実装できます。可能であれば、HAL を移行して AIDL のみを使用します(上流の HAL が HIDL を使用する場合は、HIDL を使用する必要があります)。

system.imgなどのフレームワーク コンポーネントと vendor.img などのハードウェア コンポーネント間の通信に AIDL を使用する HAL は、安定したvendor.imgを使用する必要があります。ただし、たとえばある HAL から別の HAL へなど、パーティション内で通信する場合、使用する IPC メカニズムに制限はありません。

動機

AIDL は HIDL よりも長く使用されており、Android フレームワーク コンポーネント間やアプリ内など、他の多くの場所で使用されています。 AIDL が安定性をサポートするようになったので、単一の IPC ランタイムでスタック全体を実装できます。 AIDL には、HIDL よりも優れたバージョン管理システムもあります。

  • 単一の IPC 言語を使用するということは、学習、デバッグ、最適化、および保護することが 1 つだけであることを意味します。
  • AIDL は、インターフェースの所有者向けのインプレース バージョニングをサポートしています。
    • 所有者は、インターフェイスの最後にメソッドを追加したり、Parcelable にフィールドを追加したりできます。これは、何年にもわたってコードをバージョン管理することが容易になり、また、年々のコストが小さくなることを意味します (型はインプレースで修正でき、インターフェイス バージョンごとに追加のライブラリは必要ありません)。
    • 拡張インターフェイスは、型システムではなく実行時にアタッチできるため、下流の拡張機能を新しいバージョンのインターフェイスにリベースする必要はありません。
  • 既存の AIDL インターフェースは、その所有者が安定化することを選択したときに直接使用できます。以前は、HIDL でインターフェース全体のコピーを作成する必要がありました。

AIDL HAL インターフェースの作成

システムとベンダーの間で AIDL インターフェイスを使用するには、インターフェイスに次の 2 つの変更が必要です。

  • すべての型定義に@VintfStabilityのアノテーションを付ける必要があります。
  • aidl_interface宣言には、 stability: "vintf", .

これらの変更を行うことができるのは、インターフェースの所有者だけです。

これらの変更を行う場合、機能するためにはインターフェイスがVINTF マニフェストに含まれている必要があります。 VTS テストvts_treble_vintf_vendor_testを使用して、これ (およびリリースされたインターフェイスが凍結されていることの確認などの関連要件) をテストします。 NDK バックエンドでAIBinder_forceDowngradeToLocalStability 、C++ バックエンドでandroid::Stability::forceDowngradeToLocalStability 、またはバインダー オブジェクトの送信前に Java バックエンドでandroid.os.Binder#forceDowngradeToSystemStabilityを呼び出すことにより、これらの要件なしで@VintfStabilityインターフェースを使用できます。別のプロセスに。すべてのアプリはシステム コンテキストで実行されるため、サービスをベンダー安定性にダウングレードすることは Java ではサポートされていません。

さらに、コードの移植性を最大限に高め、不要なライブラリの追加などの潜在的な問題を回避するために、CPP バックエンドを無効にします。

3 つのバックエンド (Java、NDK、および CPP) があるため、以下のコード例でのbackendsの使用は正しいことに注意してください。以下のコードは、CPP バックエンドを具体的に選択して無効にする方法を示しています。

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

AIDL HAL インターフェースの検索

HAL 用の AOSP 安定 AIDL インターフェースは、HIDL インターフェースと同じベース ディレクトリのaidlフォルダにあります。

  • ハードウェア/インターフェース
  • フレームワーク/ハードウェア/インターフェース
  • システム/ハードウェア/インターフェース

拡張インターフェイスは、 vendorまたはhardwareの他のhardware/interfacesサブディレクトリに配置する必要があります。

拡張インターフェイス

Android には、リリースごとに一連の公式 AOSP インターフェースがあります。 Android パートナーがこれらのインターフェースに機能を追加したい場合、これらを直接変更しないでください。これは、Android ランタイムが AOSP Android ランタイムと互換性がないことを意味するためです。 GMS デバイスの場合、これらのインターフェイスの変更を避けることは、GSI イメージが引き続き機能することを保証するものでもあります。

拡張機能は、次の 2 つの方法で登録できます。

  • 実行時に、添付された拡張機能を参照してください。
  • スタンドアロンで、グローバルに登録され、VINTF に登録されています。

ただし、拡張機能が登録されている場合、ベンダー固有の (上流の AOSP の一部ではないことを意味する) コンポーネントがインターフェイスを使用する場合、マージの競合が発生する可能性はありません。ただし、上流の AOSP コンポーネントに対して下流の変更が行われると、マージの競合が発生する可能性があるため、次の戦略をお勧めします。

  • インターフェイスの追加は、次のリリースで AOSP にアップストリームできます
  • マージの競合なしで、さらなる柔軟性を可能にするインターフェイスの追加は、次のリリースでアップストリームすることができます

拡張 Parcelables: ParcelableHolder

ParcelableHolderは、別のParcelableを含むことができるParcelableです。 ParcelableHolder の主な使用例は、 ParcelableHolderを拡張可能にすることParcelable 。たとえば、デバイスの実装者が、AOSP で定義されたParcelableであるAospDefinedParcelableを拡張して、付加価値機能を含めることができると期待しているイメージ。

以前はParcelableHolderがなければ、フィールドを追加するとエラーになるため、デバイス実装者は AOSP 定義の安定した AIDL インターフェイスを変更できませんでした。

parcelable AospDefinedParcelable {
  int a;
  String b;
  String x; // ERROR: added by a device implementer
  int[] y; // added by a device implementer
}

前のコードに見られるように、Android の次のリリースで Parcelable が改訂されたときに、デバイスの実装者によって追加されたフィールドが競合する可能性があるため、このプラクティスは破られています。

ParcelableHolderを使用すると、Parcelable の所有者はParcelableの拡張ポイントを定義できます。

parcelable AospDefinedParcelable {
  int a;
  String b;
  ParcelableHolder extension;
}

次に、デバイスの実装者は、拡張用に独自のParcelableを定義できます。

parcelable OemDefinedParcelable {
  String x;
  int[] y;
}

最後に、 ParcelableHolderフィールドを介して、新しいParcelableを元のParcelableにアタッチできます。


// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;

ap.extension.setParcelable(op);

...

OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);

// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();

ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);

...

std::shared_ptr<OemDefinedParcelable> op_ptr;

ap.extension.getParcelable(&op_ptr);

// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);

...

std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);

// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });

ap.extension.set_parcelable(Rc::clone(&op));

...

let op = ap.extension.get_parcelable::<OemDefinedParcelable>();

AIDL ランタイムに対するビルド

AIDL には、Java、NDK、CPP の 3 つの異なるバックエンドがあります。 Stable AIDL を使用するには、常にsystem/lib*/libbinder.soにある libbinder のシステム コピーを使用し、 /dev/binderで話す必要があります。ベンダー イメージのコードの場合、これはlibbinder (VNDK から) を使用できないことを意味します。このライブラリには、不安定な C++ API と不安定な内部が含まれています。代わりに、ネイティブ ベンダー コードは AIDL の NDK バックエンドを使用し、 libbinder_ndk (システムlibbinder.soによってサポートされている) に対してリンクし、 aidl_interfaceエントリによって作成された-ndk_platformライブラリに対してリンクする必要があります。

AIDL HAL サーバー インスタンス名

慣例により、AIDL HAL サービスのインスタンス名は$package.$type/$instanceの形式になります。たとえば、バイブレーター HAL のインスタンスはandroid.hardware.vibrator.IVibrator/defaultとして登録されます。

AIDL HAL サーバーの作成

@VintfStabilityサーバーは、VINTF マニフェストで宣言する必要があります。たとえば、次のようになります。

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

それ以外の場合は、通常どおり AIDL サービスを登録する必要があります。 VTS テストを実行する場合、宣言されたすべての AIDL HAL が利用可能であることが期待されます。

AIDL クライアントの作成

AIDL クライアントは、たとえば次のように、互換性マトリックスで自分自身を宣言する必要があります。

    <hal format="aidl" optional="true">
        <name>android.hardware.vibrator</name>
        <version>1-2</version>
        <interface>
            <name>IVibrator</name>
            <instance>default</instance>
        </interface>
    </hal>

既存の HAL を HIDL から AIDL に変換する

hidl2aidlツールを使用して、HIDL インターフェースを AIDL に変換します。

hidl2aidl機能:

  • 指定されたパッケージの.halファイルに基づいて.aidlファイルを作成します
  • すべてのバックエンドを有効にして、新しく作成された AIDL パッケージのビルド ルールを作成する
  • HIDL 型から AIDL 型に変換するための変換メソッドを Java、CPP、NDK バックエンドで作成する
  • 必要な依存関係を持つ翻訳ライブラリのビルド ルールを作成する
  • 静的アサートを作成して、HIDL および AIDL 列挙子が CPP および NDK バックエンドで同じ値を持つようにします

次の手順に従って、.hal ファイルのパッケージを .aidl ファイルに変換します。

  1. system/tools/hidl/hidl2aidlにあるツールをビルドします。

    このツールを最新のソースからビルドすると、最も完全なエクスペリエンスが提供されます。最新バージョンを使用して、以前のリリースから古いブランチのインターフェイスを変換できます。

    m hidl2aidl
    
  2. 出力ディレクトリの後に変換するパッケージを指定してツールを実行します。

    オプションで、 -l引数を使用して、新しいライセンス ファイルの内容を生成されたすべてのファイルの先頭に追加します。必ず正しいライセンスと日付を使用してください。

    hidl2aidl -o <output directory> -l <file with license> <package>
    

    例えば:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
    
  3. 生成されたファイルを読み、変換に関する問題を修正します。

    • conversion.logには、最初に修正する未処理の問題が含まれています。
    • 生成され.aidlファイルには、対処が必要な警告や提案が含まれている場合があります。これらのコメントは//で始まります。
    • この機会に、パッケージをクリーンアップして改善してください。
    • toStringequalsなど、必要になる可能性がある機能については、 @JavaDeriveアノテーションを確認してください。
  4. 必要なターゲットだけをビルドします。

    • 使用しないバックエンドを無効にします。 CPP バックエンドよりも NDK バックエンドを優先します。「 runtime の選択」を参照してください。
    • 使用されない翻訳ライブラリまたはその生成コードを削除します。
  5. AIDL と HIDL の主な違いを参照してください。

    • 通常、AIDL の組み込みStatusと例外を使用すると、インターフェースが改善され、別のインターフェース固有のステータス タイプが不要になります。
    • メソッドの AIDL インターフェース引数は、HIDL のようにデフォルトで@nullableではありません。

AIDL HAL のセポリシー

ベンダー コードに表示される AIDL サービス タイプには、 hal_service_type属性が必要です。それ以外の場合、sepolicy 構成は他の AIDL サービスと同じです (ただし、HAL には特別な属性があります)。以下は、HAL サービス コンテキストの定義例です。

    type hal_foo_service, service_manager_type, hal_service_type;

プラットフォームで定義されたほとんどのサービスでは、正しいタイプのサービス コンテキストがすでに追加されています(たとえば、 android.hardware.foo.IFoo/defaultはすでにhal_foo_serviceとしてマークされています)。ただし、フレームワーク クライアントが複数のインスタンス名をサポートしている場合は、追加のインスタンス名をデバイス固有のservice_contextsファイルに追加する必要があります。

    android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0

新しいタイプの HAL を作成するときは、HAL 属性を追加する必要があります。特定の HAL 属性が複数のサービス タイプに関連付けられている場合があります (前述のように、それぞれが複数のインスタンスを持つ場合があります)。 HAL fooの場合、 hal_attribute(foo)があります。このマクロは、属性hal_foo_clientおよびhal_foo_serverを定義します。特定のドメインに対して、 hal_client_domainおよびhal_server_domainマクロは、ドメインを特定の HAL 属性に関連付けます。たとえば、この HAL のクライアントであるシステム サーバーは、ポリシーhal_client_domain(system_server, hal_foo)対応します。同様に、HAL サーバーにはhal_server_domain(my_hal_domain, hal_foo)が含まれます。通常、特定の HAL 属性に対して、参照用またはサンプル HAL 用にhal_foo_defaultのようなドメインも作成します。ただし、一部のデバイスはこれらのドメインを独自のサーバーに使用します。複数のサーバーのドメインを区別することは、同じインターフェースを提供し、実装で異なる権限セットを必要とする複数のサーバーがある場合にのみ問題になります。これらすべてのマクロで、 hal_fooは実際には sepolicy オブジェクトではありません。代わりに、このトークンはこれらのマクロによって使用され、クライアント サーバー ペアに関連付けられた属性のグループを参照します。

ただし、これまでのところ、 hal_foo_servicehal_foo ( hal_attribute(foo)の属性ペア) を関連付けていません。 HAL 属性は、 hal_attribute_serviceマクロを使用して AIDL HAL サービスに関連付けられます(HIDL HAL はhal_attribute_hwserviceマクロを使用します)。たとえば、 hal_attribute_service(hal_foo, hal_foo_service)です。これは、 hal_foo_clientプロセスが HAL を取得でき、 hal_foo_serverプロセスが HAL を登録できることを意味します。これらの登録規則の適用は、コンテキスト マネージャー ( servicemanager ) によって行われます。サービス名が常に HAL 属性に対応しているとは限らないことに注意してください。たとえば、 hal_attribute_service(hal_foo, hal_foo2_service)が表示される場合があります。ただし、一般に、これはサービスが常に一緒に使用されることを意味するため、hal_foo2_service をhal_foo_serviceして、すべてのサービス コンテキストにhal_foo2_serviceを使用することを検討できます。複数のhal_attribute_serviceを設定するほとんどの HAL は、元の HAL 属性名が十分に一般的ではなく、変更できないためです。

これらをすべてまとめると、HAL の例は次のようになります。

    public/attributes:
    // define hal_foo, hal_foo_client, hal_foo_server
    hal_attribute(foo)

    public/service.te
    // define hal_foo_service
    type hal_foo_service, hal_service_type, protected_service, service_manager_type

    public/hal_foo.te:
    // allow binder connection from client to server
    binder_call(hal_foo_client, hal_foo_server)
    // allow client to find the service, allow server to register the service
    hal_attribute_service(hal_foo, hal_foo_service)
    // allow binder communication from server to service_manager
    binder_use(hal_foo_server)

    private/service_contexts:
    // bind an AIDL service name to the selinux type
    android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0

    private/<some_domain>.te:
    // let this domain use the hal service
    binder_use(some_domain)
    hal_client_domain(some_domain, hal_foo)

    vendor/<some_hal_server_domain>.te
    // let this domain serve the hal service
    hal_server_domain(some_hal_server_domain, hal_foo)

付属の拡張インターフェース

拡張機能は、サービス マネージャーに直接登録された最上位のインターフェイスであるか、サブインターフェイスであるかに関係なく、任意のバインダー インターフェイスに接続できます。拡張機能を取得するときは、拡張機能のタイプが期待どおりであることを確認する必要があります。拡張子は、バインダーを提供するプロセスからのみ設定できます。

拡張機能が既存の HAL の機能を変更する場合は常に、添付された拡張機能を使用する必要があります。まったく新しい機能が必要な場合は、このメカニズムを使用する必要はなく、拡張インターフェイスをサービス マネージャーに直接登録できます。接続された拡張インターフェイスは、サブインターフェイスに接続されている場合に最も意味があります。これらの階層は深いか、複数インスタンス化されている可能性があるためです。グローバル拡張機能を使用して別のサービスのバインダー インターフェース階層をミラーリングすると、直接接続された拡張機能と同等の機能を提供するために大規模な簿記が必要になります。

バインダーに拡張機能を設定するには、次の API を使用します。

  • NDK バックエンド: AIBinder_setExtension
  • Java バックエンド: android.os.Binder.setExtension
  • CPP バックエンド: android::Binder::setExtension

バインダーで拡張機能を取得するには、次の API を使用します。

  • NDK バックエンド: AIBinder_getExtension
  • Java バックエンド: android.os.IBinder.getExtension
  • CPP バックエンド: android::IBinder::getExtension

これらの API の詳細については、対応するバックエンドのgetExtension関数のドキュメントを参照してください。拡張機能の使用例は、 hardware/interfaces/tests/extension/vibratorにあります。

AIDL と HIDL の主な違い

AIDL HAL または AIDL HAL インターフェースを使用する場合は、HIDL HAL の記述との違いに注意してください。

  • AIDL 言語の構文は Java に近いです。 HIDL 構文は C++ に似ています。
  • すべての AIDL インターフェースには、組み込みのエラー ステータスがあります。カスタム ステータス タイプを作成する代わりに、インターフェイス ファイルで定数ステータス int を作成し、CPP/NDK バックエンドでEX_SERVICE_SPECIFICを使用し、Java バックエンドでServiceSpecificExceptionを使用します。エラー処理を参照してください。
  • AIDL は、バインダー オブジェクトが送信されたときにスレッドプールを自動的に開始しません。それらは手動で開始する必要があります (スレッド管理を参照)。
  • AIDL は、チェックされていないトランスポート エラーで中止されません(HIDL Returnは、チェックされていないエラーで中止されます)。
  • AIDL は、ファイルごとに 1 つのタイプのみを宣言できます。
  • AIDL 引数は、出力パラメーターに加えて、in/out/inout として指定できます (「同期コールバック」はありません)。
  • AIDL は、ハンドルの代わりに fd をプリミティブ型として使用します。
  • HIDL は、互換性のない変更にはメジャー バージョンを使用し、互換性のある変更にはマイナー バージョンを使用します。 AIDL では、下位互換性のある変更が適切に行われます。 AIDL には、メジャー バージョンの明確な概念はありません。代わりに、これはパッケージ名に組み込まれます。たとえば、AIDL はパッケージ名bluetooth2を使用する場合があります。
  • デフォルトでは、AIDL はリアルタイムの優先度を継承しません。 setInheritRt関数をバインダーごとに使用して、リアルタイムの優先順位の継承を有効にする必要があります。