Đị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 sử dụng trong quy trình cài đặt cho các mô-đun hệ thống cấp thấp hơn. Định dạng này tạo điều kiện thuận lợi cho việc cập nhật các thành phần hệ thống không phù hợp với mô hình ứng dụng Android tiêu chuẩn. Một số thành phần ví dụ là các dịch vụ và thư viện gốc, lớp trừu tượng phần cứng ( HAL ), thời gian chạy ( ART ) và thư viện lớp.

Thuật ngữ "APEX" cũng có thể đề cập đến tệp APEX.

Lý lịch

Mặc dù Android hỗ trợ cập nhật các mô-đun phù hợp với mô hình ứng dụng tiêu chuẩn (ví dụ: dịch vụ, hoạt động) thông qua các ứng dụng cài đặt gói (chẳng hạn như ứng dụng Cửa hàng Google Play), việc sử dụng 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 nhược điểm sau:

  • Không thể sử dụng các mô-đun dựa trên APK sớm trong quá trình khởi động. Trình quản lý gói là kho lưu trữ thông tin trung tâm về ứng dụng và chỉ có thể được khởi động từ trình quản lý hoạt động, trình quản lý này sẽ sẵn sàng ở giai đoạn sau của quy trình khởi động.
  • Định dạng APK (đặc biệt là tệp kê khai) được thiết kế cho ứng dụng Android và các 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, đây là dịch vụ quản lý các tệp APEX.

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

định dạng đỉnh

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

Định dạng tệp APEX

Hình 1. Định dạng file APEX

Ở cấp cao nhất, tệp APEX là tệp zip trong đó 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 tệp APEX là:

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

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

Tệp AndroidManifest.xml cho phép tệp APEX sử dụng các công cụ và cơ sở hạ tầng liên quan đến APK như ADB, PackageManager và các ứng dụng trình cài đặt gói (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 từ tệp. Tệp chứa tên gói và thông tin phiên bản. Thông tin này thường có sẵn trong apex_manifest.json .

apex_manifest.json được đề xuất trên AndroidManifest.xml cho mã và hệ thống mới xử lý APEX. AndroidManifest.xml có thể chứa thông tin nhắm mục tiêu bổ sung 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à hình ảnh hệ thống tệp ext4 được hỗ trợ bởi dm-verity. Hình ảnh được gắn vào thời gian chạy thông qua thiết bị 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 được tại chỗ). Các tệp thông thường được bao gồm bên trong tệp apex_payload.img .

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

Hướng dẫn đặt tên APEX

Để giúp ngă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 phải duy nhất cho bất kỳ công ty hoặc thiết bị nào.
  • com.<companyname>.*
    • Dành cho một công ty. Có khả năng được sử dụng bởi nhiều thiết bị từ công ty đó.
  • com.<companyname>.<devicename>.*
    • Dành riêng cho các APEX duy nhất cho một thiết bị cụ thể (hoặc tập hợp con của thiết bị).

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 các tệp APEX. Quá trình này được khởi chạy và sẵn sàng 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ị trong /system/apex . Trình quản lý APEX mặc định sử dụng các gói này 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 khác.
  2. Trình quản lý gói bắt đầu quá 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 cho trình quản lý APEX.
  3. Trình quản lý APEX 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 cập nhật để phản ánh rằng tệp APEX sẽ được kích hoạt ở lần khởi động tiếp theo.
  5. Người yêu cầu cài đặt sẽ nhận được thông báo khi xác minh gói thành công.
  6. Để tiếp tục cài đặt, hệ thống phải được khởi động lại.
  7. Ở 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à thực hiện các thao tác sau đối với từng tệp APEX được liệt kê:

    1. Xác minh tệp APEX.
    2. Tạo một thiết bị loopback từ tệp APEX.
    3. Tạo một thiết bị khối ánh xạ thiết bị trên đầu thiết bị loopback.
    4. Gắn thiết bị khối ánh xạ 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 liệt kê trong cơ sở dữ liệu nội bộ được gắn kết, trình quản lý APEX sẽ cung cấp dịch vụ liên kết cho các thành phần hệ thống khác để truy vấn thông tin về các tệp APEX đã cài đặt. Ví dụ: các thành phần hệ thống khác 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 gắn một APEX cụ thể để có thể truy cập các tệp này.

Tệp APEX là tệp APK

