Định dạng tệp APEX

Định dạng vùng chứa Android Pony EXpress (APEX) đã được giới thiệu trong Android 10 và được dùng trong quy trình cài đặt cho hệ thống cấp thấp hơn các mô-đun. Định dạng này hỗ trợ việc cập nhật những thành phần hệ thống không phù hợp vào mô hình ứng dụng Android chuẩn. Một số ví dụ về thành phần là gốc các dịch vụ và thư viện, lớp trừu tượng phần cứng (HALs), thời gian chạy (ART) và thư viện lớp.

Từ "APEX" cũng có thể tham chiếu đến tệp APEX.

Thông tin khái quát

Mặc dù Android hỗ trợ bản cập nhật các mô-đun phù hợp với ứng dụng tiêu chuẩn mô hình (ví dụ: dịch vụ, hoạt động) thông qua các ứng dụng trình cài đặt gói (chẳng hạn như ứng dụng Cửa hàng Google Play), sử dụng một mô hình tương tự cho các thành phần hệ điều hành cấp thấp hơn có những hạn chế sau:

  • Bạn không thể sử dụng sớm các mô-đun dựa trên APK trong trình tự khởi động. Gói hàng Trình quản lý quảng cáo là kho lưu trữ trung tâm thông tin về ứng dụng và chỉ có thể được bắt đầu từ trình quản lý hoạt động. Trình quản lý này sẽ sẵn sàng trong giai đoạn sau cho quy trình khởi động.
  • Định dạng APK (đặc biệt là tệp kê khai) được thiết kế cho các ứng dụng Android và mô-đun hệ thống không phải lúc nào cũng phù hợp.

Thiết kế

Phần này mô tả thiết kế cấp cao của định dạng tệp APEX và Trình quản lý APEX, một dịch vụ quản lý các tệp APEX.

Để biết thêm thông tin về lý do chọn thiết kế này cho APEX, hãy xem Các phương án thay thế được cân nhắc khi phát triển APEX.

Định dạng APEX

Đây là định dạng của tệp APEX.

Định dạng tệp APEX

Hình 1. Định dạng tệp APEX

Ở cấp cao nhất, tệp APEX là tệp zip mà các tệp được lưu trữ không nén và nằm ở ranh giới 4 KB.

Bốn tệp trong một tệp APEX là:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

Tệp apex_manifest.json chứa tên gói và phiên bản xác định tệp APEX. Đây là một ApexManifest vùng đệm giao thức ở định dạng JSON.

Tệp AndroidManifest.xml cho phép tệp APEX sử dụng các công cụ liên quan đến APK và cơ sở hạ tầng như ADB, PackageManager và các ứng dụng trình cài đặt gói (chẳng hạn như Cửa hàng Play). Ví dụ: tệp APEX có thể sử dụng một công cụ hiện có như aapt để kiểm tra siêu dữ liệu cơ bản của tệp. Tệp này chứa tên gói và thông tin phiên bản. Thông tin này cũng thường có trong apex_manifest.json.

Nên dùng apex_manifest.json trên AndroidManifest.xml cho mã mới và xử lý APEX. AndroidManifest.xml có thể chứa bổ sung thông tin nhắm mục tiêu mà các công cụ xuất bản ứng dụng hiện có có thể sử dụng.

apex_payload.img là một hình ảnh hệ thống tệp có định dạng ext4 được hỗ trợ bởi dm-verity. Hình ảnh được gắn kết trong thời gian chạy thông qua thiết bị vòng lặp (loopback). Cụ thể, cây băm và khối siêu dữ liệu được tạo bằng thư viện libavb. Tải trọng hệ thống tệp không được phân tích cú pháp (vì hình ảnh phải có thể gắn tại chỗ). Các tệp thông thường có trong tệp apex_payload.img.

apex_pubkey là khoá công khai dùng để ký hình ảnh hệ thống tệp. Trong thời gian chạy, khoá này đảm bảo rằng APEX đã tải xuống được ký bằng cùng một thực thể để ký cùng một APEX trong các phân vùng tích hợp.

Nguyên tắc đặt tên APEX

