AIDL ổn định

Android 10 bổ sung tính năng hỗ trợ cho Ngôn ngữ định nghĩa giao diện Android (AIDL) ổn định, một cách mới để theo dõi giao diện chương trình ứng dụng (API)/giao diện nhị phân của ứng dụng (ABI) do giao diện AIDL cung cấp. AIDL ổn định có những điểm khác biệt chính sau đây so với AIDL:

  • Giao diện được xác định trong hệ thống xây dựng bằng aidl_interfaces.
  • Giao diện chỉ có thể chứa dữ liệu có cấu trúc. Các Parcelable đại diện cho các loại mong muốn được tạo tự động dựa trên định nghĩa AIDL của chúng và được tự động tổng hợp và tách biệt.
  • Bạn có thể khai báo giao diện là ổn định (tương thích ngược). Khi điều này xảy ra, API của họ sẽ được theo dõi và tạo phiên bản trong một tệp bên cạnh giao diện AIDL.

AIDL có cấu trúc so với AIDL ổn định

AIDL có cấu trúc đề cập đến các loại được xác định hoàn toàn trong AIDL. Ví dụ: nội dung khai báo theo gói (gói tuỳ chỉnh) không có cấu trúc AIDL. Gói bưu kiện có các trường được xác định trong AIDL được gọi là gói bưu kiện có cấu trúc.

AIDL ổn định yêu cầu AIDL có cấu trúc để hệ thống xây dựng và trình biên dịch có thể hiểu được liệu những thay đổi được thực hiện đối với các gói có khả năng tương thích ngược hay không. Tuy nhiên, không phải giao diện có cấu trúc nào cũng ổn định. Để ổn định, giao diện chỉ được sử dụng các kiểu có cấu trúc và cũng phải sử dụng các tính năng tạo phiên bản sau. Ngược lại, một giao diện sẽ không ổn định nếu hệ thống xây dựng cốt lõi được dùng để tạo giao diện đó hoặc nếu bạn đặt unstable:true.

Xác định giao diện AIDL

