Đị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 các mô-đun hệ thống cấp thấp hơn. Định dạng này hỗ trợ 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 mẫu là dịch vụ và thư viện gốc, lớp trừu tượng phần cứng (HALs), 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.
Thông tin khái quát
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 trình cài đặt gói (chẳng hạn như ứng dụng Cửa hàng Google Play), nhưng việc 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 sẽ có những hạn chế sau:
- Không thể sử dụng các mô-đun dựa trên APK ở giai đoạn đầu của trình tự khởi động. Trình quản lý gói là kho lưu trữ trung tâm chứa thông tin về các ứng dụng. Bạn chỉ có thể bắt đầu trình quản lý này 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 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ý 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 bài viết 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.
Hình 1. Định dạng tệp 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 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 và phiên bản gói, giúp 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, chẳng hạn 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 từ 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 thường cũng có trong apex_manifest.json
.
Bạn nên dùng apex_manifest.json
thay vì 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ụ phát hành ứ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 dm-verity hỗ trợ. Hình ảnh được gắn kết trong thời gian chạy thông qua một 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 đưa vào 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ê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 APEX AOSP. 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ị của công ty đó sử dụng.
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ể.
Trình 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 ở giai đoạn đầu của 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
. Theo mặc định, trình quản lý APEX sẽ 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 như sau.
- 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.
- Trình quản lý gói bắt đầu quy trình cài đặt. Khi nhận ra tệp là một APEX, trình quản lý gói sẽ chuyển quyền kiểm soát cho người quản lý APEX.
- Trình quản lý APEX xác minh tệp APEX.
- Nếu tệp APEX đã được xác minh, thì 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 được kích hoạt vào lần khởi động tiếp theo.
- Trình yêu cầu cài đặt sẽ nhận được thông báo truyền tin sau khi xác minh gói thành công.
- Để tiếp tục quá trình cài đặt, bạn phải khởi động lại hệ thống.
Ở lần khởi động tiếp theo, trình quản lý APEX sẽ khởi động, đọc cơ sở dữ liệu nội bộ và thực hiện các thao tác sau cho từng tệp APEX được liệt kê:
- Xác minh tệp APEX.
- Tạo thiết bị vòng lặp từ tệp APEX.
- Tạo một thiết bị khối trình ánh xạ thiết bị trên thiết bị vòng lặp.
- Gắn thiết bị khối của 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ả tệp APEX được liệt kê trong cơ sở dữ liệu nội bộ được gắn, 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 một APEX cụ thể được gắn, nhờ đó có thể truy cập vào các tệp đó.
Tệp APEX là các tệp APK
Tệp APEX là tệp APK hợp lệ vì chúng là tệp lưu trữ zip đã ký (sử dụng lược đồ chữ ký APK) chứa tệp AndroidManifest.xml
. Việc 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 gói name
, versionCode
và targetSdkVersion
, minSdkVersion
và maxSdkVersion
(không bắt buộc) để nhắm mục tiêu chi tiết. Thông tin này cho phép phân phối tệp APEX qua các kênh hiện có, chẳng hạn 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 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. Việc có thể cập nhật loại tệp hay không tuỳ thuộc vào nền tảng và mức độ ổn định của định nghĩa 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, tệp apex_payload.img
(cụ thể là mã mô tả vbmeta được thêm vào apex_payload.img
) được ký bằng một khoá.
Sau đó, toàn bộ APEX được ký bằng lược đồ chữ ký APK v3. Hai khoá khác nhau được sử dụng trong quy 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ô tả vbmeta sẽ đượ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. Mỗi APEX phải được ký bằng các khoá khác nhau và được thực thi cả tại thời điểm tạo bản dựng và tại 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
. Phân vùng này đã vượt quá dm-verity, vì vậy, các tệp APEX được gắn trực tiếp trên thiết bị lặp lại.
Nếu có APEX trong một phân vùng tích hợp, bạn có thể cập nhật APEX bằng cách cung cấp một 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ài đặt sẽ che phiên bản đã có trong phân vùng tích hợp. Tuy nhiên, không giống như tệp 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ề hạt nhân
Để hỗ trợ các mô-đun chính của APEX trên thiết bị Android, bạn cần có các tính năng hạt nhân Linux sau: trình điều khiển vòng lặp lại và dm-verity. Trình điều khiển vòng lặp gắn hình ảnh hệ thống tệp trong mô-đun APEX và dm-verity xác minh mô-đunAPEX.
Hiệu suất của trình điều khiển loopback và dm-verity đóng vai trò 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ợ
Các 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 trở lên. Các 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á hạt nhân bắt buộc
Các bản vá nhân bắt buộc để hỗ trợ các mô-đun APEX được đưa vào cây phổ biến 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ị được nâng cấp từ Android 9 lên Android 10 và muốn hỗ trợ các mô-đun APEX. Để nhận các bản vá bắt buộc, bạn nên hợp nhất xuống từ nhánh android-4.4
. Dưới đây là danh sách các bản vá riêng lẻ bắt buộc cho nhân 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: block/loop: set hw_sectors (4.4)
- UPSTREAM: vòng lặp: Thêm LOOP_SET_BLOCK_SIZE trong khả năng tương thích ioctl (4.4)
- ANDROID: mnt: Sửa lỗi next_descendent (4.4)
- ANDROID: mnt: quá trình kết nối lại sẽ phổ biến đến các máy khách (slaves) (4.4)
- ANDROID: mnt: Truyền tải đúng cách việc gắn lại (4.4)
- Huỷ bỏ "ANDROID: dm verity: add minimum prefetch size" (ANDROID: dm verity: thêm kích thước tải trước tối thiểu) (4.4)
- UPSTREAM: vòng lặp: bỏ bộ nhớ đệm nếu thay đổi độ lệch hoặc khối_kích thước (block_size) (4.4)
Phiên bản kernel 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 xuống từ nhánh android-common
.
Các tuỳ chọn cấu hình hạt nhân bắt buộc
Danh sách sau đây cho thấy các yêu cầu về cấu hình cơ sở để 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à yêu cầu hiện có trên 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 của nhân đáp ứng các yêu cầu sau:
- KHÔNG được đặt
loop.max_loop
loop.max_part
phải nhỏ hơn hoặc bằng 8
Tạo 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 là 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 và vị trí tệp trong APEX
Loại tệp | Vị trí trong APEX |
---|---|
Thư viện chia sẻ | /lib và /lib64 (/lib/arm cho arm đã dịch trong 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 thư viện này sẽ được đưa vào 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 đến các thiết bị có một ABI (tức 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ư 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ỉ hỗ trợ 64 bit, thì chỉ biến thể 64 bit của tệp nhị phân được cài đặt.
Để thêm quyền kiểm soát chi tiết đối với ABI của các thư viện gốc và tệp nhị phân, hãy sử dụng các thuộc tính multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
first
: So khớp với ABI chính của thiết bị. Đây là giá trị mặc định cho tệp nhị phân.lib32
: 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ợ, hãy so khớp với ABI 64 bit.both
: Trùng khớp với cả hai ABI. Đây là giá trị mặc định chonative_shared_libraries
.
Các thuộc tính java
, libraries
và prebuilts
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 – riêng tư và tạo một mô-đun apex_key
. Sử dụng thuộc tính key
để ký APEX bằng khoá. Khoá công khai được tự động đưa vào APEX với tên avb_pubkey
.
# create an rsa key pairopenssl genrsa -out foo.pem 4096
# extract the public key from the key pairavbtool extract_public_key --key foo.pem --output foo.avbpubkey
# in Android.bpapex_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 khoá. 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 cách sử dụng một 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ý 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
theo một trong 3 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 tại
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Nếu bạn không đặt cờ nào, đường dẫn sẽ 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ớiPRODUCT_DEFAULT_DEV_CERTIFICATE
.:<name>
: APEX được ký bằng chứng chỉ do mô-đun Soong xác định có tên là<name>
. Bạn có thể xác định mô-đun chứng chỉ 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 bạn đặt supportsRebootlessUpdate
thành true
trong apex_manifest.json
và không sử dụng APEX đang được cài đặt (ví dụ: mọi dịch vụ trong đó đã 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 kết tại thư mục /apex/<apex_name>@<version>
. 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, đường dẫn tương ứng với phiên bản mới nhất được liên kết gắn 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:
- OEM hoặc ODM tải trước APEX trong
/system/apex
khi thiết bị được vận chuyển. - Các tệp trong APEX được truy cập thông qua đường dẫn
/apex/<apex_name>/
. - Khi một phiên bản APEX cập nhật đượ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ách cập nhật dịch vụ bằng APEX:
Đánh dấu dịch vụ trong phân vùng hệ thống là có thể cập nhật. Thêm tuỳ chọn
updatable
vào phần khai báo dịch vụ./system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
Tạo một tệp
.rc
mới cho dịch vụ đã cập nhật. Sử dụng tuỳ chọnoverride
để 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. Điều kiện kích hoạt 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 bắt đầu sẽ bị trì hoãn cho đến khi quá trình kích hoạt APEX hoàn tất.
Định cấu hình hệ thống để hỗ trợ các bản 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 hạt nhân cũ để hỗ trợ đầy đủ APEX. Ví dụ: nhân có thể đã được tạo 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 một APEX.
APEX làm phẳng là một APEX được xây dựng đặc biệt, có thể kích hoạt trên các thiết bị có nhân hệ điều hành cũ. Các tệp trong APEX được làm 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. Ví dụ: lib/libFoo.so
trong một APEX đã làm phẳng my.apex
được cài đặt vào /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
.
Bạn 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 APEX đã cập nhật xuống 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ả các APEX theo mặc định đều được làm phẳng, trừ phi bạn định cấu hình rõ ràng thiết bị của mình để tạo các APEX không được làm phẳng nhằm hỗ trợ việc 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 gửi các tệp APEX được ký sẵn cho các dự án như Mainline. Các APEX không được ký trước (tức là được tạo từ nguồn) cũng không được làm phẳng và phải được ký bằng các khoá thích hợp. Thiết bị phải kế thừa từ updatable_apex.mk
như giải thích trong phần Cập nhật dịch vụ bằng một APEX.
APEX nén
Android 12 trở lên có tính năng nén APEX để giảm tác động đế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 cho APEX, mặc dù phiên bản cài đặt sẵn không còn được sử dụng nữa, nhưng phiên bản này vẫn chiếm cùng một dung lượng. Không gian đã chiếm vẫn không dùng được.
Tính năng nén APEX giảm thiểu tác động đến bộ nhớ này bằng cách sử dụng một tập hợp các 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 tối ưu hoá những tệp sau:
Các APEX trong quá trình khởi động cần phải được gắn kết từ rất sớm trong trình tự khởi động.
Các 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
. Bạn có thể xem danh sách đầy đủ các APEX có thể cập nhật trên trang 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 APEX (được cài đặt sẵn và nâng cấp), nên việc nén các phiên bản này sẽ không làm tăng giá trị.
Định dạng tệp APEX được nén
Đây là định dạng của tệp APEX nén.
Hình 2. Định dạng tệp APEX được nén
Ở cấp cao nhất, tệp APEX nén là tệp zip chứa tệp apex gốc ở dạng rút gọn 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
: đã nén với mức nén 9 Đây là tệp 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ỉ lưu trữ
Các tệp apex_manifest.pb
, AndroidManifest.xml
và apex_pubkey
là bản sao của các tệp tương ứng trong original_apex
.
Tạo APEX nén
Bạn có thể tạo APEX nén bằng công cụ apex_compression_tool.py
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
, thuộc tính compressible
sẽ kiểm soát việc tệp APEX có thể nén hay không:
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 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 một bản dựng nén các APEX bằng cách đặt OVERRIDE_PRODUCT_COMPRESSED_APEX=
thành true
.
Các tệp APEX nén do hệ thống xây dựng tạo có đuôi .capex
.
Tiện ích này giúp bạn dễ dàng phân biệt giữa các phiên bản nén và không nén của 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 một APEX được 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 đã 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 minh hoạ quy trình được mô tả ở trên.
Hãy xem /system/apex/com.android.foo.capex
là một APEX nén đang được kích hoạt, với versionCode 37.
- 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
. restorecon /data/apex/decompressed/com.android.foo@37.apex
được thực hiện để xác minh nhãn SELinux chính xác.- Các bướ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 khoá này:apexd
kiểm tra khoá công khai đi kèm trong/data/apex/decompressed/com.android.foo@37.apex
để xác minh rằng khoá này bằng khoá đi kèm trong/system/apex/com.android.foo.capex
. - 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
. - Logic kích hoạt thông thường cho các tệp APEX chưa 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 nén có ảnh hưở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 đượ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ị, nên bạn phải đặt trước 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ệpAPEX trong gói OTA. Bạn có thể sử dụng URL này để xác minh rằng thiết bị có đủ dung lượng trước khi tải bản cập nhật OTA xuống.reserveSpaceForCompressedApex
– dành trước 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ẽ thử giải nén ở chế độ nền trong quy trình OTA sau khi cài đặt. Nếu không giải nén được, apexd
sẽ giải nén trong quá trình khởi động để áp dụng bản cập nhật OTA.
Các giải pháp thay thế được xem xét khi phát triển APEX
Dưới đây là một số tuỳ chọn mà AOSP đã xem xét khi thiết kế định dạng tệp APEX và lý do các tuỳ chọn đó đượ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ư dpkg
và rpm
, mạnh mẽ, trưởng thành và mạnh mẽ. Tuy nhiên, các tệp này không được sử dụng cho APEX vì 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.
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 phép hồi quy cho Android, trong đó tất cả thành phần hệ thống được lưu trữ trong các hệ thống tệp chỉ có thể đọc có tính toàn vẹn được bảo vệ bằng dm-verity cho mỗi I/O. Mọi hành vi can thiệp vào các thành phần hệ thống đều phải bị cấm hoặc có thể phát hiện được để 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 đến 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 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ột cấp độ bảo mật cho các tệp, tất cả tệp trong APEX đều được lưu trữ trong một hình ảnh hệ thống tệp được ghép nối với cây băm và chỉ số mô tả vbmeta. Nếu không có dm-verity, APEX trong phân vùng /data
sẽ dễ bị sửa đổi ngoài ý muố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ư
dm-crypt. Mặc dù phương thức này cung cấp một mức độ bảo vệ nhất định chống lại hành vi can thiệp, nhưng mục đích chính của phương thức này là quyền riêng tư chứ không phải tính toàn vẹn. Khi kẻ tấn công 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 khác. Đây lại là một 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ộ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
. Khi các tệp thuộc phân vùng /system
, bạn có thể truy cập vào các tệp đó thông qua các đường dẫn như /system/lib/libfoo.so
. Ứng dụng của tệp APEX (các tệp APEX khác hoặc nền tảng) phải sử dụng đường dẫn mới. Bạn có thể cần cập nhật mã hiện có do thay đổi đường dẫn.
Mặc dù có 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ưng nhóm Android đã quyết định không phủ các tệp lên phân vùng /system
vì điều này có thể ảnh hưởng đến hiệu suất vì số lượng tệp bị phủ (thậm chí có thể xếp chồng lên nhau) tăng lên.
Một lựa chọn khác là xâm nhập vào các hàm truy cập tệp như open
, stat
và readlink
để 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 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.