Để giúp ngăn chặn xung đột đặt tên giữa các APEX mới khi nền tảng phát triển, hãy sử dụng các nguyên tắc đặt tên sau:

  • com.android.*
    • Dành riêng cho AOSP APEX. Không dành riêng cho bất kỳ công ty hay thiết bị nào.
  • com.<companyname>.*
    • Đặt trước cho một công ty. Có thể được nhiều thiết bị sử dụng từ nền tảng đó công ty.
  • com.<companyname>.<devicename>.*
    • Dành riêng cho các APEX dành riêng cho một thiết bị (hoặc một nhóm nhỏ thiết bị) cụ thể.

Người quản lý APEX

Trình quản lý APEX (hoặc apexd) là một quy trình gốc độc lập chịu trách nhiệm xác minh, cài đặt và gỡ cài đặt tệp APEX. Quy trình này khởi chạy và sẵn sàng từ sớm trong trình tự khởi động. Các tệp APEX thường được cài đặt sẵn trên thiết bị thuộc /system/apex. Theo mặc định, trình quản lý APEX sử dụng các nếu không có bản cập nhật nào.

Trình tự cập nhật của APEX sử dụng Lớp PackageManager và như sau.

  1. Tệp APEX được tải xuống thông qua ứng dụng trình cài đặt gói, ADB hoặc nguồn.
  2. Trình quản lý gói bắt đầu quy trình cài đặt. Khi nhận ra rằng tệp là APEX, trình quản lý gói sẽ chuyển quyền kiểm soát sang APEX người quản lý.
  3. Người quản lý APEX sẽ xác minh tệp APEX.
  4. Nếu tệp APEX được xác minh, cơ sở dữ liệu nội bộ của trình quản lý APEX sẽ cập nhật để phản ánh tệp APEX được kích hoạt vào lần khởi động tiếp theo.
  5. Trình yêu cầu cài đặt nhận được một thông báo truyền tin khi gói thành công xác minh.
  6. Để tiếp tục quá trình cài đặt, bạn phải khởi động lại hệ thống.
  7. Vào lần khởi động tiếp theo, trình quản lý APEX khởi động, đọc cơ sở dữ liệu nội bộ và sau đây cho mỗi tệp APEX được liệt kê:

    1. Xác minh tệp APEX.
    2. Tạo một thiết bị lặp lại từ tệp APEX.
    3. Tạo một thiết bị khối cho trình liên kết thiết bị ở phía trên thiết bị vòng lặp (loopback).
    4. Gắn thiết bị khối trình liên kết thiết bị vào một đường dẫn duy nhất (ví dụ: /apex/name@ver).

Khi tất cả các tệp APEX có trong cơ sở dữ liệu nội bộ được gắn kết, APEX trình quản lý cung cấp dịch vụ liên kết để các thành phần hệ thống khác truy vấn về các tệp APEX đã cài đặt. Ví dụ: hệ thống khác các thành phần có thể truy vấn danh sách các tệp APEX được cài đặt trong thiết bị hoặc truy vấn đường dẫn chính xác nơi một APEX cụ thể được gắn kết để có thể truy cập vào các tệp.

Tệp APEX là các tệp APK

Các tệp APEX là các tệp APK hợp lệ vì chúng là các tệp lưu trữ zip đã ký (sử dụng Lược đồ chữ ký APK) chứa tệp AndroidManifest.xml. Thao tác này cho phép APEX để sử dụng cơ sở hạ tầng cho tệp APK (chẳng hạn như ứng dụng trình cài đặt gói), tiện ích ký và trình quản lý gói.

Tệp AndroidManifest.xml trong tệp APEX có kích thước tối thiểu, bao gồm gói name, versionCodetargetSdkVersion, minSdkVersion, và maxSdkVersion để nhắm mục tiêu chi tiết. Thông tin này cho phép APEX tệp được phân phối qua các kênh hiện có như ứng dụng trình cài đặt gói và Cầu gỡ lỗi Android (ADB).

Các loại tệp được hỗ trợ

Định dạng APEX hỗ trợ các loại tệp sau:

  • Thư viện dùng chung gốc
  • Các tệp thực thi gốc
  • Tệp JAR
  • Tệp dữ liệu
  • Tệp cấu hình

