APEX của nhà cung cấp

Bạn có thể sử dụng định dạng tệp APEX để đóng gói và cài đặt các mô-đun hệ điều hành Android cấp thấp hơn. Công cụ này cho phép tạo và cài đặt độc lập các thành phần như dịch vụ và thư viện gốc, triển khai HAL, phần mềm cơ sở, tệp cấu hình, v.v.

Hệ thống xây dựng sẽ tự động cài đặt APEX của nhà cung cấp trong phân vùng /vendor và kích hoạt APEX trong thời gian chạy bằng apexd, giống như APEX trong các phân vùng khác.

Trường hợp sử dụng

Mô-đun hoá hình ảnh của nhà cung cấp

APEX hỗ trợ việc đóng gói và mô-đun hoá tự nhiên các hoạt động triển khai tính năng trên hình ảnh của nhà cung cấp.

Khi hình ảnh của nhà cung cấp được tạo dưới dạng tổ hợp các APEX của nhà cung cấp được tạo độc lập, nhà sản xuất thiết bị có thể dễ dàng chọn và lựa chọn cách triển khai cụ thể của nhà cung cấp mà họ muốn trên thiết bị của mình. Nhà sản xuất thậm chí có thể tạo một APEX nhà cung cấp mới nếu không có APEX nào được cung cấp phù hợp với nhu cầu của họ hoặc họ có phần cứng tuỳ chỉnh hoàn toàn mới.

Ví dụ: nhà sản xuất thiết bị gốc (OEM) có thể chọn tạo thiết bị bằng APEX triển khai wifi AOSP, APEX triển khai Bluetooth SoC và APEX triển khai điện thoại OEM tuỳ chỉnh.

Nếu không có APEX của nhà cung cấp, việc triển khai với quá nhiều phần phụ thuộc giữa các thành phần của nhà cung cấp sẽ đòi hỏi bạn phải phối hợp và theo dõi cẩn thận. Bằng cách gói tất cả các thành phần (bao gồm cả tệp cấu hình và thư viện bổ sung) trong APEX với các giao diện được xác định rõ ràng tại bất kỳ điểm giao tiếp giữa các tính năng, các thành phần khác nhau có thể thay thế cho nhau.

Lặp lại cho nhà phát triển

APEX của nhà cung cấp giúp nhà phát triển lặp lại nhanh hơn trong khi phát triển các mô-đun của nhà cung cấp bằng cách đóng gói toàn bộ quá trình triển khai tính năng, chẳng hạn như HAL wifi, bên trong APEX của nhà cung cấp. Sau đó, nhà phát triển có thể tạo và đẩy riêng APEX của nhà cung cấp để kiểm thử các thay đổi, thay vì tạo lại toàn bộ hình ảnh của nhà cung cấp.

Điều này giúp đơn giản hoá và đẩy nhanh chu kỳ lặp lại của nhà phát triển đối với những nhà phát triển chủ yếu làm việc trong một khu vực tính năng và chỉ muốn lặp lại trên khu vực tính năng đó.

Việc tự nhiên gói một khu vực tính năng vào APEX cũng đơn giản hoá quy trình tạo, đẩy và kiểm thử các thay đổi cho khu vực tính năng đó. Ví dụ: việc cài đặt lại APEX sẽ tự động cập nhật mọi thư viện hoặc tệp cấu hình đi kèm mà APEX bao gồm.

Việc gói một khu vực tính năng vào APEX cũng đơn giản hoá việc gỡ lỗi hoặc quay lại khi nhận thấy hành vi xấu của thiết bị. Ví dụ: nếu điện thoại hoạt động kém trong một bản dựng mới, thì nhà phát triển có thể thử cài đặt APEX triển khai điện thoại cũ trên một thiết bị (mà không cần cài đặt ROM bản dựng đầy đủ) và xem liệu hành vi tốt có được khôi phục hay không.

Quy trình công việc mẫu:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

Ví dụ

Thông tin cơ bản

Hãy xem trang Định dạng tệp APEX chính để biết thông tin chung về APEX, bao gồm cả các yêu cầu về thiết bị, thông tin chi tiết về định dạng tệp và các bước cài đặt.

Trong Android.bp, việc đặt thuộc tính vendor: true sẽ khiến mô-đun APEX trở thành APEX của nhà cung cấp.

apex {
  ..
  vendor: true,
  ..
}

Tệp nhị phân và thư viện dùng chung

APEX bao gồm các phần phụ thuộc bắc cầu bên trong tải trọng APEX, trừ phi các phần phụ thuộc đó có giao diện ổn định.

