AIDL cho HAL

Android 11 giới thiệu khả năng sử dụng AIDL cho HAL trong Android, giúp bạn có thể triển khai các phần của Android mà không cần HIDL. Chuyển đổi HAL để chỉ sử dụng AIDL nếu có thể (khi HAL ngược dòng sử dụng HIDL, thì phải sử dụng HIDL).

Các HAL sử dụng AIDL để giao tiếp giữa các thành phần khung (chẳng hạn như các thành phần trong system.img) và các thành phần phần cứng (chẳng hạn như các thành phần trong vendor.img) phải sử dụng AIDL ổn định. Tuy nhiên, để giao tiếp trong một phân vùng, chẳng hạn như từ HAL này sang HAL khác, không có hạn chế nào đối với cơ chế IPC cần sử dụng.

Động lực

AIDL đã xuất hiện lâu hơn HIDL và được dùng ở nhiều nơi khác, chẳng hạn như giữa các thành phần của khung Android hoặc trong các ứng dụng. Giờ đây, AIDL đã có tính năng hỗ trợ độ ổn định, bạn có thể triển khai toàn bộ ngăn xếp bằng một thời gian chạy IPC duy nhất. AIDL cũng có hệ thống kiểm soát phiên bản tốt hơn HIDL. Sau đây là một số ưu điểm của AIDL:

  • Việc sử dụng một ngôn ngữ IPC duy nhất có nghĩa là bạn chỉ cần học, gỡ lỗi, tối ưu hoá và bảo mật một thứ.
  • AIDL hỗ trợ việc tạo phiên bản tại chỗ cho chủ sở hữu của một giao diện:
    • Chủ sở hữu có thể thêm các phương thức vào cuối giao diện hoặc các trường vào các đối tượng có thể chuyển đổi thành gói. Điều này có nghĩa là việc tạo phiên bản mã dễ dàng hơn theo thời gian và chi phí theo từng năm cũng thấp hơn (các loại có thể được sửa đổi tại chỗ và không cần thêm thư viện cho mỗi phiên bản giao diện).
    • Bạn có thể đính kèm các giao diện tiện ích trong thời gian chạy thay vì trong hệ thống loại, nên không cần phải đặt lại các tiện ích hạ lưu trên các phiên bản giao diện mới hơn.
  • Bạn có thể sử dụng trực tiếp một giao diện AIDL hiện có khi chủ sở hữu của giao diện đó chọn ổn định giao diện. Trước đây, bạn phải tạo toàn bộ bản sao của giao diện trong HIDL.

Tạo dựa trên thời gian chạy AIDL

AIDL có 3 chương trình phụ trợ: Java, NDK và CPP. Để sử dụng AIDL ổn định, hãy luôn sử dụng bản sao hệ thống của libbinder tại system/lib*/libbinder.so và nói về /dev/binder. Đối với mã trên hình ảnh vendor, điều này có nghĩa là bạn không thể dùng libbinder (từ VNDK): thư viện này có API C++ không ổn định và các thành phần bên trong không ổn định. Thay vào đó, mã nhà cung cấp gốc phải sử dụng phần phụ trợ NDK của AIDL, liên kết với libbinder_ndk (được hỗ trợ bởi libbinder.so hệ thống) và liên kết với các thư viện NDK do các mục aidl_interface tạo. Để biết chính xác tên mô-đun, hãy xem Quy tắc đặt tên cho mô-đun.

Viết giao diện AIDL HAL

Để giao diện AIDL được dùng giữa hệ thống và nhà cung cấp, giao diện này cần có 2 thay đổi:

  • Mọi định nghĩa loại đều phải được chú thích bằng @VintfStability.
  • Nội dung khai báo aidl_interface cần có stability: "vintf",.

Chỉ chủ sở hữu của một giao diện mới có thể thực hiện những thay đổi này.

Khi bạn thực hiện những thay đổi này, giao diện phải nằm trong tệp kê khai VINTF thì mới hoạt động. Kiểm thử yêu cầu này (và các yêu cầu liên quan, chẳng hạn như xác minh rằng các giao diện đã phát hành được cố định) bằng kiểm thử Bộ kiểm thử của nhà cung cấp (VTS) vts_treble_vintf_vendor_test. Bạn có thể sử dụng giao diện @VintfStability mà không cần các yêu cầu này bằng cách gọi AIBinder_forceDowngradeToLocalStability trong phần phụ trợ NDK, android::Stability::forceDowngradeToLocalStability trong phần phụ trợ C++ hoặc android.os.Binder#forceDowngradeToSystemStability trong phần phụ trợ Java trên một đối tượng liên kết trước khi đối tượng đó được gửi đến một quy trình khác.

