Format kontenera Android Pony EXpress (APEX) został wprowadzony w systemie Android 10 i jest używany podczas instalacji modułów systemowych niższego poziomu. Ten format ułatwia aktualizację komponentów systemu, które nie mieszczą się w standardowym modelu aplikacji Android. Niektóre przykładowe komponenty to natywne usługi i biblioteki, warstwy abstrakcji sprzętu ( HAL ), środowisko wykonawcze ( ART ) i biblioteki klas.
Termin „APEX” może również odnosić się do pliku APEX.
Tło
Chociaż system Android obsługuje aktualizacje modułów pasujących do standardowego modelu aplikacji (na przykład usług, działań) za pośrednictwem aplikacji instalujących pakiety (takich jak aplikacja Sklep Google Play), używanie podobnego modelu dla komponentów systemu operacyjnego niższego poziomu ma następujące wady:
- Modułów opartych na APK nie można używać na początku sekwencji rozruchowej. Menedżer pakietów jest centralnym repozytorium informacji o aplikacjach i można go uruchomić jedynie z poziomu menedżera aktywności, który jest gotowy na późniejszym etapie procedury rozruchu.
- Format APK (w szczególności manifest) jest przeznaczony dla aplikacji na Androida, a moduły systemowe nie zawsze są dobrze dopasowane.
Projekt
W tej sekcji opisano projekt wysokiego poziomu formatu pliku APEX i menedżera APEX, który jest usługą zarządzającą plikami APEX.
Więcej informacji o tym, dlaczego wybrano ten projekt dla APEX, można znaleźć w artykule Alternatywy brane pod uwagę przy opracowywaniu APEX .
formacie APEX
Jest to format pliku APEX.
Rysunek 1. Format pliku APEX
Na najwyższym poziomie plik APEX jest plikiem zip, w którym przechowywane są pliki nieskompresowane i rozmieszczone w granicach 4 KB.
Cztery pliki w pliku APEX to:
-
apex_manifest.json
-
AndroidManifest.xml
-
apex_payload.img
-
apex_pubkey
Plik apex_manifest.json
zawiera nazwę i wersję pakietu, które identyfikują plik APEX. To jest bufor protokołu ApexManifest
w formacie JSON.
Plik AndroidManifest.xml
umożliwia plikowi APEX korzystanie z narzędzi i infrastruktury związanej z plikiem APK, takiej jak ADB, PackageManager i aplikacje instalujące pakiety (takie jak Sklep Play). Na przykład plik APEX może wykorzystywać istniejące narzędzie, takie jak aapt
do sprawdzania podstawowych metadanych z pliku. Plik zawiera nazwę pakietu i informacje o wersji. Informacje te są ogólnie dostępne również w apex_manifest.json
.
apex_manifest.json
jest zalecany zamiast AndroidManifest.xml
w przypadku nowego kodu i systemów obsługujących APEX. AndroidManifest.xml
może zawierać dodatkowe informacje o kierowaniu, z których mogą korzystać istniejące narzędzia do publikowania aplikacji.
apex_payload.img
to obraz systemu plików ext4 wspierany przez dm-verity. Obraz jest montowany w czasie wykonywania za pomocą urządzenia z pętlą zwrotną. W szczególności drzewo skrótów i blok metadanych są tworzone przy użyciu biblioteki libavb
. Ładunek systemu plików nie jest analizowany (ponieważ obraz powinien dać się zamontować na miejscu). Zwykłe pliki znajdują się w pliku apex_payload.img
.
apex_pubkey
to klucz publiczny używany do podpisywania obrazu systemu plików. W czasie wykonywania ten klucz gwarantuje, że pobrany plik APEX zostanie podpisany tą samą jednostką, która podpisuje ten sam plik APEX na wbudowanych partycjach.
Wytyczne dotyczące nazewnictwa APEX
Aby zapobiec konfliktom nazewnictwa między nowymi APEXami w miarę rozwoju platformy, skorzystaj z następujących wskazówek dotyczących nazewnictwa:
-
com.android.*
- Zarezerwowane dla APEXów AOSP. Nie jest unikalny dla żadnej firmy ani urządzenia.
-
com.<companyname>.*
- Zarezerwowane dla firmy. Potencjalnie używany przez wiele urządzeń tej firmy.
-
com.<companyname>.<devicename>.*
- Zarezerwowane dla APEXów unikalnych dla określonego urządzenia (lub podzbioru urządzeń).
Menedżer APEX
Menedżer APEX (lub apexd
) to samodzielny, natywny proces odpowiedzialny za weryfikację, instalowanie i odinstalowywanie plików APEX. Proces ten jest uruchamiany i gotowy na początku sekwencji rozruchowej. Pliki APEX są zwykle preinstalowane na urządzeniu w katalogu /system/apex
. Menedżer APEX domyślnie używa tych pakietów, jeśli nie są dostępne żadne aktualizacje.
Sekwencja aktualizacji APEX wykorzystuje klasę PackageManager i jest następująca.
- Plik APEX jest pobierany za pośrednictwem aplikacji instalatora pakietów, ADB lub innego źródła.
- Menedżer pakietów rozpoczyna procedurę instalacji. Po rozpoznaniu, że plik jest plikiem APEX, menedżer pakietów przekazuje kontrolę menedżerowi APEX.
- Menedżer APEX weryfikuje plik APEX.
- Jeśli plik APEX zostanie zweryfikowany, wewnętrzna baza danych menedżera APEX zostanie zaktualizowana, aby odzwierciedlić fakt, że plik APEX zostanie aktywowany przy następnym uruchomieniu.
- Osoba żądająca instalacji odbiera transmisję po pomyślnej weryfikacji pakietu.
- Aby kontynuować instalację, należy ponownie uruchomić system.
Przy następnym uruchomieniu uruchamia się menedżer APEX, odczytuje wewnętrzną bazę danych i wykonuje następujące czynności dla każdego wymienionego pliku APEX:
- Weryfikuje plik APEX.
- Tworzy urządzenie pętli zwrotnej z pliku APEX.
- Tworzy urządzenie blokowe mapowania urządzeń na urządzeniu z pętlą zwrotną.
- Montuje urządzenie blokowe mapowania urządzeń na unikalnej ścieżce (na przykład
/apex/ name @ ver
).
Po zamontowaniu wszystkich plików APEX znajdujących się w wewnętrznej bazie danych menedżer APEX zapewnia usługę segregatora dla innych komponentów systemu w celu uzyskania informacji o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wysyłać zapytania o listę plików APEX zainstalowanych w urządzeniu lub o dokładną ścieżkę, w której zamontowany jest określony plik APEX, aby można było uzyskać dostęp do plików.
Pliki APEX to pliki APK
Pliki APEX są prawidłowymi plikami APK, ponieważ są podpisanymi archiwami zip (przy użyciu schematu podpisów APK) zawierającymi plik AndroidManifest.xml
. Dzięki temu pliki APEX mogą korzystać z infrastruktury plików APK, takiej jak aplikacja instalująca pakiety, narzędzie do podpisywania i menedżer pakietów.
Plik AndroidManifest.xml
w pliku APEX jest minimalny i składa się z name
pakietu, versionCode
i opcjonalnych targetSdkVersion
, minSdkVersion
i maxSdkVersion
na potrzeby precyzyjnego kierowania. Informacje te umożliwiają dostarczanie plików APEX za pośrednictwem istniejących kanałów, takich jak aplikacje instalujące pakiety i ADB.
Obsługiwane typy plików
Format APEX obsługuje następujące typy plików:
- Natywne udostępnione biblioteki
- Natywne pliki wykonywalne
- pliki JAR
- Pliki danych
- Pliki konfiguracyjne
Nie oznacza to, że APEX może aktualizować wszystkie typy plików. To, czy typ pliku można zaktualizować, zależy od platformy i stabilności definicji interfejsów dla typów plików.
Opcje podpisywania
Pliki APEX są podpisywane na dwa sposoby. Najpierw plik apex_payload.img
(w szczególności deskryptor vbmeta dołączony do apex_payload.img
) jest podpisywany kluczem. Następnie cały APEX jest podpisywany przy użyciu schematu podpisu APK v3 . W tym procesie używane są dwa różne klucze.
Po stronie urządzenia instalowany jest klucz publiczny odpowiadający kluczowi prywatnemu użytemu do podpisania deskryptora vbmeta. Menedżer APEX używa klucza publicznego do weryfikacji plików APEX, które mają zostać zainstalowane. Każdy plik APEX musi być podpisany różnymi kluczami i jest wymuszany zarówno w czasie kompilacji, jak i w czasie wykonywania.
APEX w wbudowanych partycjach
Pliki APEX mogą znajdować się na wbudowanych partycjach, takich jak /system
. Partycja jest już na poziomie dm-verity, więc pliki APEX są montowane bezpośrednio nad urządzeniem zwrotnym.
Jeśli plik APEX znajduje się na wbudowanej partycji, plik APEX można zaktualizować, dostarczając pakiet APEX o tej samej nazwie pakietu i kodzie wersji większym lub równym. Nowy plik APEX jest przechowywany w /data
i podobnie jak pliki APK, nowo zainstalowana wersja przesłania wersję już istniejącą na wbudowanej partycji. Jednak w przeciwieństwie do plików APK nowo zainstalowana wersja APEX jest aktywowana dopiero po ponownym uruchomieniu.
Wymagania jądra
Aby obsługiwać główne moduły APEX na urządzeniu z Androidem, wymagane są następujące funkcje jądra Linuksa: sterownik pętli zwrotnej i dm-verity. Sterownik pętli zwrotnej montuje obraz systemu plików w module APEX, a dm-verity weryfikuje moduł APEX.
Wydajność sterownika pętli zwrotnej i dm-verity jest ważna dla osiągnięcia dobrej wydajności systemu podczas korzystania z modułów APEX.
Obsługiwane wersje jądra
Moduły główne APEX są obsługiwane na urządzeniach korzystających z jądra w wersji 4.4 lub wyższej. Nowe urządzenia uruchamiane z systemem Android 10 lub nowszym muszą używać jądra w wersji 4.9 lub nowszej, aby obsługiwać moduły APEX.
Wymagane poprawki jądra
Wymagane poprawki jądra do obsługi modułów APEX znajdują się we wspólnym drzewie Androida. Aby uzyskać poprawki obsługujące APEX, użyj najnowszej wersji wspólnego drzewa Androida.
Wersja jądra 4.4
Ta wersja jest obsługiwana tylko dla urządzeń, które zostały zaktualizowane z Androida 9 do Androida 10 i chcą obsługiwać moduły APEX. Aby uzyskać wymagane poprawki, zdecydowanie zaleca się połączenie w dół z gałęzi android-4.4
. Poniżej znajduje się lista wymaganych poszczególnych poprawek dla wersji jądra 4.4.
- GÓRA: pętla: dodaj ioctl do zmiany rozmiaru bloku logicznego ( 4.4 )
- BACKPORT: blok/pętla: ustaw hw_sectors ( 4.4 )
- GÓRA: pętla: Dodaj LOOP_SET_BLOCK_SIZE w zgodnym ioctl ( 4.4 )
- ANDROID: mnt: Napraw next_descendent ( 4.4 )
- ANDROID: mnt: remount powinien rozprzestrzeniać się na niewolników niewolników ( 4.4 )
- ANDROID: mnt: Poprawne propagowanie ponownego montażu ( 4.4 )
- Przywróć „ANDROID: dm verity: dodaj minimalny rozmiar pobierania wstępnego” ( 4.4 )
- GÓRA: pętla: upuść pamięci podręczne, jeśli zmieniono przesunięcie lub rozmiar_bloku ( 4.4 )
Wersje jądra 4.9/4.14/4.19
Aby uzyskać wymagane poprawki dla wersji jądra 4.9/4.14/4.19, wykonaj połączenie w dół z gałęzi android-common
.
Wymagane opcje konfiguracji jądra
Poniższa lista przedstawia podstawowe wymagania konfiguracyjne dotyczące obsługi modułów APEX, które zostały wprowadzone w systemie Android 10. Elementy oznaczone gwiazdką (*) oznaczają istniejące wymagania z systemu Android 9 i starszych wersji.
(*) 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
Wymagania dotyczące parametrów wiersza poleceń jądra
Aby obsługiwać APEX, upewnij się, że parametry wiersza poleceń jądra spełniają następujące wymagania:
- NIE można ustawić
loop.max_loop
-
loop.max_part
musi wynosić <= 8
Zbuduj APEX
W tej sekcji opisano, jak zbudować APEX przy użyciu systemu kompilacji Androida. Poniżej znajduje się przykład Android.bp
dla APEX o nazwie 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",
}
przykład apex_manifest.json
:
{
"name": "com.android.example.apex",
"version": 1
}
przykład file_contexts
:
(/.*)? u:object_r:system_file:s0
/sub(/.*)? u:object_r:sub_file:s0
/sub/file3 u:object_r:file3_file:s0
Typy plików i lokalizacje w APEX
Typ pliku | Lokalizacja w APEXie |
---|---|
Wspólne biblioteki | /lib i /lib64 ( /lib/arm dla przetłumaczonego ramienia w x86) |
Pliki wykonywalne | /bin |
Biblioteki Javy | /javalib |
Gotowe | /etc |
Zależności przechodnie
Pliki APEX automatycznie zawierają przechodnie zależności natywnych udostępnionych bibliotek lub plików wykonywalnych. Na przykład, jeśli libFoo
zależy od libBar
, te dwie biblioteki są uwzględniane, gdy we właściwości native_shared_libs
wymieniona jest tylko libFoo
.
Obsługuj wiele ABI
Zainstaluj właściwość native_shared_libs
dla podstawowych i dodatkowych interfejsów binarnych aplikacji (ABI) urządzenia. Jeśli APEX jest przeznaczony dla urządzeń z pojedynczym ABI (to znaczy tylko 32-bitowym lub tylko 64-bitowym), instalowane są tylko biblioteki z odpowiednim ABI.
Zainstaluj właściwość binaries
tylko dla podstawowego interfejsu ABI urządzenia, jak opisano poniżej:
- Jeśli urządzenie jest tylko 32-bitowe, instalowana jest tylko 32-bitowa wersja pliku binarnego.
- Jeśli urządzenie jest tylko 64-bitowe, instalowana jest tylko 64-bitowa wersja pliku binarnego.
Aby dodać szczegółową kontrolę nad interfejsami ABI natywnych bibliotek i plików binarnych, użyj właściwości multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
-
first
: odpowiada podstawowemu interfejsowi ABI urządzenia. Jest to ustawienie domyślne dla plików binarnych. -
lib32
: Pasuje do 32-bitowego ABI urządzenia, jeśli jest obsługiwane. -
lib64
: Pasuje do 64-bitowego ABI urządzenia, które obsługuje. -
prefer32
: Pasuje do 32-bitowego ABI urządzenia, jeśli jest obsługiwane. Jeśli 32-bitowy ABI nie jest obsługiwany, odpowiada 64-bitowemu ABI. -
both
: Pasuje do obu ABI. Jest to ustawienie domyślne dlanative_shared_libraries
.
Właściwości java
, libraries
i prebuilts
są niezależne od ABI.
Ten przykład dotyczy urządzenia, które obsługuje 32/64 i nie preferuje 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
},
},
}
podpisywanie vbmeta
Podpisz każdy APEX różnymi kluczami. Gdy wymagany jest nowy klucz, utwórz parę kluczy publiczny-prywatny i utwórz moduł apex_key
. Użyj właściwości key
, aby podpisać APEX za pomocą klucza. Klucz publiczny jest automatycznie dołączany do pliku APEX pod nazwą 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", }
W powyższym przykładzie nazwa klucza publicznego ( foo
) staje się identyfikatorem klucza. Identyfikator klucza użytego do podpisania APEX jest zapisany w APEX. W czasie wykonywania apexd
weryfikuje APEX przy użyciu klucza publicznego o tym samym identyfikatorze w urządzeniu.
Podpisanie APEX
Podpisuj pliki APEX w taki sam sposób, w jaki podpisujesz pliki APK. Podpisz dwukrotnie APEXy; raz dla mini systemu plików (plik apex_payload.img
) i raz dla całego pliku.
Aby podpisać plik APEX na poziomie pliku, ustaw właściwość certificate
na jeden z trzech sposobów:
- Nie ustawiono: Jeśli nie ustawiono żadnej wartości, plik APEX jest podpisany certyfikatem znajdującym się pod
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Jeśli nie ustawiono żadnej flagi, domyślna ścieżka tobuild/target/product/security/testkey
. -
<name>
: APEX jest podpisany certyfikatem<name>
w tym samym katalogu coPRODUCT_DEFAULT_DEV_CERTIFICATE
. -
:<name>
: APEX jest podpisany certyfikatem zdefiniowanym przez moduł Soong o nazwie<name>
. Moduł certyfikatu można zdefiniować w następujący sposób.
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)
}
Zainstaluj APEX
Aby zainstalować APEX, użyj ADB.
adb install apex_file_name
adb reboot
Jeśli w apex_manifest.json
supportsRebootlessUpdate
ma wartość true
i aktualnie zainstalowany plik APEX jest nieużywany (na przykład wszystkie zawarte w nim usługi zostały zatrzymane), nowy plik APEX można zainstalować bez ponownego uruchamiania za pomocą flagi --force-non-staged
.
adb install --force-non-staged apex_file_name
Użyj APEX-a
Po ponownym uruchomieniu APEX jest montowany w katalogu /apex/<apex_name>@<version>
. Jednocześnie można zamontować wiele wersji tego samego APEX-a. Wśród ścieżek montowania ta odpowiadająca najnowszej wersji jest montowana przez powiązanie w /apex/<apex_name>
.
Klienci mogą używać ścieżki zamontowanej w powiązaniu do odczytywania lub wykonywania plików z APEX.
APEXy są zwykle używane w następujący sposób:
- Producent OEM lub ODM ładuje wstępnie plik APEX w katalogu
/system/apex
po dostarczeniu urządzenia. - Dostęp do plików w APEX można uzyskać poprzez ścieżkę
/apex/<apex_name>/
. - Gdy zaktualizowana wersja APEX jest zainstalowana w
/data/apex
, ścieżka wskazuje na nowy APEX po ponownym uruchomieniu.
Zaktualizuj usługę za pomocą APEX
Aby zaktualizować usługę za pomocą APEX:
Oznacz usługę na partycji systemowej jako nadającą się do aktualizacji. Dodaj opcję
updatable
do definicji usługi./system/etc/init/myservice.rc: service myservice /system/bin/myservice class core user system ... updatable
Utwórz nowy plik
.rc
dla zaktualizowanej usługi. Użyj opcjioverride
, aby przedefiniować istniejącą usługę./apex/my.apex/etc/init.rc: service myservice /apex/my.apex/bin/myservice class core user system ... override
Definicje usług można zdefiniować tylko w pliku .rc
pliku APEX. Wyzwalacze akcji nie są obsługiwane w plikach APEX.
Jeśli usługa oznaczona jako możliwa do aktualizacji zostanie uruchomiona przed aktywacją APEXów, start zostanie opóźniony do momentu zakończenia aktywacji APEXów.
Skonfiguruj system do obsługi aktualizacji APEX
Ustaw następującą właściwość systemową na true
, aby obsługiwać aktualizacje plików APEX.
<device.mk>:
PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true
BoardConfig.mk:
TARGET_FLATTEN_APEX := false
Lub tylko
<device.mk>:
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
Spłaszczony wierzchołek
W przypadku starszych urządzeń aktualizacja starego jądra, aby w pełni obsługiwała APEX, jest czasami niemożliwa lub niewykonalna. Na przykład jądro mogło zostać zbudowane bez CONFIG_BLK_DEV_LOOP=Y
, co jest kluczowe dla zamontowania obrazu systemu plików wewnątrz APEX.
Flattened APEX to specjalnie zbudowany APEX, który można aktywować na urządzeniach ze starszym jądrem. Pliki w spłaszczonym APEXie są instalowane bezpośrednio w katalogu pod wbudowaną partycją. Na przykład lib/libFoo.so
w spłaszczonym pliku APEX my.apex
jest instalowany w /system/apex/my.apex/lib/libFoo.so
.
Aktywacja spłaszczonego APEX-a nie wymaga urządzenia pętlowego. Cały katalog /system/apex/my.apex
jest bezpośrednio podłączony do /apex/name@ver
.
Spłaszczonych APEXów nie można zaktualizować poprzez pobranie zaktualizowanych wersji APEXów z sieci, ponieważ pobranych APEXów nie można spłaszczyć. Spłaszczone APEXy można aktualizować tylko za pośrednictwem zwykłego OTA.
Spłaszczony APEX jest konfiguracją domyślną. Oznacza to, że wszystkie APEXy są domyślnie spłaszczone, chyba że wyraźnie skonfigurujesz swoje urządzenie do tworzenia niespłaszczonych APEXów w celu obsługi aktualizacji APEX (jak wyjaśniono powyżej).
Mieszanie spłaszczonych i niespłaszczonych APEXów w urządzeniu NIE jest obsługiwane. Wierzchołki w urządzeniu muszą być albo niespłaszczone, albo wszystkie spłaszczone. Jest to szczególnie ważne w przypadku wysyłania wstępnie podpisanych, gotowych wersji APEX dla projektów takich jak Mainline. APEXy, które nie są wstępnie podpisane (to znaczy zbudowane ze źródła), również powinny być niespłaszczone i podpisane odpowiednimi kluczami. Urządzenie powinno dziedziczyć z updatable_apex.mk
, jak wyjaśniono w sekcji Aktualizowanie usługi za pomocą pliku APEX .
Skompresowane APEXy
Android 12 i nowsze wersje obsługują kompresję APEX, aby zmniejszyć wpływ aktualizowalnych pakietów APEX na pamięć. Po zainstalowaniu aktualizacji APEX, mimo że jego preinstalowana wersja nie jest już używana, nadal zajmuje tę samą ilość miejsca. Zajęte miejsce pozostaje niedostępne.
Kompresja APEX minimalizuje wpływ na pamięć, używając wysoce skompresowanego zestawu plików APEX na partycjach tylko do odczytu (takich jak partycja /system
). Android 12 i nowsze używają algorytmu kompresji ZIP DEFLATE.
Kompresja nie zapewnia optymalizacji następujących elementów:
Bootstrap APEX, które należy zamontować na bardzo wczesnym etapie sekwencji rozruchowej.
Nieaktualizowalne APEXy. Kompresja jest korzystna tylko wtedy, gdy na partycji
/data
jest zainstalowana zaktualizowana wersja APEX. Pełna lista aktualizowalnych APEXów jest dostępna na stronie Modułowe komponenty systemu .Dynamiczne biblioteki współdzielone APEX. Ponieważ
apexd
zawsze aktywuje obie wersje takich APEXów (preinstalowane i uaktualnione), ich kompresja nie dodaje wartości.
Skompresowany format pliku APEX
Jest to format skompresowanego pliku APEX.
Rysunek 2. Skompresowany format pliku APEX
Na najwyższym poziomie skompresowany plik APEX to plik ZIP zawierający oryginalny plik wierzchołka w postaci deflowanej z poziomem kompresji 9 i innymi plikami przechowywanymi w stanie nieskompresowanym.
Cztery pliki składają się na plik APEX:
-
original_apex
: deflowany przy poziomie kompresji 9. To jest oryginalny, nieskompresowany plik APEX . -
apex_manifest.pb
: tylko przechowywane -
AndroidManifest.xml
: tylko przechowywane -
apex_pubkey
: tylko przechowywane
Pliki apex_manifest.pb
, AndroidManifest.xml
i apex_pubkey
są kopiami odpowiadających im plików w original_apex
.
Zbuduj skompresowany APEX
Skompresowany plik APEX można zbudować za pomocą narzędzia apex_compression_tool.py
znajdującego się w system/apex/tools
.
W systemie kompilacji dostępnych jest kilka parametrów związanych z kompresją APEX.
W Android.bp
to, czy plik APEX jest skompresowany, jest kontrolowane przez właściwość compressible
:
apex {
name: "apex.test",
manifest: "apex_manifest.json",
file_contexts: "file_contexts",
compressible: true,
}
Flaga produktu PRODUCT_COMPRESSED_APEX
określa, czy obraz systemu zbudowany ze źródła musi zawierać skompresowane pliki APEX.
W przypadku eksperymentów lokalnych możesz wymusić kompresję APEXów przez kompilację, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX=
na true
.
Skompresowane pliki APEX wygenerowane przez system kompilacji mają rozszerzenie .capex
. Rozszerzenie ułatwia rozróżnienie pomiędzy skompresowanymi i nieskompresowanymi wersjami pliku APEX.
Obsługiwane algorytmy kompresji
Android 12 obsługuje tylko kompresję deflate-zip.
Aktywuj skompresowany plik APEX podczas uruchamiania
Zanim będzie można aktywować skompresowany plik APEX, znajdujący się w nim plik original_apex
jest dekompresowany do katalogu /data/apex/decompressed
. Powstały zdekompresowany plik APEX jest na stałe powiązany z katalogiem /data/apex/active
.
Rozważmy następujący przykład jako ilustrację procesu opisanego powyżej.
Rozważ /system/apex/com.android.foo.capex
jako aktywowany skompresowany plik APEX z kodem wersji 37.
- Plik
original_apex
znajdujący się w/system/apex/com.android.foo.capex
jest dekompresowany do/data/apex/decompressed/com.android.foo@37.apex
. -
restorecon /data/apex/decompressed/com.android.foo@37.apex
jest wykonywane w celu sprawdzenia, czy ma poprawną etykietę SELinux. - Kontrole weryfikacyjne są przeprowadzane na
/data/apex/decompressed/com.android.foo@37.apex
, aby zapewnić jego ważność:apexd
sprawdza klucz publiczny zawarty w/data/apex/decompressed/com.android.foo@37.apex
, aby sprawdź, czy jest równy temu zawartemu w/system/apex/com.android.foo.capex
. - Plik
/data/apex/decompressed/com.android.foo@37.apex
jest na stałe powiązany z katalogiem/data/apex/active/com.android.foo@37.apex
. - Zwykła logika aktywacji nieskompresowanych plików APEX jest wykonywana na
/data/apex/active/com.android.foo@37.apex
.
Interakcja z OTA
Skompresowane pliki APEX mają wpływ na dostarczanie i aplikacje OTA. Ponieważ aktualizacja OTA może zawierać skompresowany plik APEX z wersją wyższą niż wersja aktywna na urządzeniu, przed ponownym uruchomieniem urządzenia należy zarezerwować pewną ilość wolnego miejsca, aby można było zastosować aktualizację OTA.
Aby obsługiwać system OTA, apexd
udostępnia te dwa interfejsy API spoiw:
-
calculateSizeForCompressedApex
- oblicza rozmiar wymagany do dekompresji plików APEX w pakiecie OTA. Można to wykorzystać do sprawdzenia, czy na urządzeniu jest wystarczająco dużo miejsca, zanim zostanie pobrana OTA. -
reserveSpaceForCompressedApex
- rezerwuje miejsce na dysku do wykorzystania w przyszłości przezapexd
do dekompresji skompresowanych plików APEX wewnątrz pakietu OTA.
W przypadku aktualizacji A/B OTA, apexd
podejmuje próbę dekompresji w tle w ramach procedury OTA po instalacji. Jeśli dekompresja się nie powiedzie, apexd
wykona dekompresję podczas rozruchu, który zastosuje aktualizację OTA.
Alternatywy brane pod uwagę przy opracowywaniu APEX
Oto kilka opcji, które firma AOSP wzięła pod uwagę podczas projektowania formatu pliku APEX oraz dlaczego zostały one uwzględnione lub wykluczone.
Regularne systemy zarządzania pakietami
Dystrybucje Linuksa mają systemy zarządzania pakietami, takie jak dpkg
i rpm
, które są wydajne, dojrzałe i niezawodne. Nie zostały one jednak zaadaptowane dla APEX, ponieważ nie chronią pakietów po instalacji. Weryfikacja jest wykonywana tylko podczas instalacji pakietów. Atakujący mogą niezauważenie złamać integralność zainstalowanych pakietów. Jest to regresja dla Androida, gdzie wszystkie komponenty systemu były przechowywane w systemach plików tylko do odczytu, których integralność jest chroniona przez dm-verity dla każdego wejścia/wyjścia. Jakakolwiek ingerencja w komponenty systemu musi być zabroniona lub musi być wykrywalna, aby urządzenie mogło odmówić uruchomienia w przypadku naruszenia bezpieczeństwa.
dm-crypt dla integralności
Pliki w kontenerze APEX pochodzą z wbudowanych partycji (na przykład partycji /system
), które są chronione przez dm-verity, gdzie jakakolwiek modyfikacja plików jest zabroniona nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom bezpieczeństwa plików, wszystkie pliki w pliku APEX są przechowywane w obrazie systemu plików sparowanym z drzewem skrótów i deskryptorem vbmeta. Bez dm-verity plik APEX w partycji /data
jest podatny na niezamierzone modyfikacje wprowadzone po jego zweryfikowaniu i zainstalowaniu.
W rzeczywistości partycja /data
jest również chroniona przez warstwy szyfrowania, takie jak dm-crypt. Chociaż zapewnia to pewien poziom ochrony przed manipulacją, jego głównym celem jest prywatność, a nie integralność. Kiedy osoba atakująca uzyska dostęp do partycji /data
, nie będzie już żadnej dalszej ochrony, a to znowu oznacza regres w porównaniu do sytuacji, w której każdy komponent systemu znajduje się na partycji /system
. Drzewo skrótów w pliku APEX wraz z dm-verity zapewnia ten sam poziom ochrony treści.
Przekieruj ścieżki z /system do /apex
Pliki komponentów systemowych spakowane w APEX są dostępne poprzez nowe ścieżki, takie jak /apex/<name>/lib/libfoo.so
. Gdy pliki znajdowały się na partycji /system
, były dostępne poprzez ścieżki takie jak /system/lib/libfoo.so
. Klient pliku APEX (inne pliki APEX lub platforma) musi korzystać z nowych ścieżek. W wyniku zmiany ścieżki może być konieczna aktualizacja istniejącego kodu.
Chociaż jednym ze sposobów uniknięcia zmiany ścieżki jest nałożenie zawartości pliku APEX na partycję /system
, zespół Androida zdecydował się nie nakładać plików na partycję /system
, ponieważ mogłoby to mieć wpływ na wydajność ze względu na liczbę nakładanych plików ( ewentualnie nawet ułożone jeden po drugim) wzrosły.
Inną opcją było przejęcie funkcji dostępu do plików, takich jak open
, stat
i readlink
, tak aby ścieżki rozpoczynające się od /system
były przekierowywane do odpowiadających im ścieżek w /apex
. Zespół Androida odrzucił tę opcję, ponieważ nie można zmienić wszystkich funkcji akceptujących ścieżki. Na przykład niektóre aplikacje statycznie łączą Bionic, który implementuje te funkcje. W takich przypadkach te aplikacje nie są przekierowywane.