Điều này không có nghĩa là APEX có thể cập nhật tất cả các loại tệp này. Liệu một tệp loại có thể được cập nhật tuỳ thuộc vào nền tảng và mức độ ổn định của định nghĩa về có giao diện dành cho các loại tệp đó.

Tuỳ chọn ký

Tệp APEX được ký theo hai cách. Trước tiên, apex_payload.img (cụ thể là mã mô tả vbmeta được thêm vào tệp apex_payload.img) được ký bằng một khoá. Sau đó, toàn bộ APEX được ký bằng Lược đồ chữ ký APK phiên bản 3. Hai khoá khác nhau được sử dụng trong quá trình này.

Ở phía thiết bị, một khoá công khai tương ứng với khoá riêng tư dùng để ký mã mô tả vbmeta đã được cài đặt. Trình quản lý APEX sử dụng khoá công khai để xác minh các APEX được yêu cầu cài đặt. Bạn phải ký mỗi APEX bằng các khoá khác nhau và được thực thi cả trong thời gian xây dựng và trong thời gian chạy.

APEX trong phân vùng tích hợp

Các tệp APEX có thể nằm trong các phân vùng tích hợp sẵn như /system. Chiến lược phát hành đĩa đơn phân vùng đã vượt quá dm-verity, do đó các tệp APEX được gắn trực tiếp trên thiết bị lặp lại.

Nếu một APEX có mặt trong một phân vùng tích hợp, thì bạn có thể cập nhật APEX bằng cách cung cấp gói APEX có cùng tên gói và giá trị lớn hơn hoặc bằng vào mã phiên bản. APEX mới được lưu trữ trong /data và tương tự như APK, phiên bản mới cài đặt sẽ ẩn phiên bản đã có trong phiên bản tích hợp phân vùng. Nhưng không giống như APK, phiên bản APEX mới cài đặt chỉ được kích hoạt sau khi khởi động lại.

Yêu cầu về kernel

Để hỗ trợ các mô-đun dòng chính APEX trên thiết bị Android, Linux sau cần có các tính năng kernel: trình điều khiển vòng lặp và dm-verity. Vòng lặp trình điều khiển gắn hình ảnh hệ thống tệp trong mô-đun APEX và dm-verity xác minh mô-đun APEX.

Hiệu suất của trình điều khiển vòng lặp và dm-verity là rất quan trọng trong việc đạt được hiệu suất hệ thống tốt khi sử dụng các mô-đun APEX.

Các phiên bản nhân hệ điều hành được hỗ trợ

Mô-đun đường chính APEX được hỗ trợ trên các thiết bị sử dụng nhân phiên bản 4.4 hoặc cao hơn. Thiết bị mới chạy Android 10 trở lên phải sử dụng nhân phiên bản 4.9 trở lên để hỗ trợ các mô-đun APEX.

Bản vá kernel bắt buộc

Các bản vá hạt nhân cần thiết để hỗ trợ mô-đun APEX được bao gồm trong Cây chung của Android. Để tải bản vá hỗ trợ APEX, hãy sử dụng phiên bản mới nhất của cây chung Android.

Kernel phiên bản 4.4

Phiên bản này chỉ được hỗ trợ cho các thiết bị đã nâng cấp từ Android 9 lên Android 10 và muốn hỗ trợ các mô-đun APEX. Để tải các bản vá bắt buộc, việc hợp nhất xuống từ nhánh android-4.4 được thực hiện mạnh được đề xuất. Sau đây là danh sách các bản vá riêng lẻ bắt buộc cho kernel phiên bản 4.4.

  • UPSTREAM: vòng lặp: thêm ioctl để thay đổi kích thước khối logic (4.4)
  • BACKPORT: khối/vòng: đặt hw_sect (4.4)
  • UPSTREAM: vòng lặp: Thêm LOOP_SET_BLOCK_SIZE trong ioctl tương thích (4.4)
  • ANDROID: mnt: Sửa lỗi next_descendent (4.4)
  • ANDROID: mnt: lượt cài đặt lại sẽ truyền đến các máy chủ nô lệ (4.4)
  • ANDROID: mnt: Truyền tải lại chính xác (4.4)
  • Huỷ bỏ các thay đổi đối với "ANDROID: dm verity: thêm tối thiểu kích thước tìm nạp trước" (4.4)
  • UPSTREAM: vòng lặp: bỏ bộ nhớ đệm nếu thay đổi độ lệch hoặc kích thước khối (4.4)