Ngoài ra, để có khả năng di chuyển mã tối đa và tránh các vấn đề tiềm ẩn như các thư viện bổ sung không cần thiết, hãy tắt phần phụ trợ CPP.

Đoạn mã này cho biết cách tắt phần phụ trợ CPP:

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

Tìm các giao diện HAL AIDL

Các giao diện AIDL ổn định của AOSP cho HAL nằm trong các thư mục aidl trong cùng thư mục cơ sở với các giao diện HIDL:

  • hardware/interfaces là dành cho các giao diện thường do phần cứng cung cấp.
  • frameworks/hardware/interfaces là dành cho các giao diện cấp cao được cung cấp cho phần cứng.
  • system/hardware/interfaces là dành cho các giao diện cấp thấp được cung cấp cho phần cứng.

Đặt các giao diện tiện ích vào các thư mục con hardware/interfaces khác trong vendor hoặc hardware.

Giao diện tiện ích

Android có một bộ giao diện AOSP chính thức trong mỗi bản phát hành. Khi muốn thêm các chức năng vào những giao diện này, các đối tác Android không nên thay đổi trực tiếp những giao diện này vì điều này khiến thời gian chạy Android của họ không tương thích với thời gian chạy Android AOSP. Tránh thay đổi các giao diện này để hình ảnh GSI có thể tiếp tục hoạt động.

Tiện ích có thể đăng ký theo hai cách:

Tuy nhiên, bất kể tiện ích nào được đăng ký, khi các thành phần dành riêng cho nhà cung cấp (tức là không thuộc AOSP nguồn) sử dụng giao diện, thì không thể xảy ra xung đột hợp nhất. Tuy nhiên, khi các thành phần AOSP nguồn được sửa đổi ở hạ nguồn, có thể xảy ra xung đột hợp nhất và bạn nên áp dụng các chiến lược sau:

  • Đưa các phần bổ sung giao diện lên AOSP trong bản phát hành tiếp theo.
  • Các điểm bổ sung cho giao diện nguồn cho phép tăng tính linh hoạt (không có xung đột hợp nhất) trong bản phát hành tiếp theo.

Các đối tượng có thể chuyển đổi thành gói tiện ích: ParcelableHolder

ParcelableHolder là một thực thể của giao diện Parcelable có thể chứa một thực thể khác của Parcelable.

Trường hợp sử dụng chính của ParcelableHolder là để giúp Parcelable có thể mở rộng. Ví dụ: hình ảnh mà nhà triển khai thiết bị mong muốn có thể mở rộng Parcelable, AospDefinedParcelable do AOSP xác định để bao gồm các tính năng gia tăng giá trị của họ.

Sử dụng giao diện ParcelableHolder để mở rộng Parcelable bằng các tính năng gia tăng giá trị của bạn. Giao diện ParcelableHolder chứa một thực thể của Parcelable. Nếu bạn cố gắng thêm các trường trực tiếp vào Parcelable, thì sẽ xảy ra lỗi:

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

Như bạn thấy trong mã trước đó, phương pháp này không còn hiệu quả vì các trường do người triển khai thiết bị thêm vào có thể xảy ra xung đột khi Parcelable được sửa đổi trong các bản phát hành tiếp theo của Android.

Bằng cách sử dụng ParcelableHolder, chủ sở hữu của một đối tượng có thể chuyển đổi thành đối tượng có thể chuyển đổi tuần tự có thể xác định một điểm mở rộng trong một thực thể của Parcelable:

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

Sau đó, các đơn vị triển khai thiết bị có thể xác định phiên bản Parcelable riêng cho tiện ích của họ:

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

Bạn có thể đính kèm phiên bản Parcelable mới vào Parcelable ban đầu bằng trường ParcelableHolder:


// 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>();

Tên phiên bản máy chủ HAL AIDL