Các giao diện gốc ổn định cho các phần phụ thuộc APEX của nhà cung cấp bao gồm cc_library với thư viện stubs và LLNDK. Các phần phụ thuộc này sẽ không được đưa vào quá trình đóng gói và các phần phụ thuộc sẽ được ghi lại trong tệp kê khai APEX. Tệp kê khai được linkerconfig xử lý để các phần phụ thuộc gốc bên ngoài có sẵn trong thời gian chạy.

Trong đoạn mã sau, APEX chứa cả tệp nhị phân (my_service) và các phần phụ thuộc không ổn định của tệp đó (tệp *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

Trong đoạn mã sau, APEX chứa thư viện dùng chung my_standalone_lib và mọi phần phụ thuộc không ổn định của thư viện đó (như mô tả ở trên).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

Thu nhỏ APEX

APEX có thể lớn hơn vì gói các phần phụ thuộc không ổn định. Bạn nên sử dụng tính năng liên kết tĩnh. Các thư viện phổ biến như libc++.solibbase.so có thể được liên kết tĩnh với tệp nhị phân HAL. Bạn cũng có thể tạo một phần phụ thuộc để cung cấp giao diện ổn định. Phần phụ thuộc sẽ không được đóng gói trong APEX.

Triển khai HAL

Để xác định cách triển khai HAL, hãy cung cấp các tệp nhị phân và thư viện tương ứng bên trong APEX của nhà cung cấp tương tự như các ví dụ sau:

Để đóng gói đầy đủ quá trình triển khai HAL, APEX cũng phải chỉ định mọi mảnh VINTF và tập lệnh khởi chạy có liên quan.

Mảnh VINTF

Các mảnh VINTF có thể được phân phát từ APEX của nhà cung cấp khi các mảnh nằm trong etc/vintf của APEX.

Sử dụng thuộc tính prebuilts để nhúng các mảnh VINTF vào APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

API truy vấn

Khi các mảnh VINTF được thêm vào APEX, hãy sử dụng API libbinder_ndk để nhận bản đồ ánh xạ của giao diện HAL và tên APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true nếu thực thể HAL được xác định trong APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...) : lấy tên APEX xác định thực thể HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...) : sử dụng tính năng này để mở HAL truyền qua.

Tập lệnh khởi tạo

APEX có thể bao gồm tập lệnh khởi động theo hai cách: (A) tệp văn bản tạo sẵn trong tải trọng APEX hoặc (B) tập lệnh khởi động thông thường trong /vendor/etc. Bạn có thể đặt cả hai cho cùng một APEX.

Khởi tạo tập lệnh trong APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

Tập lệnh khởi tạo trong APEX của nhà cung cấp có thể có định nghĩa service và lệnh on <property or event>.

Đảm bảo rằng định nghĩa service trỏ đến một tệp nhị phân trong cùng một APEX. Ví dụ: com.android.foo APEX có thể xác định một dịch vụ có tên là foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

Hãy cẩn thận khi sử dụng các lệnh on. Vì các tập lệnh init trong APEX được phân tích cú pháp và thực thi sau khi APEX được kích hoạt, nên bạn không thể sử dụng một số sự kiện hoặc thuộc tính. Sử dụng apex.all.ready=true để kích hoạt các hành động càng sớm càng tốt. APEXes khởi động có thể sử dụng on init, nhưng không thể sử dụng on early-init.

Chương trình cơ sở

Ví dụ:

Nhúng phần mềm cơ sở vào APEX của nhà cung cấp bằng loại mô-đun prebuilt_firmware, như sau.

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