Kernel phiên bản 4.9/4.14/4.19

Để tải các bản vá cần thiết cho các phiên bản kernel 4.9/4.14/4.19, hãy hợp nhất từ nhánh android-common.

Tuỳ chọn cấu hình nhân hệ điều hành bắt buộc

Danh sách sau đây trình bày các yêu cầu về cấu hình cơ sở để hỗ trợ Các mô-đun APEX được ra mắt trong Android 10. Các mục có dấu hoa thị (*) là những yêu cầu hiện có từ Android 9 trở xuống.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Yêu cầu về tham số dòng lệnh kernel

Để hỗ trợ APEX, hãy đảm bảo các tham số dòng lệnh kernel đáp ứng các yêu cầu sau các yêu cầu:

  • KHÔNG được đặt loop.max_loop
  • loop.max_part phải nhỏ hơn hoặc bằng 8

Xây dựng một APEX

Phần này mô tả cách tạo APEX bằng hệ thống xây dựng Android. Sau đây là ví dụ về Android.bp cho một APEX có tên apex.test.

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

Ví dụ về apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

Ví dụ về file_contexts:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Loại tệp và vị trí trong APEX

Loại tệp Vị trí trong APEX
Thư viện chia sẻ /lib/lib64 (/lib/arm cho arm được dịch sang x86)
Tệp thực thi /bin
Thư viện Java /javalib
Lắp sẵn /etc

Phần phụ thuộc bắc cầu

Các tệp APEX tự động bao gồm các phần phụ thuộc bắc cầu của các lib gốc dùng chung hoặc tệp thực thi. Ví dụ: nếu libFoo phụ thuộc vào libBar, thì hai lib là được đưa vào khi chỉ có libFoo được liệt kê trong thuộc tính native_shared_libs.

Xử lý nhiều ABI

Cài đặt thuộc tính native_shared_libs cho cả chính và phụ giao diện nhị phân của ứng dụng (ABI) của thiết bị. Nếu một APEX nhắm đến các thiết bị với một ABI (nghĩa là chỉ 32 bit hoặc chỉ 64 bit), chỉ những thư viện có ABI tương ứng được cài đặt.

Chỉ cài đặt thuộc tính binaries cho ABI chính của thiết bị dưới dạng được mô tả bên dưới:

  • Nếu thiết bị chỉ hỗ trợ 32 bit, thì chỉ có biến thể 32 bit của tệp nhị phân là .
  • Nếu thiết bị chỉ hỗ trợ 64 bit thì chỉ có biến thể 64 bit của tệp nhị phân là .

Để thêm quyền kiểm soát chi tiết đối với ABI của thư viện gốc và tệp nhị phân, sử dụng multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] các thuộc tính.

  • first: So khớp với ABI chính của thiết bị. Đây là tuỳ chọn mặc định cho tệp nhị phân.
  • lib32: So khớp với ABI 32 bit của thiết bị (nếu được hỗ trợ).
  • lib64: Khớp với ABI 64 bit của thiết bị (được hỗ trợ).
  • prefer32: So khớp với ABI 32 bit của thiết bị (nếu được hỗ trợ). Nếu ABI 32 bit không được hỗ trợ, khớp với ABI 64 bit.
  • both: Trùng khớp với cả hai ABI. Đây là tuỳ chọn mặc định cho native_shared_libraries.

Các thuộc tính java, librariesprebuilts không phụ thuộc vào ABI.

Ví dụ này dành cho thiết bị hỗ trợ 32/64 và không thích tỷ lệ 32:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

ký vbmeta

Ký từng APEX bằng các khoá khác nhau. Khi cần một khoá mới, hãy tạo một cặp khoá công khai và riêng tư và tạo một mô-đun apex_key. Sử dụng thuộc tính key để hãy ký APEX bằng khoá. Khóa công khai sẽ tự động được đưa vào APEX có tên avb_pubkey.

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