Theo quy ước, các dịch vụ AIDL HAL có tên thực thể theo định dạng $package.$type/$instance. Ví dụ: một phiên bản của HAL bộ rung được đăng ký dưới dạng android.hardware.vibrator.IVibrator/default.

Viết một máy chủ HAL AIDL

@VintfStability Các máy chủ AIDL phải được khai báo trong tệp kê khai VINTF, ví dụ:

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

Nếu không, họ sẽ phải đăng ký dịch vụ AIDL như bình thường. Khi chạy các kiểm thử VTS, dự kiến tất cả HAL AIDL đã khai báo đều có sẵn.

Viết ứng dụng AIDL

Các ứng dụng AIDL phải khai báo chính chúng trong ma trận tương thích, ví dụ:

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

Chuyển đổi HAL hiện có từ HIDL sang AIDL

Dùng công cụ hidl2aidl để chuyển đổi giao diện HIDL thành AIDL.

Các tính năng của hidl2aidl:

  • Tạo tệp AIDL (.aidl) dựa trên tệp HAL (.hal) cho gói đã cho.
  • Tạo quy tắc xây dựng cho gói AIDL mới tạo với tất cả các phần phụ trợ được bật.
  • Tạo các phương thức dịch trong phần phụ trợ Java, CPP và NDK để dịch từ các loại HIDL sang các loại AIDL.
  • Tạo quy tắc xây dựng cho các thư viện dịch có phần phụ thuộc bắt buộc.
  • Tạo các câu lệnh xác nhận tĩnh để đảm bảo rằng các trình liệt kê HIDL và AIDL có cùng giá trị trong các phần phụ trợ CPP và NDK.

Hãy làm theo các bước sau để chuyển đổi một gói tệp HAL thành tệp AIDL:

  1. Tạo công cụ nằm trong system/tools/hidl/hidl2aidl.

    Việc tạo công cụ này từ nguồn mới nhất mang đến trải nghiệm hoàn chỉnh nhất. Bạn có thể dùng phiên bản mới nhất để chuyển đổi các giao diện trên các nhánh cũ từ các bản phát hành trước:

    m hidl2aidl
  2. Thực thi công cụ bằng một thư mục đầu ra, theo sau là gói cần chuyển đổi.

    Bạn có thể dùng đối số -l để thêm nội dung của một tệp giấy phép mới vào đầu tất cả các tệp được tạo. Hãy nhớ sử dụng đúng giấy phép và ngày:

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

    Ví dụ:

    hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
  3. Đọc các tệp được tạo và khắc phục mọi vấn đề về việc chuyển đổi:

    • conversion.log có chứa mọi vấn đề chưa được xử lý mà bạn cần khắc phục trước.
    • Các tệp AIDL được tạo có thể có cảnh báo và đề xuất cần được xử lý. Những bình luận này bắt đầu bằng //.
    • Dọn dẹp và cải thiện gói.
    • Kiểm tra chú thích @JavaDerive cho các tính năng có thể cần thiết, chẳng hạn như toString hoặc equals.
  4. Chỉ tạo những mục tiêu bạn cần:

    • Tắt những phần phụ trợ không được dùng. Ưu tiên phần phụ trợ NDK hơn phần phụ trợ CPP; xem phần Tạo dựa trên thời gian chạy AIDL.
    • Xoá các thư viện dịch hoặc bất kỳ mã nào được tạo của các thư viện đó mà bạn sẽ không dùng.
  5. Xem phần Những điểm khác biệt chính giữa AIDL và HIDL:

    • Việc sử dụng Status và các trường hợp ngoại lệ tích hợp của AIDL thường cải thiện giao diện và loại bỏ nhu cầu về một loại trạng thái khác dành riêng cho giao diện.
    • Theo mặc định, các đối số giao diện AIDL trong các phương thức không phải là @nullable như trong HIDL.

SEPolicy cho HAL AIDL

Loại dịch vụ AIDL mà mã nhà cung cấp có thể thấy phải có thuộc tính hal_service_type. Nếu không, cấu hình sepolicy sẽ giống như mọi dịch vụ AIDL khác (mặc dù có các thuộc tính đặc biệt cho HAL). Sau đây là một ví dụ về định nghĩa của ngữ cảnh dịch vụ HAL:

    type hal_foo_service, service_manager_type, hal_service_type;