Định nghĩa của aidl_interface sẽ có dạng như sau:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name: Tên của mô-đun giao diện AIDL xác định duy nhất một giao diện AIDL.
  • srcs: Danh sách các tệp nguồn AIDL cấu thành giao diện. Đường dẫn cho loại AIDL Foo được xác định trong gói com.acme phải tại <base_path>/com/acme/Foo.aidl, trong đó <base_path> có thể là bất kỳ thư mục nào liên quan đến thư mục có Android.bp. Trong ví dụ trên, <base_path>srcs/aidl.
  • local_include_dir: Đường dẫn từ nơi tên gói bắt đầu. Hướng dẫn này tương ứng với <base_path> đã giải thích ở trên.
  • imports: Danh sách các mô-đun aidl_interface mà mô-đun này sử dụng. Nếu một trong các giao diện AIDL của bạn sử dụng giao diện hoặc một gói có thể đóng gói từ một aidl_interface khác, hãy đặt tên của giao diện đó tại đây. Đây có thể là tên riêng để chỉ phiên bản mới nhất hoặc tên có hậu tố phiên bản (chẳng hạn như -V1) để chỉ một phiên bản cụ thể. Tính năng chỉ định một phiên bản đã được hỗ trợ kể từ Android 12
  • versions: Các phiên bản trước của giao diện bị treo trong api_dir, Kể từ Android 11, versions sẽ bị treo trong aidl_api/name. Nếu một giao diện không có phiên bản bị treo nào, thì bạn không nên chỉ định và sẽ không kiểm tra khả năng tương thích. Trường này đã được thay thế bằng versions_with_info cho phiên bản 13 trở lên.
  • versions_with_info: Danh sách các bộ dữ liệu, mỗi bộ dữ liệu chứa tên của một phiên bản cố định và một danh sách có các lệnh nhập phiên bản của các mô-đun aidl_Interface khác mà phiên bản aidl_ giao diện này đã nhập. Định nghĩa của phiên bản V của IFACE giao diện AIDL nằm tại aidl_api/IFACE/V. Trường này ra mắt từ Android 13 và bạn không nên sửa đổi trực tiếp trường này trong Android.bp. Bạn có thể thêm hoặc cập nhật trường này bằng cách gọi *-update-api hoặc *-freeze-api. Ngoài ra, các trường versions sẽ được tự động di chuyển sang versions_with_info khi người dùng gọi *-update-api hoặc *-freeze-api.
  • stability: Cờ tuỳ chọn cho cam kết về độ ổn định của giao diện này. Tính năng này hiện chỉ hỗ trợ "vintf". Nếu bạn không đặt stability, hệ thống xây dựng sẽ kiểm tra để đảm bảo rằng giao diện này có khả năng tương thích ngược, trừ phi bạn chỉ định unstable. Nếu bạn không đặt chính sách này, thì giao diện sẽ có độ ổn định trong ngữ cảnh biên dịch này (vì vậy tất cả nội dung hệ thống, chẳng hạn như trong system.img và các phân vùng liên quan hoặc mọi nội dung của nhà cung cấp, chẳng hạn như nội dung trong vendor.img và các phân vùng liên quan). Nếu bạn đặt stability thành "vintf", thì điều này tương ứng với một lời hứa về độ ổn định: giao diện phải được giữ ổn định miễn là giao diện đó được sử dụng.
  • gen_trace: Cờ không bắt buộc để bật hoặc tắt tính năng theo dõi. Kể từ Android 14, chế độ mặc định là true cho phần phụ trợ cppjava.
  • host_supported: Cờ không bắt buộc mà khi bạn đặt thành true, thì các thư viện đã tạo sẽ được cung cấp cho môi trường lưu trữ.
  • unstable: Cờ không bắt buộc dùng để đánh dấu giao diện này không cần ổn định. Khi bạn đặt thuộc tính này thành true, hệ thống xây dựng sẽ không tạo tệp kết xuất API cho giao diện và cũng không yêu cầu cập nhật.
  • frozen: Cờ không bắt buộc khi được đặt thành true có nghĩa là giao diện không có thay đổi nào kể từ phiên bản giao diện trước đó. Điều này cho phép kiểm tra thời gian xây dựng nhiều hơn. Khi bạn đặt thành false, điều này có nghĩa là giao diện đang trong quá trình phát triển và có các thay đổi mới. Vì vậy, việc chạy foo-freeze-api sẽ tạo ra một phiên bản mới và tự động thay đổi giá trị thành true. Ra mắt trong Android 14.
  • backend.<type>.enabled: Những cờ này bật/tắt từng phần phụ trợ mà trình biên dịch AIDL tạo mã. Hiện tại, có 4 phần phụ trợ được hỗ trợ là Java, C++, NDK và Rust. Các phần phụ trợ Java, C++ và NDK được bật theo mặc định. Nếu không cần bất kỳ phần phụ trợ nào trong số 3 phần phụ trợ này, bạn cần tắt phần phụ trợ đó một cách rõ ràng. Theo mặc định, Rust sẽ bị tắt cho đến Android 15 (thử nghiệm AOSP).
  • backend.<type>.apex_available: Danh sách các tên APEX có sẵn trong thư viện mã giả lập đã tạo.
  • backend.[cpp|java].gen_log: Cờ không bắt buộc kiểm soát việc có tạo thêm mã để thu thập thông tin về giao dịch hay không.
  • backend.[cpp|java].vndk.enabled: Cờ không bắt buộc để biến giao diện này trở thành một phần của VNDK. Mặc định là false.
  • backend.[cpp|ndk].additional_shared_libraries: Ra mắt từ Android 14, cờ này sẽ thêm các phần phụ thuộc vào thư viện gốc. Cờ này hữu ích với ndk_headercpp_header.
  • backend.java.sdk_version: Cờ không bắt buộc để chỉ định phiên bản SDK mà thư viện mã giả lập Java được xây dựng dựa trên đó. Giá trị mặc định là "system_current". Bạn không nên đặt thuộc tính này khi backend.java.platform_apis là true.
  • backend.java.platform_apis: Cờ không bắt buộc nên được đặt thành true khi các thư viện đã tạo cần xây dựng dựa trên API nền tảng thay vì SDK.

Đối với mỗi tổ hợp phiên bản và phần phụ trợ đã bật, một thư viện mã giả lập sẽ được tạo. Để biết cách tham chiếu đến phiên bản cụ thể của thư viện mã giả lập cho một phần phụ trợ cụ thể, hãy xem Quy tắc đặt tên mô-đun.