Trong ví dụ trên, tên của khoá công khai (foo) trở thành mã nhận dạng của . Mã nhận dạng của khoá dùng để ký APEX được ghi trong APEX. Trong thời gian chạy, apexd xác minh APEX bằng khoá công khai có cùng mã nhận dạng trong thiết bị.

Ký APEX

Ký APEX theo cách tương tự như khi ký tệp APK. Ký các APEX hai lần; một lần cho hệ thống tệp mini (tệp apex_payload.img) và một lần cho toàn bộ tệp.

Để ký APEX ở cấp tệp, hãy đặt thuộc tính certificate vào một trong ba cách sau:

  • Chưa đặt: Nếu bạn không đặt giá trị nào, thì APEX sẽ được ký bằng chứng chỉ nằm trong lúc PRODUCT_DEFAULT_DEV_CERTIFICATE. Nếu bạn không đặt cờ nào, đường dẫn mặc định thành build/target/product/security/testkey.
  • <name>: APEX được ký bằng chứng chỉ <name> trong cùng một chứng chỉ thư mục có tên PRODUCT_DEFAULT_DEV_CERTIFICATE.
  • :<name>: APEX được ký bằng chứng chỉ do Mô-đun Soong có tên là <name>. Mô-đun chứng chỉ có thể được định nghĩa là theo dõi.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

Cài đặt APEX

Để cài đặt APEX, hãy sử dụng ADB.

adb install apex_file_name
adb reboot

Nếu bạn đặt supportsRebootlessUpdate thành true trong apex_manifest.json và APEX hiện đã cài đặt không được sử dụng (ví dụ: bất kỳ dịch vụ nào trong đó có bị dừng), thì bạn có thể cài đặt APEX mới mà không cần khởi động lại bằng Cờ --force-non-staged.

adb install --force-non-staged apex_file_name

Sử dụng APEX

Sau khi khởi động lại, APEX được gắn tại /apex/<apex_name>@<version> thư mục. Bạn có thể gắn nhiều phiên bản của cùng một APEX cùng lúc. Trong số các đường dẫn gắn kết, đường dẫn tương ứng với phiên bản mới nhất là được gắn liên kết tại /apex/<apex_name>.

Ứng dụng có thể dùng đường dẫn gắn trên ràng buộc để đọc hoặc thực thi các tệp từ APEX.

APEX thường được sử dụng như sau:

  1. OEM hoặc ODM tải trước APEX trong /system/apex khi thiết bị đã vận chuyển.
  2. Các tệp trong APEX được truy cập thông qua đường dẫn /apex/<apex_name>/.
  3. Khi một phiên bản cập nhật của APEX được cài đặt trong /data/apex, đường dẫn trỏ đến APEX mới sau khi khởi động lại.

Cập nhật dịch vụ bằng APEX

Cách cập nhật dịch vụ bằng APEX:

  1. Đánh dấu dịch vụ trong phân vùng hệ thống là có thể cập nhật. Thêm lựa chọn updatable vào định nghĩa dịch vụ.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Tạo một tệp .rc mới cho dịch vụ đã cập nhật. Sử dụng lựa chọn override để xác định lại dịch vụ hiện có.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

Bạn chỉ có thể xác định định nghĩa dịch vụ trong tệp .rc của một APEX. Hành động không được hỗ trợ trong APEX.

Nếu một dịch vụ được đánh dấu là có thể cập nhật bắt đầu trước khi các APEX được kích hoạt, thì quá trình khởi động sẽ bị trì hoãn cho đến khi quá trình kích hoạt các APEX hoàn tất.

Định cấu hình hệ thống để hỗ trợ cập nhật APEX

Đặt thuộc tính hệ thống sau đây thành true để hỗ trợ cập nhật tệp APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

hoặc chỉ

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

APEX phẳng