Đối với hầu hết các dịch vụ do nền tảng xác định, một ngữ cảnh dịch vụ có loại chính xác đã được thêm (ví dụ: android.hardware.foo.IFoo/default đã được đánh dấu là hal_foo_service). Tuy nhiên, nếu một ứng dụng khung hỗ trợ nhiều tên phiên bản, thì bạn phải thêm các tên phiên bản bổ sung vào các tệp service_contexts dành riêng cho thiết bị:

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

Khi tạo một loại HAL mới, bạn phải thêm các thuộc tính HAL. Một thuộc tính HAL cụ thể có thể được liên kết với nhiều loại dịch vụ (mỗi loại có thể có nhiều phiên bản như vừa thảo luận). Đối với HAL, foo, có hal_attribute(foo). Macro này xác định các thuộc tính hal_foo_clienthal_foo_server. Đối với một miền nhất định, các macro hal_client_domainhal_server_domain sẽ liên kết một miền với một thuộc tính HAL nhất định. Ví dụ: máy chủ hệ thống là một ứng dụng của HAL này, tương ứng với chính sách hal_client_domain(system_server, hal_foo). Tương tự, máy chủ HAL cũng bao gồm hal_server_domain(my_hal_domain, hal_foo).

Thông thường, đối với một thuộc tính HAL nhất định, hãy tạo một miền như hal_foo_default để tham chiếu hoặc ví dụ về HAL. Tuy nhiên, một số thiết bị sử dụng các miền này cho máy chủ riêng. Việc phân biệt giữa các miền cho nhiều máy chủ chỉ quan trọng nếu có nhiều máy chủ cung cấp cùng một giao diện và cần một bộ quyền khác trong quá trình triển khai. Trong tất cả các macro này, hal_foo không phải là đối tượng sepolicy. Thay vào đó, mã thông báo này được các macro này dùng để tham chiếu đến nhóm thuộc tính được liên kết với một cặp máy chủ ứng dụng.

Tuy nhiên, cho đến nay, hal_foo_servicehal_foo (cặp thuộc tính trong hal_attribute(foo)) chưa được liên kết. Một thuộc tính HAL được liên kết với các dịch vụ HAL AIDL bằng cách sử dụng macro hal_attribute_service (HAL HIDL sử dụng macro hal_attribute_hwservice), ví dụ: hal_attribute_service(hal_foo, hal_foo_service). Điều này có nghĩa là các quy trình hal_foo_client có thể truy cập vào HAL và các quy trình hal_foo_server có thể đăng ký HAL. Việc thực thi các quy tắc đăng ký này do trình quản lý bối cảnh (servicemanager) thực hiện.

Tên dịch vụ có thể không phải lúc nào cũng tương ứng với các thuộc tính HAL, ví dụ: hal_attribute_service(hal_foo, hal_foo2_service). Nhìn chung, vì điều này ngụ ý rằng các dịch vụ luôn được dùng cùng nhau, nên bạn có thể xoá hal_foo2_service và dùng hal_foo_service cho tất cả các ngữ cảnh dịch vụ. Khi HAL đặt nhiều phiên bản hal_attribute_service, đó là do tên thuộc tính HAL ban đầu không đủ chung và không thể thay đổi.

Kết hợp tất cả lại với nhau, một ví dụ về HAL sẽ có dạng như sau:

    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)

Các giao diện tiện ích được đính kèm

Bạn có thể đính kèm một tiện ích vào bất kỳ giao diện liên kết nào, cho dù đó là giao diện cấp cao nhất được đăng ký trực tiếp với trình quản lý dịch vụ hay đó là một giao diện phụ. Khi nhận được một tiện ích, bạn phải xác nhận loại tiện ích đó đúng như dự kiến. Bạn chỉ có thể thiết lập các tiện ích từ quy trình phân phát một đối tượng liên kết.

Sử dụng các tiện ích đính kèm bất cứ khi nào một tiện ích sửa đổi chức năng của HAL hiện có. Khi cần có một chức năng hoàn toàn mới, bạn không cần dùng cơ chế này và có thể đăng ký trực tiếp một giao diện tiện ích với trình quản lý dịch vụ. Các giao diện tiện ích đính kèm có ý nghĩa nhất khi được đính kèm vào các giao diện phụ, vì các hệ thống phân cấp này có thể sâu hoặc có nhiều phiên bản. Việc sử dụng một tiện ích chung để phản chiếu hệ phân cấp giao diện liên kết của một dịch vụ khác đòi hỏi phải có sổ sách kế toán mở rộng để cung cấp các chức năng tương đương cho các tiện ích được đính kèm trực tiếp.