Tệp APEX là tệp APK hợp lệ vì chúng là tệp lưu trữ zip có chữ ký (sử dụng sơ đồ chữ ký APK) có chứa tệp AndroidManifest.xml . Điều này cho phép các tệp APEX sử dụng cơ sở hạ tầng cho các 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 bên trong tệp APEX có kích thước tối thiểu, bao gồm name gói, versionCodetargetSdkVersion , minSdkVersionmaxSdkVersion tùy chọn để nhắm mục tiêu chi tiết. Thông tin này cho phép các tệp APEX đượ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à 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 chia sẻ gốc
  • Tệp thực thi gốc
  • Tệp JAR
  • Hồ sơ dữ liệu
  • Tập tin 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. Việc loại tệp có thể được cập nhật hay không tùy thuộc vào nền tảng và mức độ ổn định của định nghĩa giao diện cho loại tệp đó.

Tùy chọn ký

Các tệp APEX được ký theo hai cách. Đầu tiên, tệp apex_payload.img (cụ thể là bộ mô tả vbmeta được thêm vào apex_payload.img ) được ký bằng một khóa. Sau đó, toàn bộ APEX được ký bằng lược đồ chữ ký APK v3 . Hai phím khác nhau được sử dụng trong quá trình này.

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

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

Các tệp APEX có thể được đặt trong các phân vùng tích hợp sẵn như /system . 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ị loopback.

Nếu APEX có trong phân vùng tích hợp, APEX có thể được cập nhật bằng cách cung cấp gói APEX có cùng tên gói và lớn hơn hoặc bằng 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 cài đặt sẽ che khuất phiên bản đã có trong phân vùng tích hợp sẵn. 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 hạt nhân

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

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

Phiên bản hạt nhân được hỗ trợ

Các mô-đun dòng chính APEX được hỗ trợ trên các thiết bị sử dụng phiên bản kernel 4.4 trở lên. Các thiết bị mới chạy Android 10 trở lên phải sử dụng kernel phiên bản 4.9 trở lên để hỗ trợ các mô-đun APEX.

Các bản vá kernel cần thiết

Các bản vá hạt nhân cần thiết để hỗ trợ các mô-đun APEX được bao gồm trong cây chung của Android. Để có được các 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.

Phiên bản hạt nhân 4.4

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

  • UPSTREAM: loop: thêm ioctl để thay đổi kích thước khối logic ( 4.4 )
  • BACKPORT: chặn/vòng lặp: đặt hw_sectors ( 4.4 )
  • UPSTREAM: vòng lặp: Thêm LOOP_SET_BLOCK_SIZE trong compat ioctl ( 4.4 )
  • ANDROID: mnt: Sửa next_descendent ( 4.4 )
  • ANDROID: mnt: remount nên truyền bá tới nô lệ của nô lệ ( 4.4 )
  • ANDROID: mnt: Truyền lại chính xác ( 4.4 )
  • Hoàn nguyên "ANDROID: dm verity: thêm kích thước tìm nạp trước tối thiểu" ( 4.4 )
  • UPSTREAM: loop: xóa bộ đệm nếu offset hoặc block_size bị thay đổi ( 4.4 )

Phiên bản hạt nhân 4.9/4.14/4.19

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

Tùy chọn cấu hình kernel bắt buộc

Danh sách sau đây hiển thị các yêu cầu cấu hình cơ bản để hỗ trợ các mô-đun APEX được giới thiệu trong Android 10. Các mục có dấu hoa thị (*) là các 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 tham số dòng lệnh hạt nhân

Để 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:

  • loop.max_loop KHÔNG được đặt
  • loop.max_part phải <= 8

Xây dựng một ĐỈNH

Phần này mô tả cách xây dựng APEX bằng hệ thống xây dựng Android. Sau đây là ví dụ về Android.bp cho 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

Các loại tệp và vị trí trong APEX

Loại tệp Vị trí ở APEX
Thư viện chia sẻ /lib/lib64 ( /lib/arm cho nhánh được dịch trong x86)
Thực thi /bin
Thư viện Java /javalib
Bản dựng sẵn /etc

Sự 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 tệp thực thi hoặc lib được chia sẻ gốc. Ví dụ: nếu libFoo phụ thuộc vào libBar thì hai lib sẽ được bao gồm khi chỉ 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ả giao diện nhị phân ứng dụng chính và phụ (ABI) của thiết bị. Nếu APEX nhắm mục tiêu đến các thiết bị có ABI duy nhất (nghĩa là chỉ 32 bit hoặc chỉ 64 bit), thì chỉ các thư viện có ABI tương ứng mới được cài đặt.

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

  • Nếu thiết bị chỉ có 32 bit thì chỉ cài đặt biến thể 32 bit của tệp nhị phân.
  • Nếu thiết bị chỉ có 64 bit thì chỉ cài đặt biến thể 64 bit của tệp nhị phân.