Đối với các thiết bị cũ, đôi khi bạn không thể hoặc không thể cập nhật phiên bản cũ nhân hệ điều hành để hỗ trợ đầy đủ APEX. Ví dụ: nhân có thể đã được xây dựng nếu không có CONFIG_BLK_DEV_LOOP=Y, điều này rất quan trọng để gắn hệ thống tệp bên trong một APEX.

APEX phẳng là một APEX được thiết kế đặc biệt, có thể kích hoạt trên những thiết bị có một nhân cũ. Các tệp trong một APEX đã làm phẳng sẽ được cài đặt trực tiếp vào một thư mục trong phân vùng tích hợp. Ví dụ: lib/libFoo.so trong một APEX đã làm phẳng my.apex đã được cài đặt trên /system/apex/my.apex/lib/libFoo.so.

Việc kích hoạt APEX đã làm phẳng sẽ không liên quan đến thiết bị lặp. Toàn bộ thư mục /system/apex/my.apex được liên kết trực tiếp với /apex/name@ver.

Không thể cập nhật các APEX đã làm phẳng bằng cách tải các phiên bản đã cập nhật xuống của APEX từ mạng vì không thể làm phẳng các APEX đã tải xuống. Bạn chỉ có thể cập nhật các APEX đã làm phẳng qua một OTA thông thường.

APEX đã làm phẳng là cấu hình mặc định. Điều này có nghĩa là tất cả APEX sẽ được làm phẳng theo mặc định trừ phi bạn định cấu hình thiết bị một cách rõ ràng để tạo các APEX không được làm phẳng nhằm hỗ trợ cập nhật APEX (như đã giải thích ở trên).

Việc kết hợp các APEX đã làm phẳng và không làm phẳng trong một thiết bị là KHÔNG được hỗ trợ. Tất cả các APEX trong một thiết bị phải được làm phẳng hoặc không được làm phẳng. Điều này đặc biệt quan trọng khi vận chuyển được tạo sẵn APEX đã ký sẵn cho các dự án như Mainline. Các APEX không được ký trước (nghĩa là được tạo từ nguồn) cũng phải được làm phẳng và được ký bằng các khoá thích hợp. Chiến lược phát hành đĩa đơn thiết bị phải kế thừa từ updatable_apex.mk như được giải thích trong Cập nhật một dịch vụ bằng một APEX.

APEX được nén

Android 12 trở lên có tính năng nén APEX cho giảm tác động đến bộ nhớ của các gói APEX có thể cập nhật. Sau khi cập nhật lên APEX đã được cài đặt, mặc dù phiên bản được cài đặt sẵn không được sử dụng nữa, vẫn chiếm cùng một dung lượng. Không gian bị chiếm dụng đó vẫn không có sẵn.

Tính năng nén APEX giúp giảm thiểu tác động đến dung lượng lưu trữ bằng cách sử dụng tập dữ liệu được nén ở mức độ cao của tệp APEX trên các phân vùng chỉ đọc (chẳng hạn như phân vùng /system). của Android Phiên bản 12 trở lên sử dụng thuật toán nén zip DEFLATE.

Việc nén không giúp tối ưu hoá những mục sau:

  • Các APEX trong quá trình tự khởi động cần được gắn kết từ rất sớm khi khởi động trình tự.

  • Các APEX không thể cập nhật. Việc nén chỉ có lợi nếu bạn cài đặt phiên bản cập nhật của APEX trên phân vùng /data. Bạn có thể xem danh sách đầy đủ các APEX có thể cập nhật trên Thành phần hệ thống mô-đun .

  • Các APEX dùng chung của thư viện động. Vì apexd luôn kích hoạt cả hai phiên bản của các APEX (được cài đặt trước và nâng cấp) như vậy, việc nén chúng sẽ không làm tăng thêm giá trị.

Định dạng tệp APEX được nén

Đây là định dạng của tệp APEX nén.

Sơ đồ cho thấy định dạng của tệp APEX được nén

Hình 2. Định dạng tệp APEX được nén

Ở cấp cao nhất, tệp APEX được nén là tệp zip chứa nội dung gốc tệp apex ở dạng đã rút gọn với cấp độ nén là 9 và với các tệp khác được lưu trữ không nén.