Để đặt một tiện ích trên một đối tượng liên kết, hãy sử dụng các API sau:

  • Phụ trợ NDK: AIBinder_setExtension
  • Phụ trợ Java: android.os.Binder.setExtension
  • Phụ trợ CPP: android::Binder::setExtension
  • Phần phụ trợ Rust: binder::Binder::set_extension

Để nhận một tiện ích trên một đối tượng liên kết, hãy sử dụng các API sau:

  • Phụ trợ NDK: AIBinder_getExtension
  • Phụ trợ Java: android.os.IBinder.getExtension
  • Phụ trợ CPP: android::IBinder::getExtension
  • Phần phụ trợ Rust: binder::Binder::get_extension

Bạn có thể tìm thêm thông tin về các API này trong tài liệu về hàm getExtension trong phần phụ trợ tương ứng. Ví dụ về cách sử dụng các tiện ích là trong hardware/interfaces/tests/extension/vibrator.

Những điểm khác biệt chính giữa AIDL và HIDL

Khi sử dụng HAL AIDL hoặc giao diện HAL AIDL, hãy lưu ý đến những điểm khác biệt so với việc viết HAL HIDL.

  • Cú pháp của ngôn ngữ AIDL gần với Java hơn. Cú pháp HIDL tương tự như C++.
  • Tất cả giao diện AIDL đều có trạng thái lỗi tích hợp. Thay vì tạo các loại trạng thái tuỳ chỉnh, hãy tạo các số nguyên trạng thái hằng số trong tệp giao diện và sử dụng EX_SERVICE_SPECIFIC trong các phần phụ trợ CPP và NDK, cũng như ServiceSpecificException trong phần phụ trợ Java. Xem phần Xử lý lỗi.
  • AIDL không tự động khởi động các nhóm luồng khi các đối tượng liên kết được gửi. Bạn phải bắt đầu các luồng này theo cách thủ công (xem phần Quản lý luồng).
  • AIDL không huỷ bỏ khi có lỗi truyền tải chưa được kiểm tra (HIDL Return huỷ bỏ khi có lỗi chưa được kiểm tra).
  • AIDL chỉ có thể khai báo một loại cho mỗi tệp.
  • Bạn có thể chỉ định đối số AIDL là in, out hoặc inout ngoài tham số đầu ra (không có lệnh gọi lại đồng bộ).
  • AIDL sử dụng fd làm kiểu nguyên thuỷ thay vì handle.
  • HIDL sử dụng phiên bản chính cho các thay đổi không tương thích và phiên bản phụ cho các thay đổi tương thích. Trong AIDL, các thay đổi tương thích ngược được thực hiện tại chỗ. AIDL không có khái niệm rõ ràng về phiên bản chính; thay vào đó, khái niệm này được đưa vào tên gói. Ví dụ: AIDL có thể sử dụng tên gói bluetooth2.
  • Theo mặc định, AIDL không kế thừa mức độ ưu tiên theo thời gian thực. Bạn phải dùng hàm setInheritRt cho mỗi liên kết để bật tính năng kế thừa mức độ ưu tiên theo thời gian thực.

Kiểm thử cho HAL

Phần này mô tả các phương pháp hay nhất để kiểm thử HAL. Những phương pháp này vẫn hợp lệ ngay cả khi kiểm thử tích hợp cho HAL của bạn không có trong VTS.

Android dựa vào VTS để xác minh các hoạt động triển khai HAL dự kiến. VTS giúp đảm bảo Android có thể tương thích ngược với các chế độ triển khai cũ của nhà cung cấp. Những hoạt động triển khai không vượt qua VTS có các vấn đề đã biết về khả năng tương thích, có thể khiến các hoạt động này không hoạt động được với các phiên bản hệ điều hành trong tương lai.

VTS cho HAL có 2 phần chính.

1. Xác minh rằng Android biết và dự kiến các HAL trên thiết bị