Ghi tệp AIDL

Các giao diện trong AIDL ổn định cũng tương tự như giao diện truyền thống, chỉ có ngoại lệ là chúng không được phép sử dụng các gói không có cấu trúc (vì các thành phần này không ổn định! Hãy xem phần AIDL có cấu trúc và ổn định). Điểm khác biệt chính trong AIDL ổn định là cách xác định các gói. Trước đây, các gói được khai báo chuyển tiếp; trong AIDL ổn định (và do đó có cấu trúc), các trường và biến của gói đó được xác định rõ ràng.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Chế độ mặc định hiện được hỗ trợ (nhưng không bắt buộc) cho boolean, char, float, double, byte, int, longString. Trong Android 12, giá trị mặc định cho giá trị enum do người dùng xác định cũng được hỗ trợ. Khi giá trị mặc định không được chỉ định, giá trị giống 0 hoặc trống sẽ được sử dụng. Liệt kê không có giá trị mặc định sẽ được khởi tạo về 0 ngay cả khi không có liệt kê bằng 0.

Sử dụng thư viện mã giả lập

Sau khi thêm các thư viện mã giả lập làm phần phụ thuộc cho mô-đun, bạn có thể đưa các thư viện đó vào tệp của mình. Dưới đây là ví dụ về các thư viện mã giả lập trong hệ thống xây dựng (bạn cũng có thể sử dụng Android.mk cho các định nghĩa mô-đun cũ):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

Ví dụ trong C++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Ví dụ trong Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Ví dụ trong Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Giao diện tạo phiên bản

Việc khai báo mô-đun có tên foo cũng sẽ tạo một mục tiêu trong hệ thống xây dựng mà bạn có thể sử dụng để quản lý API của mô-đun. Khi được tạo, foo-freeze-api sẽ thêm định nghĩa API mới trong api_dir hoặc aidl_api/name, tuỳ thuộc vào phiên bản Android và thêm một tệp .hash, cả hai đều đại diện cho phiên bản mới bị treo của giao diện. foo-freeze-api cũng cập nhật thuộc tính versions_with_info để phản ánh phiên bản bổ sung và imports cho phiên bản. Về cơ bản, imports trong versions_with_info được sao chép từ trường imports. Tuy nhiên, phiên bản ổn định mới nhất được chỉ định trong imports trong versions_with_info để nhập dữ liệu không có phiên bản rõ ràng. Sau khi chỉ định thuộc tính versions_with_info, hệ thống xây dựng sẽ chạy kiểm tra khả năng tương thích giữa các phiên bản bị treo cũng như giữa Top of Tree (ToT) với phiên bản bị treo mới nhất.

Ngoài ra, bạn cần quản lý định nghĩa API của phiên bản ToT. Bất cứ khi nào một API được cập nhật, hãy chạy foo-update-api để cập nhật aidl_api/name/current chứa định nghĩa API của phiên bản ToT.

Để duy trì tính ổn định của giao diện, chủ sở hữu có thể thêm mới:

  • Các phương thức để kết thúc một giao diện (hoặc các phương thức có các sê-ri mới được xác định rõ ràng)
  • Các phần tử nằm cuối gói có thể đóng gói (bắt buộc phải thêm giá trị mặc định cho mỗi phần tử)
  • Giá trị không đổi
  • Trong Android 11, enum
  • Trong Android 12, các trường đến cuối kết hợp

Không được phép thực hiện hành động nào khác và không ai khác có thể sửa đổi giao diện (nếu không, chúng có nguy cơ xung đột với các thay đổi mà chủ sở hữu thực hiện).

Để kiểm thử nhằm đảm bảo tất cả giao diện đều bị treo để phát hành, bạn có thể tạo bằng tập hợp các biến môi trường sau:

  • AIDL_FROZEN_REL=true m ... – bản dựng yêu cầu phải cố định tất cả giao diện AIDL ổn định mà không có trường owner: nào được chỉ định.
  • AIDL_FROZEN_OWNERS="aosp test" – bản dựng yêu cầu tất cả giao diện AIDL ổn định phải được cố định bằng trường owner: được chỉ định là "aosp" hoặc "test".

Tính ổn định của quá trình nhập