Để 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, hãy sử dụng thuộc tính multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] .

  • first : Phù hợp với ABI chính của thiết bị. Đây là mặc định cho nhị phân.
  • lib32 : Khớp với ABI 32 bit của thiết bị, nếu được hỗ trợ.
  • lib64 : Phù hợp với ABI 64-bit của thiết bị được hỗ trợ.
  • prefer32 : Phù hợ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ợ, hãy khớp với ABI 64 bit.
  • both : Khớp với cả hai ABI. Đây là mặc định cho native_shared_libraries .

Các thuộc tính java , librariesprebuilts là bất khả tri của ABI.

Ví dụ này dành cho thiết bị hỗ trợ 32/64 và không thích 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ý vào mỗi APEX bằng các phím khác nhau. Khi cần có khóa mới, hãy tạo cặp khóa công khai và tạo mô-đun apex_key . Sử dụng thuộc tính key để ký APEX bằng khóa. Khóa chung được tự động đưa vào APEX với 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 khóa chung ( foo ) trở thành ID của khóa. ID của khóa dùng để ký APEX được ghi bằng APEX. Trong thời gian chạy, apexd xác minh APEX bằng khóa chung có cùng ID trong thiết bị.

ký kết APEX

Ký APEX giống như cách bạn ký APK. Ký APEX hai lần; một lần cho hệ thống tệp nhỏ (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 theo một trong ba cách sau:

  • Chưa được đặt: Nếu không có giá trị nào được đặt, APEX được ký bằng chứng chỉ có tại PRODUCT_DEFAULT_DEV_CERTIFICATE . Nếu không có cờ nào được đặt, đường dẫn mặc định là build/target/product/security/testkey .
  • <name> : APEX được ký bằng chứng chỉ <name> trong cùng thư mục với PRODUCT_DEFAULT_DEV_CERTIFICATE .
  • :<name> : APEX được ký bằng chứng chỉ được xác định bởi mô-đun Soong có tên <name> . Mô-đun chứng chỉ có thể được định nghĩa như sau.
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 supportsRebootlessUpdate được đặt thành true trong apex_manifest.json và APEX hiện được cài đặt không được sử dụng (ví dụ: mọi dịch vụ trong đó đã bị dừng), thì APEX mới có thể được cài đặt mà không cần khởi động lại với 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 vào thư mục /apex/<apex_name>@<version> . Có thể gắn nhiều phiên bản của cùng một APEX cùng một 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 được gắn kết tại /apex/<apex_name> .

Khách hàng có thể sử dụng đường dẫn được gắn kết để đọ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ị được 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 phiên bản cập nhật của APEX được cài đặt trong /data/apex , đường dẫn sẽ trỏ đến APEX mới sau khi khởi động lại.

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

Để 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 tùy 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 tệp .rc mới cho dịch vụ được cập nhật. Sử dụng tùy 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
    

Định nghĩa dịch vụ chỉ có thể được xác định trong tệp .rc của APEX. Trình kích hoạt hành động không được hỗ trợ trong APEXes.

Nếu một dịch vụ được đánh dấu là có thể cập nhật khởi động trước khi 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 APEX hoàn tất.

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

Đặt thuộc tính hệ thống sau 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)

ĐỈNH phẳng

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

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

Kích hoạt APEX dẹt không liên quan đến thiết bị vòng lặp. Toàn bộ thư mục /system/apex/my.apex được gắn trực tiếp vào /apex/name@ver .

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

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

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

APEX được nén

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

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

Tính năng nén không cung cấp sự tối ưu hóa cho những mục sau:

  • Các APEX của Bootstrap bắt buộc phải được gắn từ rất sớm trong trình tự khởi động.

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

  • APEX lib được chia sẻ động. Vì apexd luôn kích hoạt cả hai phiên bản của các APEX đó (được cài đặt sẵn và nâng cấp), nên việc nén chúng sẽ không tăng thêm giá trị.

Định dạng tệp APEX nén

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

Diagram shows the format of a compressed APEX file

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

Ở cấp cao nhất, tệp APEX được nén là tệp zip chứa tệp apex gốc ở dạng xì hơi với mức nén là 9 và 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 9 Đây là file APEX 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 của chúng trong original_apex .

Xây dựng APEX nén

APEX đã nén có thể được xây dựng bằng công cụ apex_compression_tool.py có tại system/apex/tools .

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

Trong Android.bp , việc tệp APEX có thể nén được hay không được kiểm soát bởi 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 xem hình ảnh hệ thống được tạo từ nguồn có phải chứa tệp APEX nén hay không.

Đố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 đặ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ó phần mở rộng .capex . Tiện ích mở rộng giúp phân biệt giữa phiên bản nén và không nén của tệp APEX dễ dàng hơn.

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 khi khởi động

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

