Format kontenera Android Pony EXpress (APEX) został wprowadzony w systemie Android 10 i jest używany w procesie instalacji modułów systemowych niższego poziomu. Ten format ułatwia aktualizację składników systemu, które nie pasują do standardowego modelu aplikacji na Androida. 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 mieszczą się w standardowym modelu aplikacji (na przykład usługi, działania) za pośrednictwem aplikacji do instalowania pakietów (takich jak aplikacja Google Play Store), używanie podobnego modelu dla komponentów systemu operacyjnego niższego poziomu ma następujące wady:
- Modułów opartych na plikach APK nie można używać na wczesnym etapie sekwencji rozruchowej. Menedżer pakietów jest centralnym repozytorium informacji o aplikacjach i można go uruchomić tylko z poziomu menedżera aktywności, który staje się gotowy w późniejszym etapie procedury rozruchu.
- Format APK (zwłaszcza 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.
Aby uzyskać więcej informacji o tym, dlaczego wybrano ten projekt dla APEX, zobacz Alternatywy rozważane podczas opracowywania APEX .
formacie APEX
Jest to format pliku APEX.
Rysunek 1. Format pliku APEX
Na najwyższym poziomie plik APEX to plik ZIP, w którym pliki są przechowywane w stanie nieskompresowanym i znajdują 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ę pakietu i wersję, 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ązanych z APK, takich jak ADB, PackageManager i aplikacje do instalowania pakietów (takie jak Play Store). 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. Te informacje są ogólnie dostępne również w apex_manifest.json
.
apex_manifest.json
jest zalecany zamiast AndroidManifest.xml
dla nowego kodu i systemów obsługujących APEX. AndroidManifest.xml
może zawierać dodatkowe informacje o kierowaniu, których mogą używać 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 pośrednictwem 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 być montowalny w 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 zapewnia, że pobrany APEX jest podpisany z tą samą jednostką, która podpisuje ten sam APEX we wbudowanych partycjach.
Wytyczne nazewnictwa APEX
Aby zapobiec konfliktom nazw między nowymi APEXami w miarę rozwoju platformy, użyj następujących wskazówek dotyczących nazewnictwa:
-
com.android.*
- Zarezerwowane dla wierzchołkó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 unikalnych dla określonego urządzenia (lub podzbioru urządzeń).
kierownik firmy APEX
Menedżer APEX (lub apexd
) to samodzielny natywny proces odpowiedzialny za weryfikowanie, instalowanie i odinstalowywanie plików APEX. Ten proces jest uruchamiany i jest gotowy na wczesnym etapie sekwencji rozruchowej. Pliki APEX są zwykle wstępnie instalowane 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 używa klasy PackageManager i wygląda następująco.
- Plik APEX jest pobierany za pomocą aplikacji do instalowania pakietów, ADB lub innego źródła.
- Menedżer pakietów rozpocznie 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 otrzymuje rozgłoszenie po pomyślnej weryfikacji pakietu.
- Aby kontynuować instalację, należy ponownie uruchomić system.
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 sprzężenia zwrotnego.
- Montuje urządzenie bloku mapowania urządzeń na unikatowej ścieżce (na przykład
/apex/ name @ ver
).
Gdy wszystkie pliki APEX wymienione w wewnętrznej bazie danych zostaną zamontowane, menedżer APEX zapewnia usługę łączenia dla innych składników systemu w celu uzyskania informacji o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wyszukiwać listę plików APEX zainstalowanych w urządzeniu lub sprawdzać dokładną ścieżkę, w której zamontowany 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 podpisów APK) zawierającymi plik AndroidManifest.xml
. Dzięki temu pliki APEX mogą korzystać z infrastruktury plików APK, takiej jak aplikacja do instalowania 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 opcjonalnie targetSdkVersion
, minSdkVersion
i maxSdkVersion
na potrzeby szczegółowego kierowania. Te informacje umożliwiają dostarczanie plików APEX za pośrednictwem istniejących kanałów, takich jak aplikacje do instalowania 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. To, czy typ pliku może być aktualizowany, 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
(konkretnie 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 mają zostać zainstalowane. Każdy APEX musi być podpisany różnymi kluczami i jest wymuszany zarówno w czasie kompilacji, jak i w czasie wykonywania.
APEX we wbudowanych partycjach
Pliki APEX mogą znajdować się we wbudowanych partycjach, takich jak /system
. Partycja jest już ponad 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 z tą samą nazwą pakietu i kodem wersji większym lub równym. Nowy APEX jest przechowywany w /data
i podobnie jak APK, nowo zainstalowana wersja przesłania wersję już obecną na wbudowanej partycji. Ale w przeciwieństwie do plików APK, nowo zainstalowana wersja APEX jest aktywowana dopiero po ponownym uruchomieniu.
Wymagania dotyczące jądra
Aby obsługiwać główne moduły APEX na urządzeniu z systemem Android, wymagane są następujące funkcje jądra systemu Linux: sterownik sprzężenia zwrotnego i dm-verity. Sterownik sprzężenia zwrotnego 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 uzyskania 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 z jądrem w wersji 4.4 lub nowszej. Nowe urządzenia uruchamiane z systemem Android 10 lub nowszym muszą korzystać z 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ć poprawki obsługujące APEX, użyj najnowszej wersji wspólnego drzewa Androida.
Wersja jądra 4.4
Ta wersja jest obsługiwana tylko w przypadku urządzeń uaktualnionych z Androida 9 do Androida 10 i chcących obsługiwać moduły APEX. Aby uzyskać wymagane łatki, zdecydowanie zaleca się scalenie w dół z gałęzi android-4.4
. Poniżej znajduje się lista wymaganych indywidualnych poprawek dla wersji jądra 4.4.
- UPSTREAM: 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 kompatybilnym 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 )
- Przywróć „ANDROID: dm verity: dodaj minimalny rozmiar pobierania wstępnego” ( 4.4 )
- UPSTREAM: pętla: usuń pamięć podręczną, jeśli zmieniono przesunięcie lub rozmiar bloku ( 4.4 )
Wersje jądra 4.9/4.14/4.19
Aby uzyskać wymagane łatki dla wersji jądra 4.9/4.14/4.19, połącz w dół z gałęzi android-common
.
Wymagane opcje konfiguracji jądra
Na poniższej liście przedstawiono podstawowe wymagania dotyczące konfiguracji obsługi modułów APEX, które zostały wprowadzone w systemie Android 10. Elementy oznaczone gwiazdką (*) to istniejące wymagania 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ć ustawiona -
loop.max_part
musi być <= 8
Zbuduj 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 współdzielone | /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 bibliotek współdzielonych lub plików wykonywalnych. Na przykład, jeśli libFoo
zależy od libBar
, dwie biblioteki lib są uwzględnione, 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 dodatkowego interfejsu binarnego (ABI) aplikacji urządzenia. Jeśli APEX jest przeznaczony dla urządzeń z pojedynczym ABI (czyli tylko 32-bitowym lub tylko 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, 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 ABI natywnych bibliotek i plików binarnych, użyj właściwości multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
-
first
: pasuje do podstawowego ABI urządzenia. Jest to ustawienie domyślne dla plików binarnych. -
lib32
: Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwane. -
lib64
: Pasuje do 64-bitowego ABI urządzenia, które obsługuje. -
prefer32
: Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwane. Jeśli 32-bitowy ABI nie jest obsługiwany, odpowiada 64-bitowemu ABI. -
both
: Dopasowuje oba 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 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żywanego 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.
Podpisywanie APEX
Podpisuj pliki APEX w taki sam sposób, jak podpisujesz pliki APK. Podpisz dwa razy APEXy; raz dla mini systemu plików (plik apex_payload.img
) i raz dla całego pliku.
Aby podpisać dokument APEX na poziomie pliku, ustaw właściwość certificate
na jeden z trzech sposobów:
- Nie ustawiono: jeśli nie ustawiono żadnej wartości, APEX jest podpisany certyfikatem znajdującym się w
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Jeśli nie jest ustawiona żadna flaga, 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
ustawiono true
supportsRebootlessUpdate
, a aktualnie zainstalowany APEX jest nieużywany (na przykład wszystkie zawarte w nim usługi zostały zatrzymane), to nowy APEX można zainstalować bez ponownego uruchamiania z flagą --force-non-staged
.
adb install --force-non-staged apex_file_name
Użyj APEXa
Po ponownym uruchomieniu APEX jest montowany w katalogu /apex/<apex_name>@<version>
. W tym samym czasie można zamontować wiele wersji tego samego APEX. Wśród ścieżek montowania ta, która odpowiada najnowszej wersji, jest montowana z powiązaniem w /apex/<apex_name>
.
Klienci mogą używać ścieżki podłączonej do wiązania do odczytywania lub wykonywania plików z APEX.
APEX są zwykle używane w następujący sposób:
- Producent OEM lub ODM wstępnie ładuje APEX w katalogu
/system/apex
gdy urządzenie jest dostarczane. - Pliki w APEX są dostępne za pośrednictwem ścieżki
/apex/<apex_name>/
. - Gdy zaktualizowana wersja APEX jest zainstalowana w
/data/apex
, ścieżka wskazuje 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 aktualizowalną. 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 ponownie zdefiniować 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 APEXach.
Jeśli usługa oznaczona jako aktualizowalna zostanie uruchomiona przed aktywacją APEXów, jej uruchomienie zostanie opóźnione do czasu zakończenia aktywacji APEXów.
Skonfiguruj system do obsługi aktualizacji APEX
Ustaw następującą właściwość systemową na wartość 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 w celu pełnej obsługi APEX jest czasem 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 w APEX.
Spłaszczony 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 pod wbudowaną partycją. 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 wierzchołków APEX nie można zaktualizować, pobierając zaktualizowane wersje wierzchołków APEX z sieci, ponieważ pobranych wierzchołków APEX nie można spłaszczyć. Spłaszczone wierzchołki wierzchołków można aktualizować tylko za pośrednictwem zwykłego OTA.
Spłaszczony APEX jest konfiguracją domyślną. Oznacza to, że wszystkie wierzchołki APEX są domyślnie spłaszczone, chyba że wyraźnie skonfigurujesz urządzenie do tworzenia niespłaszczonych wierzchołków APEX w celu obsługi aktualizacji APEX (jak wyjaśniono powyżej).
Mieszanie spłaszczonych i niespłaszczonych wierzchołków w urządzeniu NIE jest obsługiwane. APEXy w urządzeniu muszą być albo wszystkie niespłaszczone, albo wszystkie spłaszczone. Jest to szczególnie ważne w przypadku wysyłania wstępnie podpisanych gotowych zestawów APEX do 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 temacie Aktualizowanie usługi za pomocą APEX .
Skompresowane wierzchołki
Android 12 i nowsze obsługują kompresję APEX w celu zmniejszenia wpływu aktualizowalnych pakietów APEX na pamięć masową. Po zainstalowaniu aktualizacji APEX, mimo że preinstalowana wersja nie jest już używana, nadal zajmuje taką samą ilość miejsca. To zajęte miejsce pozostaje niedostępne.
Kompresja APEX minimalizuje ten wpływ na pamięć masową, 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 muszą być zamontowane na bardzo wczesnym etapie sekwencji startowej.
APEXy, których nie można aktualizować. 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 APEXes. Ponieważ
apexd
zawsze aktywuje obie wersje takich APEX-ów (wstępnie zainstalowane i zaktualizowane), 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 APEX w postaci opróżnionej z kompresją na poziomie 9 i innymi plikami przechowywanymi w postaci nieskompresowanej.
Cztery pliki składają się na plik APEX:
-
original_apex
: opróżniony z poziomem kompresji 9 To jest oryginalny, nieskompresowany plik APEX . -
apex_manifest.pb
: tylko przechowywane -
AndroidManifest.xml
: tylko przechowywany -
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 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
kontroluje, czy obraz systemu zbudowany ze źródła musi zawierać skompresowane pliki APEX.
W przypadku lokalnych eksperymentów można zmusić kompilację do kompresji APEX, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX=
na true
.
Skompresowane pliki APEX generowane przez system kompilacji mają rozszerzenie .capex
. Rozszerzenie ułatwia rozróżnienie skompresowanych i nieskompresowanych wersji pliku APEX.
Obsługiwane algorytmy kompresji
Android 12 obsługuje tylko kompresję typu deflate-zip.
Aktywuj skompresowany plik APEX podczas uruchamiania
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 powiązany na stałe z katalogiem /data/apex/active
.
Rozważ poniższy przykład jako ilustrację procesu opisanego powyżej.
Rozważ /system/apex/com.android.foo.capex
jako aktywowany skompresowany APEX z kodem wersji 37.
-
original_apex
plik_apex wewnątrz/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ą wykonywane na
/data/apex/decompressed/com.android.foo@37.apex
, aby zapewnić jego poprawność:apexd
sprawdza klucz publiczny zawarty w/data/apex/decompressed/com.android.foo@37.apex
do sprawdź, czy jest równy temu, który znajduje się w pakiecie/system/apex/com.android.foo.capex
. - Plik
/data/apex/decompressed/com.android.foo@37.apex
jest powiązany na stałe z katalogiem/data/apex/active/com.android.foo@37.apex
. - Regularna 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ż wersja aktywna na urządzeniu, przed ponownym uruchomieniem urządzenia w celu zastosowania aktualizacji OTA należy zarezerwować pewną ilość wolnego miejsca.
Aby wesprzeć system OTA, apexd
udostępnia te dwa interfejsy API wiązania:
-
calculateSizeForCompressedApex
- oblicza rozmiar wymagany do rozpakowania 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
podejmuje próbę dekompresji w tle jako część procedury OTA po instalacji. Jeśli dekompresja się nie powiedzie, apexd
przeprowadza dekompresję podczas rozruchu, który stosuje aktualizację OTA.
Alternatywy rozważane przy opracowywaniu APEX
Oto kilka opcji, które AOSP rozważało podczas projektowania formatu pliku APEX i dlaczego zostały uwzględnione lub wykluczone.
Zwykłe systemy zarządzania pakietami
Dystrybucje Linuksa mają systemy zarządzania pakietami, takie jak dpkg
i rpm
, które są wydajne, dojrzałe i solidne. Jednak nie zostały one przyjęte dla APEX, ponieważ nie mogą chronić pakietów po instalacji. Weryfikacja jest przeprowadzana tylko podczas instalowania pakietów. Atakujący mogą niezauważ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 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 wszelkie modyfikacje plików są zabronione nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom bezpieczeństwa plikom, wszystkie pliki w APEX są przechowywane w obrazie systemu plików, który jest sparowany z drzewem skrótów i deskryptorem vbmeta. Bez dm-verity APEX w partycji /data
jest narażony na niezamierzone modyfikacje, które są wprowadzane po 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 będzie już żadnej dodatkowej ochrony, a to znowu jest regresem w porównaniu do każdego komponentu systemu znajdującego 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 systemu 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ć konieczne zaktualizowanie 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ół systemu Android postanowił nie nakładać plików na partycję /system
, ponieważ mogłoby to wpłynąć na wydajność, ponieważ liczba nakładanych plików ( być może nawet układane 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 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.