Android dựa vào danh sách tĩnh và chính xác về tất cả HAL đã cài đặt. Danh sách này được thể hiện trong Tệp kê khai VINTF. Các kiểm thử đặc biệt trên toàn nền tảng sẽ xác minh tính toàn vẹn của các lớp HAL trong toàn bộ hệ thống. Trước khi viết bất kỳ kiểm thử nào dành riêng cho HAL, bạn cũng nên chạy các kiểm thử này vì chúng có thể cho biết liệu HAL có cấu hình VINTF không nhất quán hay không.

Bạn có thể tìm thấy bộ kiểm thử này trong test/vts-testcase/hal/treble/vintf. Nếu bạn đang triển khai HAL của nhà cung cấp, hãy sử dụng vts_treble_vintf_vendor_test để xác minh. Bạn có thể chạy kiểm thử này bằng lệnh atest vts_treble_vintf_vendor_test.

Các kiểm thử này chịu trách nhiệm xác minh:

  • Mọi giao diện @VintfStability được khai báo trong tệp kê khai VINTF đều được cố định ở một phiên bản đã phát hành xác định. Thao tác này xác minh cả hai phía của giao diện đều đồng ý về định nghĩa chính xác của phiên bản giao diện đó. Đây là điều kiện cần thiết cho hoạt động cơ bản.
  • Tất cả HAL được khai báo trong tệp kê khai VINTF đều có trên thiết bị đó. Mọi ứng dụng có đủ quyền sử dụng một dịch vụ HAL đã khai báo đều phải có thể nhận và sử dụng các dịch vụ đó bất cứ lúc nào.
  • Tất cả HAL được khai báo trong tệp kê khai VINTF đều đang phân phát phiên bản của giao diện mà chúng khai báo trong tệp kê khai.
  • Không có HAL nào không được dùng nữa đang được cung cấp trên thiết bị. Android ngừng hỗ trợ các phiên bản thấp hơn của giao diện HAL như mô tả trong vòng đời FCM.
  • Các HAL bắt buộc có trên thiết bị. Android cần một số HAL để hoạt động đúng cách.

2. Xác minh hành vi dự kiến của từng HAL

Mỗi giao diện HAL đều có các kiểm thử VTS riêng để xác minh hành vi dự kiến của các ứng dụng. Các trường hợp kiểm thử chạy trên mọi phiên bản của giao diện HAL đã khai báo và thực thi hành vi cụ thể dựa trên phiên bản của giao diện được triển khai.

Trong C++, bạn có thể nhận được danh sách mọi HAL được cài đặt trên hệ thống bằng hàm android::getAidlHalInstanceNames trong libaidlvintf_gtest_helper. Trong Rust, hãy sử dụng binder::get_declared_instances.

Các kiểm thử này cố gắng bao gồm mọi khía cạnh của việc triển khai HAL mà khung Android dựa vào hoặc có thể dựa vào trong tương lai.

Các thử nghiệm này bao gồm việc xác minh khả năng hỗ trợ các tính năng, xử lý lỗi và mọi hành vi khác mà ứng dụng có thể mong đợi từ dịch vụ.

Các cột mốc VTS để phát triển HAL

Bạn nên cập nhật các bài kiểm thử VTS (hoặc mọi bài kiểm thử) khi tạo hoặc sửa đổi các giao diện HAL của Android.

Bạn phải hoàn tất các kiểm thử VTS và sẵn sàng xác minh việc triển khai của nhà cung cấp trước khi các kiểm thử này được cố định cho các bản phát hành Android Vendor API. Các giao diện này phải sẵn sàng trước khi bị đóng băng để nhà phát triển có thể tạo các triển khai, xác minh và cung cấp ý kiến phản hồi cho nhà phát triển giao diện HAL.

Kiểm thử trên Cuttlefish

Khi không có phần cứng, Android sẽ dùng Cuttlefish làm phương tiện phát triển cho các giao diện HAL. Điều này cho phép kiểm thử tích hợp có thể mở rộng của Android.

hal_implementation_test kiểm thử rằng Cuttlefish có các chế độ triển khai phiên bản giao diện HAL mới nhất để đảm bảo Android sẵn sàng xử lý các giao diện mới và các kiểm thử VTS sẵn sàng kiểm thử các chế độ triển khai mới của nhà cung cấp ngay khi có phần cứng và thiết bị mới.