Bốn tệp bao gồm một tệp APEX:

  • original_apex: đã xả hơi với mức nén là 9 Đây là tệpAPEX gốc, không nén.
  • apex_manifest.pb: chỉ đã được lưu trữ
  • AndroidManifest.xml: chỉ đã được lưu trữ
  • apex_pubkey: chỉ đã được lưu trữ

Các tệp apex_manifest.pb, AndroidManifest.xmlapex_pubkey là bản sao của các tệp tương ứng trong original_apex.

Tạo APEX được nén

Bạn có thể tạo APEX được nén bằng công cụ apex_compression_tool.py nằm tại system/apex/tools.

Một số tham số liên quan đến tính năng nén APEX có sẵn trong hệ thống xây dựng.

Trong Android.bp, việc một tệp APEX có thể nén được hay không có được kiểm soát bằng Thuộc tính compressible:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Cờ sản phẩm PRODUCT_COMPRESSED_APEX kiểm soát việc hình ảnh hệ thống có được tạo hay không từ nguồn phải chứa tệp APEX được nén.

Đối với thử nghiệm cục bộ, bạn có thể buộc bản dựng nén APEX bằng cách cài đặt OVERRIDE_PRODUCT_COMPRESSED_APEX= thành true.

Các tệp APEX được nén do hệ thống xây dựng tạo ra có đuôi .capex. Tiện ích này giúp bạn dễ dàng phân biệt giữa tệp nén và không nén của một tệp APEX.

Các thuật toán nén được hỗ trợ

Android 12 chỉ hỗ trợ nén deflate-zip.

Kích hoạt tệp APEX nén trong quá trình khởi động

Trước khi có thể kích hoạt APEX được nén, tệp original_apex bên trong nó đã giải nén vào thư mục /data/apex/decompressed. Kết quả tệp APEX đã giải nén được liên kết cứng với thư mục /data/apex/active.

Hãy xem ví dụ sau đây minh hoạ quy trình được mô tả ở trên.

Hãy xem /system/apex/com.android.foo.capex là một APEX được nén đang là được kích hoạt, với versionCode 37.

  1. Tệp original_apex bên trong /system/apex/com.android.foo.capex là được giải nén thành /data/apex/decompressed/com.android.foo@37.apex.
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex được thực hiện để xác minh rằng nó có nhãn SELinux chính xác.
  3. Các bước kiểm tra xác minh sẽ được thực hiện trên /data/apex/decompressed/com.android.foo@37.apex để đảm bảo tính hợp lệ: apexd kiểm tra khoá công khai được nhóm trong /data/apex/decompressed/com.android.foo@37.apex để xác minh số này bằng nhau với mục được nhóm trong /system/apex/com.android.foo.capex.
  4. Tệp /data/apex/decompressed/com.android.foo@37.apex được liên kết cứng với thư mục /data/apex/active/com.android.foo@37.apex.
  5. Logic kích hoạt thông thường cho các tệp APEX không nén được thực hiện trên /data/apex/active/com.android.foo@37.apex.

Tương tác với OTA

Các tệp APEX được nén có ảnh hưởng đến việc phân phối và ứng dụng qua OTA. Từ bản cập nhật OTA có thể chứa tệp APEX được nén với cấp độ phiên bản cao hơn so với nội dung đang hoạt động trên thiết bị thì phải dành riêng một lượng dung lượng trống nhất định trước khi khởi động lại thiết bị để áp dụng bản cập nhật OTA.

Để hỗ trợ hệ thống OTA, apexd sẽ hiển thị 2 API liên kết sau:

  • calculateSizeForCompressedApex – tính toán kích thước cần thiết để giải nén Các tệp APEX trong gói OTA. Thông tin này có thể được dùng để xác minh rằng một thiết bị đã đủ dung lượng trước khi OTA được tải xuống.
  • reserveSpaceForCompressedApex – dự trữ dung lượng trên đĩa để sử dụng trong tương lai bằng apexd để giải nén các tệp APEX được nén bên trong gói OTA.

Trong trường hợp cập nhật A/B OTA, apexd sẽ thử giải nén trong trong quy trình sau khi cài đặt qua mạng không dây. Nếu giải nén không thành công, apexd giải nén trong quá trình khởi động để áp dụng OTA cập nhật.