Việc cập nhật các phiên bản nhập cho các phiên bản bị treo của một giao diện sẽ tương thích ngược ở lớp AIDL ổn định. Tuy nhiên, việc cập nhật các thuộc tính này yêu cầu cập nhật tất cả máy chủ và ứng dụng khách sử dụng phiên bản giao diện cũ, đồng thời một số ứng dụng có thể bị nhầm lẫn khi kết hợp các phiên bản khác nhau của các loại. Nhìn chung, đối với các gói chỉ dùng loại hoặc gói phổ biến, điều này là an toàn vì cần phải viết mã để xử lý các loại không xác định từ các giao dịch IPC.

Trong mã nền tảng Android, android.hardware.graphics.common là ví dụ lớn nhất về hình thức nâng cấp phiên bản này.

Sử dụng giao diện được tạo phiên bản

Phương thức giao diện

Trong thời gian chạy, khi cố gắng gọi các phương thức mới trên một máy chủ cũ, các ứng dụng mới sẽ gặp lỗi hoặc ngoại lệ, tuỳ thuộc vào phần phụ trợ.

  • Phần phụ trợ cpp nhận được ::android::UNKNOWN_TRANSACTION.
  • Phần phụ trợ ndk nhận được STATUS_UNKNOWN_TRANSACTION.
  • Phần phụ trợ java nhận được android.os.RemoteException với thông báo cho biết API chưa được triển khai.

Để biết các chiến lược xử lý việc này, hãy xem các phiên bản truy vấnsử dụng các giá trị mặc định.

Theo gói

Khi bạn thêm các trường mới vào các gói được, các ứng dụng và máy chủ cũ sẽ loại bỏ những trường đó. Khi máy khách và máy chủ mới nhận được các gói dữ liệu cũ, giá trị mặc định cho trường mới sẽ tự động được điền vào. Điều này có nghĩa là các giá trị mặc định cần được chỉ định cho tất cả các trường mới trong một gói.

Ứng dụng không nên trông đợi máy chủ sẽ sử dụng các trường mới trừ phi họ biết máy chủ đang triển khai phiên bản đã xác định trường đó (xem các phiên bản truy vấn).

Enum và hằng số

Tương tự như vậy, nếu thích hợp, ứng dụng và máy chủ nên từ chối hoặc bỏ qua các giá trị hằng số và giá trị liệt kê không được nhận dạng, vì trong tương lai có thể sẽ có thêm nhiều giá trị khác. Ví dụ: một máy chủ không được huỷ khi nhận được một enum mà máy chủ đó không biết. Ứng dụng nên bỏ qua hoặc trả về một giá trị nào đó để ứng dụng biết rằng hàm đó không được hỗ trợ trong quá trình triển khai này.

Liên minh

Việc cố gắng gửi một kết hợp với một trường mới sẽ không thành công nếu receiver đã cũ và không biết về trường đó. Quá trình triển khai sẽ không bao giờ cho thấy sự kết hợp với trường mới. Lỗi sẽ bị bỏ qua nếu đó là giao dịch một chiều; nếu không, lỗi sẽ là BAD_VALUE(đối với phần phụ trợ C++ hoặc NDK) hoặc IllegalArgumentException(đối với phần phụ trợ Java). Lỗi này sẽ xảy ra nếu máy khách đang gửi tập hợp liên kết đến trường mới đến máy chủ cũ hoặc khi máy khách cũ nhận được liên kết hợp nhất từ máy chủ mới.

Phát triển dựa trên cờ

Bạn không thể sử dụng giao diện đang phát triển (không cố định) trên thiết bị phát hành vì chúng không được đảm bảo có khả năng tương thích ngược.

AIDL hỗ trợ tính năng dự phòng thời gian chạy cho các thư viện giao diện không được cố định này để viết mã dựa trên phiên bản không được cố định mới nhất và vẫn được sử dụng trên các thiết bị phát hành. Hành vi tương thích ngược của ứng dụng cũng tương tự như hành vi hiện có và với tính năng dự phòng, việc triển khai cũng cần phải tuân theo các hành vi đó. Xem phần Sử dụng giao diện được tạo phiên bản.

Cờ bản dựng AIDL