Các mô-đun prebuilt_firmware được cài đặt trong thư mục <apex name>/etc/firmware của APEX. ueventd quét các thư mục /apex/*/etc/firmware để tìm các mô-đun phần mềm.

file_contexts của APEX phải gắn nhãn đúng cách cho mọi mục tải trọng phần mềm để đảm bảo rằng ueventd có thể truy cập vào các tệp này trong thời gian chạy; thường thì nhãn vendor_file là đủ. Ví dụ:

(/.*)? u:object_r:vendor_file:s0

Mô-đun nhân

Nhúng các mô-đun hạt nhân vào APEX của nhà cung cấp dưới dạng các mô-đun tạo sẵn, như sau.

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

file_contexts của APEX phải gắn nhãn đúng cách cho mọi mục nhập trọng tải mô-đun hạt nhân. Ví dụ:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

Bạn phải cài đặt rõ ràng các mô-đun hạt nhân. Tập lệnh khởi động mẫu sau đây trong phân vùng của nhà cung cấp cho thấy quá trình cài đặt thông qua insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

Lớp phủ tài nguyên trong thời gian chạy

Ví dụ:

Nhúng lớp phủ tài nguyên thời gian chạy trong APEX của nhà cung cấp bằng cách sử dụng thuộc tính rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

Các tệp cấu hình khác

APEX của nhà cung cấp hỗ trợ nhiều tệp cấu hình khác thường có trên phân vùng của nhà cung cấp dưới dạng tệp tạo sẵn bên trong các APEX của nhà cung cấp và nhiều tệp khác đang được thêm vào.

Ví dụ:

APEX của nhà cung cấp Bootstrap

Một số dịch vụ HAL như keymint phải có sẵn trước khi APEX được kích hoạt. Các HAL đó thường đặt early_hal trong định nghĩa dịch vụ trong tập lệnh init. Một ví dụ khác là lớp animation thường được bắt đầu sớm hơn sự kiện post-fs-data. Khi một dịch vụ HAL sớm như vậy được đóng gói trong APEX của nhà cung cấp, hãy tạo "vendorBootstrap": true đỉnh trong Tệp kê khai APEX để có thể kích hoạt sớm hơn. Xin lưu ý rằng bạn chỉ có thể kích hoạt APEX khởi động từ vị trí tạo sẵn như /vendor/apex, chứ không phải từ /data/apex.

Thuộc tính hệ thống

Đây là các thuộc tính hệ thống mà khung đọc để hỗ trợ APEX của nhà cung cấp:

  • input_device.config_file.apex=<apex name> – khi được đặt, các tệp cấu hình đầu vào (*.idc, *.kl*.kcm) sẽ được tìm kiếm từ thư mục /etc/usr của APEX.
  • ro.vulkan.apex=<apex name> – khi được đặt, trình điều khiển Vulkan sẽ được tải từ APEX. Vì trình điều khiển Vulkan được các HAL đời đầu sử dụng, hãy tạo APEX Bootstrap APEX và định cấu hình không gian tên của trình liên kết đó.

Đặt các thuộc tính hệ thống trong tập lệnh init bằng lệnh setprop.

Các tính năng phát triển bổ sung

Lựa chọn APEX khi khởi động

Ví dụ:

Nhà phát triển cũng có thể cài đặt nhiều phiên bản APEX của nhà cung cấp có cùng tên và khoá APEX, sau đó chọn phiên bản được kích hoạt trong mỗi lần khởi động bằng cách sử dụng sysprops ổn định. Đối với một số trường hợp sử dụng nhất định của nhà phát triển, việc này có thể đơn giản hơn so với việc cài đặt bản sao mới của APEX bằng adb install.

Ví dụ về trường hợp sử dụng:

  • Cài đặt 3 phiên bản APEX của nhà cung cấp HAL wifi: Nhóm đảm bảo chất lượng có thể chạy quy trình kiểm thử thủ công hoặc tự động bằng một phiên bản, sau đó khởi động lại vào một phiên bản khác và chạy lại quy trình kiểm thử, sau đó so sánh kết quả cuối cùng.
  • Cài đặt 2 phiên bản APEX của nhà cung cấp HAL máy ảnh, hiện tạithử nghiệm: Người dùng thử nghiệm có thể sử dụng phiên bản thử nghiệm mà không cần tải xuống và cài đặt tệp bổ sung, nhờ đó, họ có thể dễ dàng hoán đổi trở lại.

Trong quá trình khởi động, apexd sẽ tìm kiếm sysprops theo một định dạng cụ thể để kích hoạt phiên bản APEX phù hợp.

Định dạng dự kiến cho khoá thuộc tính là:

  • Bootconfig
    • Dùng để đặt giá trị mặc định trong BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • Sysprop ổn định
    • Dùng để thay đổi giá trị mặc định, được đặt trên một thiết bị đã khởi động.
    • Ghi đè giá trị bootconfig nếu có.
    • persist.vendor.apex.<apex name>

Giá trị của thuộc tính này phải là tên tệp của APEX cần được kích hoạt.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

Bạn cũng nên định cấu hình phiên bản mặc định bằng cách sử dụng bootconfig trong BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

Sau khi thiết bị khởi động, hãy thay đổi phiên bản đã kích hoạt bằng cách đặt sysprop ổn định:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

Nếu thiết bị hỗ trợ việc cập nhật bootconfig sau khi cài đặt ROM (chẳng hạn như thông qua các lệnh fastboot oem), thì việc thay đổi thuộc tính bootconfig cho APEX được cài đặt nhiều lần cũng sẽ thay đổi phiên bản được kích hoạt khi khởi động.

Đối với các thiết bị tham chiếu ảo dựa trên Cuttlefish, bạn có thể sử dụng lệnh --extra_bootconfig_args để đặt trực tiếp thuộc tính bootconfig trong khi khởi chạy. Ví dụ:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";