Các phương án thay thế được cân nhắc khi phát triển APEX

Dưới đây là một số phương án mà AOSP đã xem xét khi thiết kế tệp APEX định dạng quảng cáo và lý do khiến chúng được đưa vào hoặc bị loại trừ.

Hệ thống quản lý gói thông thường

Các bản phân phối Linux có các hệ thống quản lý gói như dpkgrpm, mạnh mẽ, trưởng thành và mạnh mẽ. Tuy nhiên, họ đã không được được áp dụng cho APEX vì chúng không thể bảo vệ các gói hàng sau cài đặt. Việc xác minh chỉ được thực hiện khi các gói đang được cài đặt. Kẻ tấn công có thể phá vỡ tính toàn vẹn của các gói đã cài đặt mà không được để ý. Đây là một sự hồi quy cho Android trong đó tất cả các thành phần hệ thống được lưu trữ ở chế độ chỉ đọc hệ thống tệp có tính toàn vẹn được bảo vệ bằng dm-verity cho mỗi I/O. Bất kỳ hạng nào hành vi can thiệp vào các thành phần của hệ thống phải bị nghiêm cấm hoặc có thể phát hiện được rằng thiết bị có thể từ chối khởi động nếu bị xâm phạm.

mã hoá dm-crypt để đảm bảo tính toàn vẹn

Các tệp trong vùng chứa APEX được lấy từ các phân vùng tích hợp (ví dụ: phân vùng /system) được bảo vệ bằng dm-verity, trong đó mọi sửa đổi đối với các tệp bị cấm ngay cả sau khi các phân vùng đã được gắn kết. Để cung cấp cùng mức độ bảo mật đối với các tệp, nên mọi tệp trong một APEX đều được lưu trữ trong một tệp hình ảnh hệ thống được ghép nối với cây băm và mã mô tả vbmeta. Không có dm-verity, một APEX trong phân vùng /data dễ bị tấn công ngoài ý muốn các sửa đổi được thực hiện sau khi đã được xác minh và cài đặt.

Trên thực tế, phân vùng /data cũng được bảo vệ bằng các lớp mã hoá như hầm mã dm. Mặc dù điều này cung cấp một mức độ bảo vệ chống lại sự can thiệp, mục đích chính là quyền riêng tư chứ không phải tính toàn vẹn. Khi kẻ tấn công có được quyền truy cập vào /data, không thể có thêm biện pháp bảo vệ nào, và đây là so với mọi thành phần hệ thống có trong phân vùng /system. Cây băm bên trong tệp APEX cùng với dm-verity cung cấp cùng một mức độ bảo vệ nội dung.

Chuyển hướng đường dẫn từ /system đến /apex

Các tệp thành phần hệ thống được đóng gói trong một APEX có thể truy cập được thông qua các đường dẫn mới như /apex/<name>/lib/libfoo.so. Trường hợp các tệp thuộc /system chúng có thể truy cập được qua các đường dẫn như /system/lib/libfoo.so. Đáp máy khách của một tệp APEX (các tệp APEX khác hoặc nền tảng) phải sử dụng đường dẫn. Bạn có thể cần phải cập nhật mã hiện có do việc thay đổi đường dẫn.

Mặc dù một cách để tránh thay đổi đường dẫn là phủ nội dung tệp trong APEX lên phân vùng /system, nhóm Android đã quyết định không che phủ trên phân vùng /system vì điều này có thể ảnh hưởng đến hiệu suất số lượng tệp bị phủ lên (thậm chí có thể được xếp chồng lên nhau) đều tăng.

Một phương án khác là chiếm đoạt các hàm truy cập tệp như open, statreadlink, để các đường dẫn bắt đầu bằng /system được chuyển hướng đến đường dẫn tương ứng trong /apex. Nhóm Android đã loại bỏ tuỳ chọn này vì không thể thay đổi tất cả các hàm chấp nhận đường dẫn. Ví dụ: một số ứng dụng liên kết tĩnh Bionic để triển khai các hàm. Trong những trường hợp như vậy, những ứng dụng đó sẽ không bị chuyển hướng.