Cờ kiểm soát hành vi này được RELEASE_AIDL_USE_UNFROZEN xác định trong build/release/build_flags.bzl. true có nghĩa là phiên bản không được cố định của giao diện sẽ được dùng trong thời gian chạy và false có nghĩa là thư viện của các phiên bản không được cố định đều hoạt động như phiên bản cố định gần đây nhất. Bạn có thể ghi đè cờ này thành true để phát triển cục bộ, nhưng phải chuyển về false trước khi phát hành. Thông thường, quá trình phát triển được thực hiện với cấu hình có cờ được đặt thành true.

Tệp kê khai và ma trận tương thích

Đối tượng giao diện nhà cung cấp (đối tượng VINTF) xác định phiên bản dự kiến và phiên bản được cung cấp ở một trong hai bên của giao diện nhà cung cấp.

Hầu hết các thiết bị không phải là mực ống đều chỉ nhắm đến ma trận tương thích mới nhất sau khi giao diện bị đóng băng, do đó, không có gì khác biệt trong các thư viện AIDL dựa trên RELEASE_AIDL_USE_UNFROZEN.

Ma trận

Giao diện do đối tác sở hữu được thêm vào ma trận tương thích dành riêng cho thiết bị hoặc sản phẩm mà thiết bị nhắm đến trong quá trình phát triển. Vì vậy, khi một phiên bản giao diện mới, không bị cố định được thêm vào ma trận tương thích, các phiên bản bị treo trước đó vẫn cần tồn tại trong RELEASE_AIDL_USE_UNFROZEN=false. Bạn có thể xử lý vấn đề này bằng cách dùng nhiều tệp ma trận tương thích cho nhiều cấu hình RELEASE_AIDL_USE_UNFROZEN hoặc cho phép cả hai phiên bản trong một tệp ma trận tương thích được dùng trong mọi cấu hình.

Ví dụ: khi thêm phiên bản 4 không cố định, hãy sử dụng <version>3-4</version>.

Khi phiên bản 4 bị treo, bạn có thể xoá phiên bản 3 khỏi ma trận tương thích vì phiên bản 4 bị treo được sử dụng khi RELEASE_AIDL_USE_UNFROZENfalse.

Tệp kê khai

Trong Android 15 (thử nghiệm AOSP), một thay đổi trong libvintf được đưa ra để sửa đổi các tệp kê khai tại thời điểm xây dựng dựa trên giá trị của RELEASE_AIDL_USE_UNFROZEN.

Tệp kê khai và mảnh tệp kê khai khai báo phiên bản giao diện mà một dịch vụ sẽ triển khai. Khi sử dụng phiên bản không cố định mới nhất của một giao diện, bạn phải cập nhật tệp kê khai để phản ánh phiên bản mới này. Khi RELEASE_AIDL_USE_UNFROZEN=false, các mục nhập tệp kê khai được libvintf điều chỉnh để phản ánh thay đổi trong thư viện AIDL đã tạo. Phiên bản này được sửa đổi từ phiên bản không cố định N thành phiên bản cố định gần đây nhất N - 1. Do đó, người dùng không cần phải quản lý nhiều tệp kê khai hoặc mảnh tệp kê khai cho từng dịch vụ của họ.

Thay đổi ứng dụng HAL

Mã ứng dụng HAL phải có khả năng tương thích ngược với từng phiên bản bị treo được hỗ trợ trước đó. Khi RELEASE_AIDL_USE_UNFROZENfalse, các dịch vụ sẽ luôn giống như phiên bản cố định gần đây nhất hoặc phiên bản cũ hơn (ví dụ: việc gọi các phương thức không được cố định mới sẽ trả về UNKNOWN_TRANSACTION hoặc các trường parcelable mới có giá trị mặc định). Các ứng dụng khung Android bắt buộc phải tương thích ngược với các phiên bản trước đó. Tuy nhiên, đây là thông tin chi tiết mới dành cho ứng dụng của nhà cung cấp và ứng dụng có giao diện do đối tác sở hữu.

Thay đổi về cách triển khai HAL

Điểm khác biệt lớn nhất trong quá trình phát triển HAL với việc phát triển dựa trên cờ là yêu cầu các phương thức triển khai HAL phải tương thích ngược với phiên bản bị treo gần đây nhất để hoạt động khi RELEASE_AIDL_USE_UNFROZEN có giá trị là false. Xem xét khả năng tương thích ngược trong quá trình triển khai và mã thiết bị là một bài tập mới. Xem phần Sử dụng giao diện được tạo phiên bản.