Hãy xem ví dụ sau đây như một minh họa cho quá trình được mô tả ở trên.

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

  1. Tệp original_apex bên trong /system/apex/com.android.foo.capex đượ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. Việc kiểm tra xác minh được thực hiện trên /data/apex/decompressed/com.android.foo@37.apex để đảm bảo tính hợp lệ của nó: apexd kiểm tra khóa chung được gói trong /data/apex/decompressed/com.android.foo@37.apex để xác minh rằng nó bằng với gói được gói 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ó tác động đến việc phân phối và ứng dụng OTA. Vì bản cập nhật OTA có thể chứa tệp APEX nén có cấp phiên bản cao hơn tệp đang hoạt động trên thiết bị nên phải dành 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 hiển thị hai 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. Điều này có thể được sử dụng để xác minh rằng thiết bị có đủ dung lượng trước khi tải xuống OTA.
  • reserveSpaceForCompressedApex - dành dung lượng trên đĩa để apexd sử dụng trong tương lai nhằm giải nén các tệp APEX đã nén bên trong gói OTA.

Trong trường hợp cập nhật OTA A/B, apexd sẽ cố gắng giải nén ở chế độ nền như một phần của quy trình OTA sau cài đặt. Nếu giải nén không thành công, apexd sẽ thực hiện giải nén trong quá trình khởi động áp dụng bản cập nhật OTA.

Các lựa chọn thay thế được cân nhắc khi phát triển APEX

Dưới đây là một số tùy chọn mà AOSP đã cân nhắc khi thiết kế định dạng tệp APEX và lý do chúng được bao gồm hoặc 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 , rất mạnh mẽ, hoàn thiện và mạnh mẽ. Tuy nhiên, chúng không được áp dụng cho APEX vì chúng không thể bảo vệ các gói sau khi cài đặt. Việc xác minh chỉ được thực hiện khi các gói đang được cài đặt. Những 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 bị phát hiện. Đây là một hồi quy dành cho Android trong đó tất cả các thành phần hệ thống được lưu trữ trong hệ thống tệp chỉ đọc có tính toàn vẹn được bảo vệ bởi dm-verity cho mọi I/O. Bất kỳ hành vi giả mạo nào đối với các thành phần hệ thống đều phải bị cấm hoặc phải bị phát hiện để thiết bị có thể từ chối khởi động nếu bị xâm phạm.

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

Các tệp trong vùng chứa APEX là từ các phân vùng tích hợp sẵn (ví dụ: phân vùng /system ) được bảo vệ bởi dm-verity, trong đó mọi sửa đổi đối với tệp đều 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 cho các tệp, tất cả các tệp trong APEX được lưu trữ trong hình ảnh hệ thống tệp được ghép nối với cây băm và bộ mô tả vbmeta. Nếu không có dm-verity, một APEX trong phân vùng /data sẽ dễ bị ảnh hưởng bởi các sửa đổi ngoài ý muốn được thực hiện sau khi nó được xác minh và cài đặt.

Trên thực tế, phân vùng /data còn được bảo vệ bởi các lớp mã hóa như dm-crypt. Mặc dù điều này cung cấp một số mức độ bảo vệ chống giả mạo nhưng mục đích chính của nó là quyền riêng tư chứ không phải tính toàn vẹn. Khi kẻ tấn công giành được quyền truy cập vào phân vùng /data , không thể có biện pháp bảo vệ nào nữa và đây lại là sự hồi quy so với mọi thành phần hệ thống nằm 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ức độ bảo vệ nội dung.

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

Các tệp thành phần hệ thống được đóng gói trong APEX có thể truy cập được thông qua các đường dẫn mới như /apex/<name>/lib/libfoo.so . Khi các tệp là một phần của phân vùng /system , chúng có thể được truy cập thông qua các đường dẫn như /system/lib/libfoo.so . Máy khách của tệp APEX (các tệp APEX khác hoặc nền tảng) phải sử dụng các đường dẫn mới. Bạn có thể cần phải cập nhật mã hiện có do 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 tệp APEX lên phân vùng /system , nhóm Android đã quyết định không phủ các tệp trên phân vùng /system vì điều này có thể ảnh hưởng đến hiệu suất khi số lượng tệp được phủ ( thậm chí có thể xếp chồng lên nhau) tăng lên.

Một tùy chọn khác là chiếm đoạt các chức năng 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 các đường dẫn tương ứng của chúng trong /apex . Nhóm Android đã loại bỏ tùy chọn này vì không thể thay đổi tất cả các chức năng chấp nhận đường dẫn. Ví dụ: một số ứng dụng liên kết tĩnh Bionic, ứng dụng này triển khai các chức năng. Trong những trường hợp như vậy, những ứng dụng đó sẽ không được chuyển hướng.