Format kontenera Android Pony EXpress (APEX) został wprowadzony w systemie Android 10 i jest używany w przepływie instalacji dla modułów systemowych niższego poziomu. Ten format ułatwia aktualizowanie składników systemu, które nie pasują do standardowego modelu aplikacji systemu 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ż Android obsługuje aktualizacje modułów, które pasują 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 pakiecie 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ć tylko z 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ą odpowiednie.
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.
Aby uzyskać więcej informacji na temat tego, dlaczego wybrano ten projekt dla APEX, zobacz Alternatywy brane pod uwagę podczas opracowywania APEX .
Format APEX
To jest format pliku APEX.
Rysunek 1. Format pliku APEX
Na najwyższym poziomie plik APEX to plik zip, w którym pliki są przechowywane w postaci nieskompresowanej i mieszczą się 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.
Plik AndroidManifest.xml
umożliwia plikowi APEX korzystanie z narzędzi i infrastruktury związanej z pakietem APK, takich jak ADB, PackageManager i aplikacje do instalacji pakietów (takie jak Sklep Play). Na przykład plik APEX może używać istniejącego narzędzia, takiego jak aapt
, do sprawdzania podstawowych metadanych z pliku. Plik zawiera informacje o nazwie i wersji pakietu. Ta informacja jest ogólnie dostępna 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 dotyczące kierowania, które mogą być używane przez 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 pętli zwrotnej. 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 są zawarte w pliku apex_payload.img
.
apex_pubkey
to klucz publiczny używany do podpisywania obrazu systemu plików. W czasie wykonywania ten klucz zapewnia, że pobrany APEX jest podpisany za pomocą tej samej jednostki, która podpisuje ten sam APEX na wbudowanych partycjach.
Wytyczne nazewnictwa APEX
Aby zapobiec konfliktom nazewnictwa między nowymi APEX w miarę rozwoju platformy, skorzystaj z następujących wskazówek dotyczących nazewnictwa:
-
com.android.*
- Zarezerwowane dla AOSP APEX. Nie unikalny dla żadnej firmy lub 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. Ten proces jest uruchamiany i jest gotowy na wczesnym etapie 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 korzysta z klasy PackageManager i wygląda następująco.
- Plik APEX jest pobierany za pośrednictwem aplikacji do instalacji pakietów, ADB lub innego źródła.
- Menedżer pakietów rozpoczyna procedurę instalacji. Po rozpoznaniu, że plik jest APEX, menedżer pakietów przekazuje kontrolę do menedżera APEX.
- Menedżer APEX weryfikuje plik APEX.
- Jeśli plik APEX zostanie zweryfikowany, wewnętrzna baza danych menedżera APEX zostanie zaktualizowana, aby odzwierciedlić, że plik APEX zostanie aktywowany przy następnym uruchomieniu.
- Żądający instalacji odbiera transmisję po pomyślnej weryfikacji pakietu.
- Aby kontynuować instalację, system musi zostać ponownie uruchomiony.
Przy następnym uruchomieniu menedżer APEX uruchamia się, 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 pętli zwrotnej.
- Montuje urządzenie blokowe programu mapującego urządzenia w unikatowej ścieżce (na przykład
/apex/ name @ ver
).
Gdy wszystkie pliki APEX wymienione w wewnętrznej bazie danych są zamontowane, menedżer APEX udostępnia usługę spinacza dla innych komponentów systemu, aby przeszukiwać informacje o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wysyłać zapytania do listy plików APEX zainstalowanych w urządzeniu lub sprawdzać dokładną ścieżkę, w której zainstalowany jest określony 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 podpisu APK) zawierającymi plik AndroidManifest.xml
. Dzięki temu pliki APEX mogą korzystać z infrastruktury dla plików APK, takich jak aplikacja do instalacji pakietów, 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
dla precyzyjnego kierowania. Informacje te umożliwiają dostarczanie plików APEX za pośrednictwem istniejących kanałów, takich jak aplikacje do instalacji pakietów i ADB.
Obsługiwane typy plików
Format APEX obsługuje następujące typy plików:
- Natywne biblioteki współdzielone
- Natywne pliki wykonywalne
- Pliki JAR
- Pliki danych
- Pliki konfiguracyjne
Nie oznacza to, że APEX może aktualizować wszystkie te typy plików. Możliwość aktualizacji typu pliku zależy od platformy i stabilności definicji interfejsów dla typów plików.
Podpisywanie
Pliki APEX są podpisywane na dwa sposoby. Najpierw apex_payload.img
(w szczególności deskryptor vbmeta dołączony do apex_payload.img
) jest podpisany kluczem. Następnie cały APEX jest podpisany 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żywanemu do podpisywania deskryptora vbmeta. Menedżer APEX używa klucza publicznego do weryfikacji APEX, które są wymagane do zainstalowania. Każdy APEX musi być podpisany różnymi kluczami i jest wymuszany zarówno w czasie kompilacji, jak i w czasie wykonywania.
APEX w przegrodach do zabudowy
Pliki APEX mogą znajdować się na wbudowanych partycjach, takich jak /system
. Partycja jest już nad dm-verity, więc pliki APEX są montowane bezpośrednio nad urządzeniem pętli zwrotnej.
Jeśli APEX jest obecny we wbudowanej partycji, APEX można zaktualizować, dostarczając pakiet APEX o tej samej nazwie i kodzie wersji większym lub równym. Nowy APEX jest przechowywany w /data
i podobnie jak w przypadku pakietów APK, nowo zainstalowana wersja przesłania wersję już obecną na wbudowanej partycji. Ale w przeciwieństwie do pakietów APK, nowo zainstalowana wersja APEX jest aktywowana dopiero po ponownym uruchomieniu.
Wymagania jądra
Aby obsługiwać moduły APEX mainline na urządzeniu z systemem Android, wymagane są następujące funkcje jądra Linux: 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
Główne moduły 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 są zawarte we wspólnym drzewie Androida. Aby uzyskać łatki 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ę downmerge z gałęzi android-4.4
. Poniżej znajduje się lista wymaganych pojedynczych łat dla wersji jądra 4.4.
- WYPRZEDAŻ: pętla: dodaj ioctl do zmiany rozmiaru bloku logicznego ( 4.4 )
- BACKPORT: blok/pętla: ustaw hw_sectors ( 4.4 )
- UPSTREAM: pętla: Dodaj LOOP_SET_BLOCK_SIZE w compat 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: Propaguj ponowne montowanie poprawnie ( 4.4 )
- Cofnij „ANDROID: dm verity: dodaj minimalny rozmiar pobierania wstępnego” ( 4.4 )
- WYDOBYCIE: 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 łaty dla wersji jądra 4.9/4.14/4.19, połącz 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ą (*) są istniejącymi wymaganiami z systemu Android 9 i niższych.
(*) 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 polecenia jądra
Aby obsługiwać APEX, upewnij się, że parametry wiersza poleceń jądra spełniają następujące wymagania:
-
loop.max_loop
NIE może być ustawiony -
loop.max_part
musi być <= 8
Budowanie APEX
W tej sekcji opisano, jak zbudować APEX przy użyciu systemu kompilacji Android. 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 APEX |
---|---|
Biblioteki udostępnione | /lib i /lib64 ( /lib/arm dla przetłumaczonego ramienia w x86) |
Pliki wykonywalne | /bin |
Biblioteki Java | /javalib |
Prefabrykowane | /etc |
Zależności przechodnie
Pliki APEX automatycznie zawierają przechodnie zależności natywnych bibliotek współdzielonych lub plików wykonywalnych. Na przykład, jeśli libFoo
zależy od libBar
, dwie biblioteki są uwzględniane, gdy tylko libFoo
jest wymienione we właściwości native_shared_libs
.
Obsługa wielu ABI
Zainstaluj właściwość native_shared_libs
zarówno dla podstawowego, jak i pomocniczego interfejsu binarnego aplikacji (ABI) urządzenia. Jeśli APEX jest przeznaczony dla urządzeń z pojedynczym ABI (czyli tylko 32-bitowym lub 64-bitowym), instalowane są tylko biblioteki z odpowiednim ABI.
Zainstaluj właściwość binaries
tylko dla podstawowego ABI urządzenia, jak opisano poniżej:
- Jeśli urządzenie jest tylko 32-bitowe, instalowany jest tylko 32-bitowy wariant pliku binarnego.
- Jeśli urządzenie jest tylko 64-bitowe, instalowany jest tylko 64-bitowy wariant pliku binarnego.
Aby dodać szczegółową kontrolę nad ABI natywnych bibliotek i plików binarnych, użyj właściwości multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
-
first
: Dopasowuje podstawowy ABI urządzenia. Jest to ustawienie domyślne dla plików binarnych. -
lib32
: Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwany. -
lib64
: Odpowiada 64-bitowemu ABI urządzenia, które jest obsługiwane. -
prefer32
: Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwany. 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 obsługującego 32/64 i nie preferującego 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 za pomocą różnych kluczy. 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 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 za pomocą klucza publicznego o tym samym identyfikatorze w urządzeniu.
Podpisywanie ZIP
Podpisuj pliki APEX w taki sam sposób, jak podpisujesz pakiety APK. Dwukrotnie podpisuj APEX; raz dla systemu plików mini (plik apex_payload.img
) i raz dla całego pliku.
Aby podpisać APEX na poziomie pliku, ustaw właściwość certificate
na jeden z trzech sposobów:
- Nieustawione: jeśli nie ustawiono żadnej wartości, APEX jest podpisany certyfikatem znajdującym się pod adresem
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Jeśli żadna flaga nie jest ustawiona, ścieżka domyślnie 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)
}
Instalowanie APEX
Aby zainstalować APEX, użyj ADB.
adb install apex_file_name
adb reboot
Korzystanie z APEX
Po ponownym uruchomieniu APEX jest montowany w katalogu /apex/<apex_name>@<version>
. Wiele wersji tego samego APEX-a może być montowanych w tym samym czasie. Wśród ścieżek montowania ta, która odpowiada najnowszej wersji, jest montowana przez wiązanie w /apex/<apex_name>
.
Klienci mogą używać ścieżki montowanej przez powiązanie do odczytywania lub wykonywania plików z APEX.
APEX są zwykle używane w następujący sposób:
- OEM lub ODM wstępnie ładuje APEX w
/system/apex
, gdy urządzenie jest dostarczane. - Pliki w APEX są dostępne poprzez ścieżkę
/apex/<apex_name>/
. - Gdy zaktualizowana wersja APEX jest zainstalowana w
/data/apex
, ścieżka wskazuje nowy APEX po ponownym uruchomieniu.
Aktualizacja usługi za pomocą APEX
Aby zaktualizować usługę za pomocą APEX:
Oznacz usługę na partycji systemowej jako zdatną do aktualizacji. Dodaj opcję z możliwością
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
APEX. Wyzwalacze akcji nie są obsługiwane w APEX.
Jeżeli usługa oznaczona jako aktualizowalna rozpoczyna się przed aktywacją APEX-ów, start jest opóźniany do momentu zakończenia aktywacji APEX-ów.
Konfiguracja systemu 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 APEX
W przypadku starszych urządzeń aktualizacja starego jądra do pełnej obsługi 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 montowania 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 APEX są instalowane bezpośrednio w katalogu na wbudowanej partycji. Na przykład lib/libFoo.so
w spłaszczonym APEX my.apex
jest instalowany w /system/apex/my.apex/lib/libFoo.so
.
Aktywacja spłaszczonego APEX nie obejmuje urządzenia pętli. Cały katalog /system/apex/my.apex
jest bezpośrednio podłączony do /apex/name@ver
.
Spłaszczonych APEX-ów nie można zaktualizować, pobierając zaktualizowane wersje APEX-ów z sieci, ponieważ pobranych APEX-ów nie można spłaszczyć. Spłaszczone APEX mogą być aktualizowane tylko przez zwykłą OTA.
Spłaszczony APEX jest konfiguracją domyślną. Oznacza to, że wszystkie APEX są domyślnie spłaszczone, chyba że jawnie skonfigurujesz urządzenie do budowania niespłaszczonych APEX-ów do obsługi aktualizacji APEX (jak wyjaśniono powyżej).
Mieszanie spłaszczonych i niespłaszczonych APEX w urządzeniu NIE jest obsługiwane. Punkty APEX 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 prekompilacji APEX dla projektów takich jak Mainline. APEX, które nie są wstępnie podpisane (czyli 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 Aktualizowanie usługi za pomocą APEX .
Skompresowane APEX
Android 12 i nowsze obsługują kompresję APEX w celu zmniejszenia wpływu pamięci masowej aktualizowanych pakietów APEX. Po zainstalowaniu aktualizacji APEX, mimo że jej preinstalowana wersja nie jest już używana, nadal zajmuje tyle samo miejsca. Ta zajęta przestrzeń pozostaje niedostępna.
Kompresja APEX minimalizuje ten wpływ na pamięć, używając mocno 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:
Bootstrap APEX, które należy zamontować na bardzo wczesnym etapie sekwencji rozruchu.
Nieaktualizowalne APEX. Kompresja jest korzystna tylko wtedy, gdy na partycji
/data
jest zainstalowana zaktualizowana wersja APEX. Pełna lista APEX-ów, które można aktualizować, jest dostępna na stronie Komponenty systemu modułowego .Dynamiczne biblioteki współdzielone APEX. Ponieważ
apexd
zawsze aktywuje obie wersje takich APEX-ów (preinstalowanych i zaktualizowanych), ich kompresja nie dodaje wartości.
Skompresowany format pliku APEX
To jest format skompresowanego pliku APEX.
Rysunek 2. Skompresowany format pliku APEX
Na najwyższym poziomie skompresowany plik APEX jest plikiem zip zawierającym oryginalny plik apex w nieskompresowanej formie z poziomem kompresji 9 oraz innymi plikami przechowywanymi w postaci nieskompresowanej.
Na plik APEX składają się cztery pliki:
-
original_apex
: deflated z poziomem kompresji 9 To jest oryginalny, nieskompresowany plik APEX . -
apex_manifest.pb
: tylko przechowywane -
AndroidManifest.xml
: tylko przechowywane -
apex_pubkey
: tylko przechowywane
apex_manifest.pb
, AndroidManifest.xml
i apex_pubkey
są kopiami odpowiadających im plików w pliku original_apex
.
Budowanie skompresowanego APEX
Skompresowany 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 kompresowalny, 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
, czy obraz systemu utworzony ze źródła musi zawierać skompresowane pliki APEX.
Aby przeprowadzić lokalne eksperymenty, możesz wymusić w kompilacji kompresję APEX, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX=
na true
.
Skompresowane pliki APEX generowane przez system budowania mają rozszerzenie .capex
. Rozszerzenie ułatwia rozróżnienie między skompresowanymi i nieskompresowanymi wersjami pliku APEX.
Obsługiwane algorytmy kompresji
Android 12 obsługuje tylko kompresję deflate-zip.
Aktywacja skompresowanego pliku APEX podczas rozruchu
Zanim skompresowany APEX będzie mógł zostać aktywowany, znajdujący się w nim plik original_apex
jest dekompresowany do katalogu /data/apex/decompressed
. Wynikowy 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 APEX z kodem wersji 37.
- Plik
original_apex
wewnątrz/system/apex/com.android.foo.capex
jest zdekompresowany do/data/apex/decompressed/com.android.foo@37.apex
. -
restorecon /data/apex/decompressed/com.android.foo@37.apex
jest wykonywany 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
pod kątem 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 dla 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 stosowanie OTA. Ponieważ aktualizacja OTA może zawierać skompresowany plik APEX z wersją wyższą niż ta, która jest aktywna na urządzeniu, pewna ilość wolnego miejsca musi zostać zarezerwowana przed ponownym uruchomieniem urządzenia w celu zastosowania aktualizacji OTA.
Aby obsługiwać system OTA, apexd
udostępnia te dwa interfejsy API spinaczy:
-
calculateSizeForCompressedApex
— oblicza rozmiar wymagany do dekompresji plików APEX w pakiecie OTA. Można to wykorzystać do sprawdzenia, czy urządzenie ma wystarczającą ilość miejsca przed pobraniem 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
próbuje dokonać dekompresji w tle w ramach procedury poinstalacyjnej OTA. Jeśli dekompresja się nie powiedzie, apexd
wykonuje dekompresję podczas rozruchu, który stosuje aktualizację OTA.
Alternatywy brane pod uwagę przy opracowywaniu APEX
Oto kilka opcji, które firma AOSP rozważyła podczas projektowania formatu pliku APEX, i 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ą potężne, dojrzałe i solidne. Jednak nie zostały one zaadoptowane do APEX, ponieważ nie mogą chronić pakietów po instalacji. Weryfikacja jest wykonywana tylko wtedy, gdy pakiety są instalowane. Atakujący mogą niepostrzeżenie złamać integralność zainstalowanych pakietów. Jest to regresja dla Androida, w której wszystkie komponenty systemu były przechowywane w systemach plików tylko do odczytu, których integralność jest chroniona przez dm-verity dla każdego I/O. Wszelkie ingerencje w składniki systemu muszą być zabronione lub być wykrywalne, 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 wszelkie modyfikacje plików są zabronione nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom bezpieczeństwa plików, wszystkie pliki w APEX są przechowywane w obrazie systemu plików, który jest sparowany z drzewem mieszającym i deskryptorem vbmeta. Bez dm-verity APEX w partycji /data
jest podatny na niezamierzone modyfikacje, które są wprowadzane 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ść. Gdy atakujący uzyska dostęp do partycji /data
, nie może być dalszej ochrony, a to znowu jest regresją w porównaniu z każdym komponentem systemu znajdującym się na partycji /system
. Drzewo skrótów w pliku APEX wraz z dm-verity zapewnia ten sam poziom ochrony treści.
Przekierowywanie ścieżek z /system do /apex
Pliki komponentów systemowych spakowane w APEX są dostępne za pośrednictwem nowych ścieżek, takich jak /apex/<name>/lib/libfoo.so
. Gdy pliki były częścią partycji /system
, były dostępne za pośrednictwem ścieżek takich 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 postanowił nie nakładać plików na partycję /system
ponieważ może to wpłynąć na wydajność, ponieważ liczba nakładanych plików ( być może nawet ułożone jeden po drugim) wzrosła.
Inną opcją było przejęcie funkcji dostępu do plików, takich jak open
, stat
i readlink
, tak aby ścieżki zaczynające się od /system
były przekierowywane do odpowiadających im ścieżek w /apex
. Zespół Androida odrzucił tę opcję, ponieważ zmiana wszystkich funkcji akceptujących ścieżki jest niewykonalna. Na przykład niektóre aplikacje statycznie łączą Bionic, który implementuje funkcje. W takich przypadkach te aplikacje nie są przekierowywane.