Những cân nhắc về khả năng tương thích ngược thường giống nhau đối với máy khách và máy chủ, cũng như đối với mã khung và mã nhà cung cấp, nhưng có một số khác biệt nhỏ mà bạn cần lưu ý vì hiện bạn đang triển khai hiệu quả hai phiên bản sử dụng cùng một mã nguồn (phiên bản hiện tại, không được cố định).

Ví dụ: Một giao diện có ba phiên bản cố định. Giao diện được cập nhật bằng một phương thức mới. Cả ứng dụng và dịch vụ đều được cập nhật để sử dụng thư viện phiên bản 4 mới. Vì thư viện V4 dựa trên phiên bản không được đóng băng của giao diện, nên thư viện này hoạt động như phiên bản bị treo gần đây nhất, phiên bản 3, khi RELEASE_AIDL_USE_UNFROZENfalse và ngăn việc sử dụng phương thức mới.

Khi giao diện bị treo, tất cả các giá trị của RELEASE_AIDL_USE_UNFROZEN đều sử dụng phiên bản bị treo đó, đồng thời mã xử lý khả năng tương thích ngược có thể bị xoá.

Khi gọi phương thức trên các lệnh gọi lại, bạn phải xử lý linh hoạt trường hợp khi UNKNOWN_TRANSACTION được trả về. Ứng dụng có thể triển khai hai phiên bản khác nhau của lệnh gọi lại dựa trên cấu hình phát hành. Vì vậy, bạn không thể cho rằng ứng dụng sẽ gửi phiên bản mới nhất và các phương thức mới có thể trả về phiên bản này. Điều này tương tự như cách các ứng dụng AIDL ổn định duy trì khả năng tương thích ngược với máy chủ được mô tả trong bài viết Sử dụng giao diện được tạo phiên bản.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

Các trường mới trong các loại hiện có (parcelable, enum, union) có thể không tồn tại hoặc chứa giá trị mặc định khi RELEASE_AIDL_USE_UNFROZENfalse và giá trị của các trường mới mà dịch vụ cố gắng gửi sẽ bị loại bỏ trong quá trình.

Bạn không thể gửi hoặc nhận các loại mới được thêm vào phiên bản không cố định này thông qua giao diện.

Quá trình triển khai không bao giờ nhận lệnh gọi phương thức mới từ bất kỳ ứng dụng nào khi RELEASE_AIDL_USE_UNFROZENfalse.

Hãy lưu ý chỉ sử dụng liệt kê mới với phiên bản được giới thiệu, chứ không phải phiên bản trước đó.

Thông thường, bạn sử dụng foo->getInterfaceVersion() để xem giao diện từ xa đang sử dụng phiên bản nào. Tuy nhiên, với tính năng hỗ trợ tạo phiên bản dựa trên cờ, bạn đang triển khai 2 phiên bản khác nhau nên có thể bạn sẽ muốn nhận phiên bản của giao diện hiện tại. Bạn có thể thực hiện việc này bằng cách lấy phiên bản giao diện của đối tượng hiện tại, ví dụ: this->getInterfaceVersion() hoặc các phương thức khác cho my_ver. Hãy xem phần Truy vấn phiên bản giao diện của đối tượng từ xa để biết thêm thông tin.

Giao diện ổn định mới của VINTF

Khi thêm một gói giao diện AIDL mới, không có phiên bản bị treo gần đây nhất, do đó, không có hành vi nào để quay lại sử dụng khi RELEASE_AIDL_USE_UNFROZENfalse. Đừng sử dụng những giao diện này. Khi RELEASE_AIDL_USE_UNFROZENfalse, Trình quản lý dịch vụ sẽ không cho phép dịch vụ đăng ký giao diện và ứng dụng sẽ không tìm thấy giao diện đó.

Bạn có thể thêm các dịch vụ theo điều kiện dựa trên giá trị của cờ RELEASE_AIDL_USE_UNFROZEN trong tệp makefile của thiết bị:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Nếu dịch vụ là một phần của một quy trình lớn hơn nên bạn không thể thêm dịch vụ đó vào thiết bị theo điều kiện, thì bạn có thể kiểm tra xem dịch vụ có được khai báo bằng IServiceManager::isDeclared() hay không. Nếu hệ thống khai báo lệnh này nhưng không đăng ký được, hãy huỷ quá trình này. Nếu bạn không khai báo miền này, thì hệ thống có thể sẽ không đăng ký được.

Mực ống làm công cụ phát triển

Mỗi năm sau khi VINTF bị đóng băng, chúng tôi sẽ điều chỉnh ma trận tương thích khung (FCM) target-levelPRODUCT_SHIPPING_API_LEVEL của Cuttlefish để phản ánh các thiết bị ra mắt trong bản phát hành vào năm sau. Chúng tôi điều chỉnh target-levelPRODUCT_SHIPPING_API_LEVEL để đảm bảo có một số thiết bị phát hành được kiểm thử và đáp ứng các yêu cầu mới cho bản phát hành vào năm sau.

Khi RELEASE_AIDL_USE_UNFROZENtrue, Mực ống sẽ được dùng để phát triển các bản phát hành Android trong tương lai. Bản phát hành này nhắm đến cấp độ FCM của bản phát hành Android năm tới và PRODUCT_SHIPPING_API_LEVEL, yêu cầu bản phát hành này phải đáp ứng các Yêu cầu về phần mềm dành cho nhà cung cấp (VSR) của bản phát hành tiếp theo.

Khi RELEASE_AIDL_USE_UNFROZENfalse, Mực ống sẽ có target-levelPRODUCT_SHIPPING_API_LEVEL trước đó để phản ánh thiết bị phát hành. Trong Android 14 trở xuống, sự khác biệt này sẽ được thực hiện với nhiều nhánh Git không nhận thay đổi đối với FCM target-level, cấp độ API vận chuyển hoặc bất kỳ mã nào khác nhắm đến bản phát hành tiếp theo.

Quy tắc đặt tên mô-đun

Trong Android 11, đối với mỗi tổ hợp phiên bản và phần phụ trợ được bật, một mô-đun thư viện mã giả lập sẽ được tạo tự động. Để tham chiếu đến một mô-đun thư viện mã giả lập cụ thể cho việc liên kết, đừng sử dụng tên của mô-đun aidl_interface mà hãy dùng tên của mô-đun thư viện mã giả lập (ifacename-version-backend), trong đó

  • ifacename: tên của mô-đun aidl_interface
  • version là một trong hai
    • Vversion-number đối với các phiên bản bị treo
    • Vlatest-frozen-version-number + 1 cho phiên bản đầu cây (chưa được đóng băng)
  • backend là một trong hai
    • java cho phần phụ trợ Java,
    • cpp cho phần phụ trợ C++,
    • ndk hoặc ndk_platform cho phần phụ trợ NDK. Vị trí thứ nhất là dành cho ứng dụng, phần thứ nhất là dành cho việc sử dụng nền tảng,
    • rust cho phần phụ trợ Rust.

Giả sử có một mô-đun có tên foo và phiên bản mới nhất của mô-đun đó là 2, đồng thời hỗ trợ cả NDK và C++. Trong trường hợp này, AIDL tạo các mô-đun sau:

  • Dựa trên phiên bản 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Dựa trên phiên bản 2 (phiên bản ổn định mới nhất)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • Dựa trên phiên bản Điều khoản dịch vụ
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

So với Android 11,

  • foo-backend, được gọi là phiên bản ổn định mới nhất trở thành foo-V2-backend
  • foo-unstable-backend, được gọi là phiên bản Điều khoản dịch vụ, trở thành foo-V3-backend

Tên tệp đầu ra luôn giống với tên mô-đun.

  • Dựa trên phiên bản 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • Dựa trên phiên bản 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • Dựa trên phiên bản Điều khoản dịch vụ: foo-V3-(cpp|ndk|ndk_platform|rust).so

Xin lưu ý rằng trình biên dịch AIDL không tạo mô-đun phiên bản unstable hoặc mô-đun không được tạo phiên bản cho giao diện AIDL ổn định. Kể từ Android 12, tên mô-đun được tạo từ giao diện AIDL ổn định luôn bao gồm cả phiên bản của giao diện đó.

Phương thức giao diện meta mới

Android 10 thêm một số phương thức giao diện meta cho AIDL ổn định.

Truy vấn phiên bản giao diện của đối tượng từ xa

Ứng dụng có thể truy vấn phiên bản và hàm băm của giao diện mà đối tượng từ xa đang triển khai rồi so sánh các giá trị được trả về với các giá trị của giao diện mà ứng dụng đang sử dụng.

Ví dụ với phần phụ trợ cpp:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

Ví dụ với phần phụ trợ ndk (và ndk_platform):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

Ví dụ với phần phụ trợ java:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Đối với ngôn ngữ Java, phía từ xa PHẢI triển khai getInterfaceVersion()getInterfaceHash() như sau (super được dùng thay cho IFoo để tránh lỗi khi sao chép/dán. Bạn có thể cần chú giải @SuppressWarnings("static") để tắt cảnh báo, tuỳ thuộc vào cấu hình javac):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Nguyên nhân là do các lớp đã tạo (IFoo, IFoo.Stub, v.v.) được chia sẻ giữa ứng dụng và máy chủ (ví dụ: các lớp này có thể nằm trong đường dẫn lớp khởi động). Khi các lớp được chia sẻ, máy chủ cũng được liên kết với phiên bản mới nhất của các lớp, mặc dù phiên bản này có thể được tạo bằng một phiên bản cũ của giao diện. Nếu được triển khai trong lớp dùng chung, giao diện meta này sẽ luôn trả về phiên bản mới nhất. Tuy nhiên, bằng cách triển khai phương thức như trên, số phiên bản của giao diện được nhúng vào mã của máy chủ (vì IFoo.VERSIONstatic final int cùng dòng khi được tham chiếu) và do đó, phương thức này có thể trả về phiên bản chính xác mà máy chủ được xây dựng cùng.

Xử lý giao diện cũ

Có thể máy khách đã được cập nhật lên phiên bản giao diện AIDL mới hơn, nhưng máy chủ đang sử dụng giao diện AIDL cũ. Trong những trường hợp như vậy, việc gọi một phương thức trên giao diện cũ sẽ trả về UNKNOWN_TRANSACTION.

Khi sử dụng AIDL ổn định, khách hàng sẽ có nhiều quyền kiểm soát hơn. Ở phía máy khách, bạn có thể đặt phương thức triển khai mặc định thành giao diện AIDL. Một phương thức trong quá trình triển khai mặc định chỉ được gọi khi phương thức đó không được triển khai ở phía từ xa (vì phương thức này được tạo bằng một phiên bản giao diện cũ hơn). Vì các giá trị mặc định được thiết lập chung, nên bạn không nên sử dụng các giá trị này trong ngữ cảnh có thể dùng chung.

Ví dụ trong C++ trên Android 13 trở lên:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Ví dụ trong Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process


foo.anAddedMethod(...);

Bạn không cần cung cấp phương thức triển khai mặc định cho tất cả các phương thức trong giao diện AIDL. Các phương thức được đảm bảo sẽ triển khai ở phía xa (vì bạn chắc chắn rằng điều khiển từ xa được tạo khi các phương thức đó nằm trong phần mô tả giao diện AIDL) không cần ghi đè trong lớp impl mặc định.

Chuyển đổi AIDL hiện có thành AIDL có cấu trúc/ổn định

Nếu bạn đã có sẵn giao diện và mã AIDL sử dụng giao diện đó, hãy thực hiện các bước sau để chuyển đổi giao diện sang giao diện AIDL ổn định.

  1. Xác định tất cả phần phụ thuộc của giao diện. Đối với mọi gói mà giao diện phụ thuộc vào, hãy xác định xem gói đó có được xác định trong AIDL ổn định hay không. Nếu không được xác định, gói phải được chuyển đổi.

  2. Chuyển đổi tất cả các gói được trong giao diện của bạn thành các gói ổn định (bản thân các tệp giao diện có thể không thay đổi). Bạn có thể thực hiện việc này bằng cách biểu thị cấu trúc của chúng ngay trong tệp AIDL. Các lớp quản lý phải được viết lại để sử dụng các loại mới này. Bạn có thể thực hiện việc này trước khi tạo gói aidl_interface (ở bên dưới).

  3. Tạo một gói aidl_interface (như mô tả ở trên) chứa tên mô-đun, các phần phụ thuộc của mô-đun đó và mọi thông tin khác mà bạn cần. Để làm cho mã ổn định (không chỉ có cấu trúc), bạn cũng cần tạo phiên bản cho mã. Để biết thêm thông tin, hãy xem bài viết Giao diện tạo phiên bản.