Format kontenera Android Pony EXpress (APEX) został wprowadzony w Androidzie 10 i jest używany w procesie instalacji w przypadku modułów systemowych niskiego poziomu. Ten format ułatwia aktualizowanie komponentów systemu, które nie pasują do standardowego modelu aplikacji na Androida. Przykładowe komponenty to natywne usługi i biblioteki, warstwy abstrakcji sprzętowej (HAL), środowisko wykonawcze (ART) oraz biblioteki klas.
Termin „APEX” może także odnosić się do pliku APEX.
Tło
Android obsługuje aktualizacje modułów, które pasują do standardowego modelu aplikacji (np. usług czy aktywności) za pomocą instalatorów aplikacji (np. Sklep Google Play), jednak korzystanie z podobnego modelu do komponentów systemu operacyjnego niższego poziomu ma te wady:
- Modułów opartych na pliku APK nie można używać na wczesnym etapie sekwencji rozruchu. Menedżer pakietów to centralne repozytorium informacji o aplikacjach i można je uruchamiać wyłącznie z poziomu menedżera aktywności, który staje się gotowy na późniejszym etapie procedury uruchamiania.
- Format APK (zwłaszcza plik manifestu) jest przeznaczony dla aplikacji na Androida, a moduły systemowe nie zawsze są odpowiednie.
Projektowanie
W tej sekcji opisano ogólną strukturę formatu pliku APEX oraz menedżera APEX, czyli usługę zarządzającą plikami APEX.
Więcej informacji o tym, dlaczego wybrany został ten model APEX, znajdziesz w artykule o alternatywach branych pod uwagę przy tworzeniu tej strategii.
Format 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ą nieskompresowane i znajdują się na 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 ApexManifest
bufor protokołu w formacie JSON.
Plik AndroidManifest.xml
umożliwia plikowi APEX korzystanie z narzędzi i infrastruktury związanych z plikami APK, takich jak ADB, PackageManager i aplikacje do instalowania pakietów (np. Sklep Play). Plik APEX może na przykład używać dotychczasowego narzędzia, takiego 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 usłudze apex_manifest.json
.
apex_manifest.json
jest zalecany dla AndroidManifest.xml
w przypadku 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 z weryfikacją dm-verity. Obraz jest montowany w czasie działania za pomocą urządzenia pętli. Konkretnie drzewo haszy i blok metadanych są tworzone za pomocą biblioteki libavb
. Ładunek systemu plików nie jest analizowany (ponieważ obraz powinien być możliwy do podłączenia). 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 działania ten klucz gwarantuje, że pobrany punkt APEX jest podpisany tym samym elementem, który podpisuje ten sam punkt APEX we wbudowanych partycjach.
Wytyczne dotyczące nazewnictwa w APEX
Aby uniknąć konfliktów w nazwach nowych punktów APEX w miarę rozwoju platformy, stosuj się do tych wytycznych:
com.android.*
- Zarezerwowane dla AOSP APEX. Nie jest unikalne dla żadnej firmy ani na żadnym urządzeniu.
com.<companyname>.*
- Zarezerwowane dla firmy. Mogą być używane przez wiele urządzeń tej firmy.
com.<companyname>.<devicename>.*
- Zarezerwowana dla punktów APEX, które są unikalne dla określonego urządzenia (lub podzbioru urządzeń).
Menedżer APEX
Menedżer APEX (lub apexd
) to samodzielny natywny proces odpowiedzialny za weryfikowanie, instalowanie i odinstalowywanie plików APEX. Ten proces jest uruchamiany i gotowy na początku sekwencji uruchamiania. Pliki APEX są zwykle wstępnie instalowane
na urządzeniu poniżej /system/apex
. Jeśli nie ma żadnych aktualizacji, menedżer APEX domyślnie używa tych pakietów.
Sekwencja aktualizacji APEX korzysta z klasy PackageManager i wygląda tak:
- 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 to APEX, menedżer pakietów przekazuje kontrolę do menedżera APEX.
- Menedżer APEX weryfikuje plik APEX.
- Po zweryfikowaniu pliku APEX wewnętrzna baza danych menedżera APEX jest aktualizowana tak, aby odzwierciedlała, że plik APEX jest aktywowany przy następnym uruchomieniu.
- Po pomyślnej weryfikacji pakietu żądający instalacji otrzymuje komunikat.
- Aby kontynuować instalację, należy ponownie uruchomić system.
Podczas następnego uruchomienia menedżer APEX wczyta wewnętrzną bazę danych i wykonuje te czynności w przypadku każdego wymienionego pliku APEX:
- Weryfikuje plik APEX.
- Tworzy urządzenie pętli z pliku APEX.
- Tworzy urządzenie blokujące mapowanie urządzenia na urządzeniu pętli zwrotnej.
- Dodaje urządzenie do mapowania urządzeń na unikalnej ścieżce (na przykład
/apex/name@ver
).
Po podłączeniu wszystkich plików APEX wymienionych w wewnętrznej bazie danych menedżer APEX udostępnia usługę powiązania, która umożliwia innym komponentom systemowym wysyłanie zapytań o informacje o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wysyłać zapytania o listę plików APEX zainstalowanych na urządzeniu lub o ścisłą ścieżkę, w której podłączony jest określony punkt APEX, aby można było uzyskać dostęp do plików.
Pliki APEX to pliki APK
Pliki APEX to prawidłowe pliki APK, ponieważ są to podpisane archiwa ZIP (według schematu podpisu APK) zawierające 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 krótki. Składa się z pakietu name
i versionCode
oraz (opcjonalnie) targetSdkVersion
, minSdkVersion
i maxSdkVersion
do kierowania szczegółowego. Te informacje umożliwiają dostarczanie plików APEX za pomocą istniejących kanałów, takich jak aplikacje do instalowania pakietów i ADB.
Obsługiwane typy plików
Format APEX obsługuje te typy plików:
- Natywne udostępniane biblioteki
- 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 zostać zaktualizowany, zależy od platformy i stabilności definicji interfejsów typów plików.
Opcje podpisywania
Pliki APEX są podpisywane na 2 sposoby. Najpierw plik apex_payload.img
(czyli deskryptor vbmeta dołączony do apex_payload.img
) jest podpisany kluczem.
Następnie cały APEX jest podpisywany za pomocą schematu podpisu pliku APK w wersji 3. W tym procesie używane są 2 różne klucze.
Po stronie urządzenia zainstalowany jest klucz publiczny odpowiadający kluczowi prywatnemu służącemu do podpisywania deskryptora vbmeta. Menedżer APEX używa klucza publicznego do weryfikowania APEX, które mają zostać zainstalowane. Każdy plik APEX musi być podpisany za pomocą różnych kluczy i musi być egzekwowany zarówno w czasie kompilacji, jak i w czasie wykonywania.
APEX we wbudowanych partycjach
Pliki APEX mogą znajdować się we wbudowanych partycjach, np. /system
. Partycja ma już ponad dm-verity, więc pliki APEX są podłączane bezpośrednio w urządzeniu pętli.
Jeśli we wbudowanej partycji znajduje się APEX, można go zaktualizować, przesyłając pakiet APEX z tą samą nazwą pakietu i kodem wersji większą lub równą. Nowy APEX jest przechowywany w pliku /data
i podobnie jak w przypadku plików APK nowo zainstalowana wersja zastępuje wersję obecną w wbudowanej partycji. 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ć moduły główne APEX na urządzeniu z Androidem, wymagane są te funkcje jądra Linuxa: sterownik pętli zwrotnej i dm-verity. Sterownik pętli z powrotem montuje obraz systemu plików w module APEX, a dm-verity weryfikuje ten moduł.
Wydajność sterownika pętli i dm-verity ma duże znaczenie dla zapewnienia dobrej wydajności systemu przy korzystaniu z modułów APEX.
Obsługiwane wersje jądra
Moduł główny APEX jest obsługiwany na urządzeniach z jądrem w wersji 4.4 lub nowszej. Nowe urządzenia z Androidem 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 obsługujące moduły APEX są zawarte w drzewie wspólnym Androida. Aby pobrać poprawki obsługujące APEX, użyj najnowszej wersji narzędzia Android Common Tree.
Wersja jądra 4.4
Ta wersja jest obsługiwana tylko na urządzeniach, które zostały zaktualizowane z Androida 9 do Androida 10 i które mają obsługiwać moduły APEX. Aby uzyskać wymagane poprawki, zdecydowanie zalecamy użycie opcji scalania w dół z gałęzi android-4.4
. Poniżej znajdziesz listę wymaganych indywidualnych poprawek dla wersji jądra 4.4.
- UPSTREAM: pętla: dodaj ioctl, by zmienić rozmiar bloku logicznego (4.4)
- BACKPORT: block/loop: set hw_sectors (4.4)
- UPSTREAM: pętla: dodaj LOOP_SET_BLOCK_SIZE w zgodnym ioctl (4.4)
- ANDROID: mnt: Napraw next_descendent (4.4)
- ANDROID: mnt: remount powinna rozpowszechnić się wśród niewolników (4.4)
- ANDROID: mnt: Poprawnie przeprowadź ponowną instalację (4.4)
- Przywróć „ANDROID: dm verity: add minimum prefetch size” (4.4)
- UPSTREAM: pętla: usuń pamięć podręczną, jeśli offset lub block_size uległy zmianie (4.4)
Wersje jądra 4.9/4.14/4.19
Aby uzyskać wymagane łaty na wersje jądra 4.9/4.14/4.19, złącz gałęzi android-common
w dół.
Wymagane opcje konfiguracji jądra
Na liście poniżej znajdziesz podstawowe wymagania konfiguracyjne dotyczące obsługi modułów APEX wprowadzonych w Androidzie 10. Elementy oznaczone gwiazdką (*) to wymagania obowiązujące w Androidzie 9 i starszych wersjach.
(*) 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ą te wymagania:
loop.max_loop
nie może być ustawionyloop.max_part
musi być mniejsza niż 8
Tworzenie APEX
Z tej sekcji dowiesz się, jak utworzyć APEX za pomocą systemu kompilacji Androida.
Poniżej znajdziesz przykład użycia funkcji Android.bp
w przypadku obszaru 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 strony apex_manifest.json
:
{
"name": "com.android.example.apex",
"version": 1
}
file_contexts
przykład:
(/.*)? u:object_r:system_file:s0
/sub(/.*)? u:object_r:sub_file:s0
/sub/file3 u:object_r:file3_file:s0
Typy i lokalizacje plików w APEX
Typ pliku | Lokalizacja w APEX |
---|---|
Biblioteki udostępnione | /lib i /lib64 (/lib/arm w przypadku przetłumaczonego kodu na ARM w systemie x86) |
Pliki wykonywalne | /bin |
biblioteki Java; | /javalib |
Gotowe rozwiązania | /etc |
Zależności pośrednie
Pliki APEX automatycznie uwzględniają przechodnie zależności natywnych udostępnionych bibliotek lub plików wykonywalnych. Jeśli na przykład libFoo
zależy od libBar
, obie biblioteki są uwzględniane, gdy w właściwości native_shared_libs
wymieniona jest tylko libFoo
.
Obsługa kilku interfejsów ABI
Zainstaluj właściwość native_shared_libs
zarówno dla głównego, jak i dodatkowego interfejsu binarnego aplikacji (ABI) na urządzeniu. Jeśli punkt APEX jest kierowany na urządzenia z 1 interfejsem ABI (czyli tylko 32-bitowym lub tylko 64-bitowym), instalowane są tylko biblioteki z odpowiednim interfejsem ABI.
Zainstaluj właściwość binaries
tylko dla głównego interfejsu ABI urządzenia w sposób opisany poniżej:
- Jeśli urządzenie jest tylko 32-bitowe, zostanie zainstalowana tylko 32-bitowa wersja pliku binarnego.
- Jeśli urządzenie ma tylko wersję 64-bitową, zainstalowana jest tylko wersja 64-bitowa pliku binarnego.
Aby zyskać szczegółową kontrolę nad interfejsami ABI bibliotek natywnych i plikami binarnymi, użyj właściwości multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]
.
first
: pasuje do głównego interfejsu ABI urządzenia. Jest to ustawienie domyślne w przypadku plików binarnych.lib32
: dopasowuje się do 32-bitowego interfejsu ABI urządzenia, jeśli jest on obsługiwany.lib64
: odpowiada 64-bitowemu interfejsowi ABI obsługiwanego urządzenia.prefer32
: pasuje do 32-bitowego interfejsu ABI urządzenia, jeśli jest obsługiwany. Jeśli ABI 32-bitowy nie jest obsługiwany, pasuje do ABI 64-bitowego.both
: pasuje do obu interfejsów ABI. Jest to wartość domyślna w usłudzenative_shared_libraries
.
Właściwości java
, libraries
i prebuilts
są niezależne od interfejsu 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 plik APEX za pomocą różnych kluczy. Gdy wymagany jest nowy klucz, utwórz parę kluczy publiczny-prywatny i moduł apex_key
. Użyj właściwości key
, aby podpisać APEX za pomocą klucza. Klucz publiczny jest automatycznie dołączany do Apex o nazwie 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 działania apexd
weryfikuje APEX za pomocą klucza publicznego o tym samym identyfikatorze na urządzeniu.
Podpisywanie APEX
Podpisuj APEX w taki sam sposób, w jaki podpisujesz pliki APK. Zapisz APEX 2 razy: raz dla mini systemu plików (plik apex_payload.img
) i raz dla całego pliku.
Aby podpisać APEX na poziomie pliku, ustaw właściwość certificate
na jeden z tych 3 sposobów:
- Nie ustawiono: jeśli nie ustawisz żadnej wartości, certyfikat APEX będzie podpisany certyfikatem znajdującym się pod adresem
PRODUCT_DEFAULT_DEV_CERTIFICATE
. Jeśli nie ustawisz żadnej flagi, ścieżka zostanie domyślnie ustawiona nabuild/target/product/security/testkey
. <name>
: plik APEX jest podpisany certyfikatem<name>
znajdującym się w tym samym katalogu co plikPRODUCT_DEFAULT_DEV_CERTIFICATE
.:<name>
: APEX jest podpisany certyfikatem zdefiniowanym przez moduł utworu o nazwie<name>
. Moduł certyfikatu można zdefiniować w ten 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 parametry supportsRebootlessUpdate
w pliku apex_manifest.json
jest ustawiona wartość true
, a obecnie zainstalowany plik APEX jest nieużywany (np. wszystkie usługi, które zawiera, zostały zatrzymane), nowy plik APEX można zainstalować bez ponownego uruchamiania komputera za pomocą flagi --force-non-staged
.
adb install --force-non-staged apex_file_name
Używanie APEX
Po ponownym uruchomieniu interfejs APEX jest podłączany w katalogu /apex/<apex_name>@<version>
. Jednocześnie można zamontować wiele wersji tego samego Apex.
Spośród ścieżek do podłączenia ta, która odpowiada najnowszej wersji, jest zamontowana w miejscu /apex/<apex_name>
.
Klienci mogą używać ścieżki podłączonej do powiązania do odczytu i wykonywania plików z APEX.
Punkty końcowe APEX są zwykle używane w taki sposób:
- OEM lub ODM wstępnie wczytuje APEX w sekcji
/system/apex
podczas wysyłki urządzenia. - Do plików w APEX można uzyskać dostęp przez ścieżkę
/apex/<apex_name>/
. - Gdy na urządzeniu
/data/apex
zainstalowana jest zaktualizowana wersja APEX, po ponownym uruchomieniu ścieżka wskazuje na nową wersję APEX.
Aktualizowanie usługi za pomocą APEX
Aby zaktualizować usługę za pomocą APEX:
Oznacz usługę na partycji systemowej jako możliwą 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 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
w Apex. W APEX-ach nie są obsługiwane wyzwalacze działań.
Jeśli usługa oznaczona jako aktualizowana rozpocznie się przed aktywacją APEX, jej uruchomienie zostanie opóźnione do czasu zakończenia aktywacji APEX.
Skonfiguruj system do obsługi aktualizacji APEX
Aby obsługiwać aktualizacje plików APEX, ustaw tę właściwość systemu na true
.
<device.mk>:
PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true
BoardConfig.mk:
TARGET_FLATTEN_APEX := false
lub po prostu
<device.mk>:
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
Spłaszczony APEX
W przypadku starszych urządzeń zaktualizowanie starego jądra tak, aby w pełni obsługiwał APEX, jest niemożliwe lub niemożliwe. Na przykład jądro może być skompilowane bez CONFIG_BLK_DEV_LOOP=Y
, co jest kluczowe dla zamontowania obrazu systemu plików w APEX.
Flattened APEX to specjalnie utworzona wersja APEX, którą można aktywować na urządzeniach ze starszym jądrem. Pliki w sprasowanym pliku APEX są instalowane bezpośrednio w katalogu wbudowanej partycji. Na przykład klucz lib/libFoo.so
w płaskim punkcie APEXmy.apex
jest instalowany w /system/apex/my.apex/lib/libFoo.so
.
Aktywowanie płaskiego punktu APEX nie obejmuje urządzenia z pętlą. Cały katalog /system/apex/my.apex
jest bezpośrednio połączony z /apex/name@ver
.
Nie można aktualizować spłaszczonych APEX-ów przez pobranie z sieci zaktualizowanych wersji APEX-ów, ponieważ pobranych APEX-ów nie można spłaszczyć. Rozdzielone punkty APEX można aktualizować tylko za pomocą zwykłego OTA.
Spłaszczony APEX to konfiguracja domyślna. Oznacza to, że wszystkie punkty APK są domyślnie spłaszczone, chyba że jawnie skonfigurujesz urządzenie pod kątem tworzenia niespłaszczonych punktów na potrzeby obsługi aktualizacji APEX (jak wyjaśniono powyżej).
Mieszanie spłaszczonych i niespłaszczonych APEX na urządzeniu NIE jest obsługiwane. Punkty APEX w urządzeniu muszą być albo niespłaszczone, albo w całości spłaszczone.
Jest to szczególnie ważne, gdy wysyłasz wstępnie podpisane pakiety APEX do projektów takich jak Mainline. APEX-y, które nie są wstępnie podpisane (czyli są kompilowane ze źródła), również nie powinny być spłaszczone i podpisane odpowiednimi kluczami. Urządzenie powinno dziedziczyć ustawienie updatable_apex.mk
, zgodnie z opisem w sekcji Aktualizowanie usługi za pomocą punktu APEX.
Skompresowane APEX-y
Android 12 i nowsze wersje obsługują kompresję APEX, która zmniejsza wpływ na miejsce na dane w przypadku pakietów APEX z możliwością aktualizacji. Po zainstalowaniu aktualizacji Apex, chociaż fabrycznie zainstalowana wersja nie jest już używana, nadal zajmuje tyle samo miejsca. Zajmowane miejsce pozostaje niedostępne.
Kompresja APEX minimalizuje ten wpływ na miejsce na dane, używając bardzo skompresowanego zestawu plików APEX na partycjach tylko do odczytu (takich jak partycja /system
). Android 12 i nowsze korzystają z algorytmu DEFLATE kompresji plików ZIP.
Kompresja nie zapewnia optymalizacji w tych przypadkach:
Wstępnie wczytuj punkty APEX, które muszą być zamontowane bardzo wcześnie w sekwencji rozruchu.
Nieaktualizowane pakiety APEX. Kompresja jest przydatna tylko wtedy, gdy w partycji
/data
zainstalowana jest zaktualizowana wersja APEX. Pełna lista komponentów APEX, które można aktualizować, jest dostępna na stronie Komponenty modułowego systemu.Dynamic shared libs APEX. Ponieważ
apexd
zawsze aktywuje obie wersje takich APEX-ów (wstępnie zainstalowanych i uaktualnionych), ich kompresowanie nie zwiększa wartości.
skompresowany format pliku APEX,
Jest to format skompresowanego pliku APEX.
Rysunek 2. skompresowany format pliku APEX,
Najwyższego poziomu skompresowany plik APEX to plik ZIP, który zawiera oryginalny plik apex w formie skróconej o poziomie kompresji 9 oraz innych plikach nieskompresowanych.
Cztery pliki składają się na plik APEX:
original_apex
: spakowany z poziomem kompresji 9. Jest to oryginalny, niezpakowany plik APEX.apex_manifest.pb
: tylko zapisaneAndroidManifest.xml
: tylko przechowywaneapex_pubkey
: tylko przechowywane
Pliki apex_manifest.pb
, AndroidManifest.xml
i apex_pubkey
to kopie odpowiadających im plików w usłudze original_apex
.
Kompilowanie skompresowanego pliku APEX
Skompresowany APEX można utworzyć za pomocą narzędzia apex_compression_tool.py
znajdującego się pod adresem system/apex/tools
.
W systemie kompilacji dostępnych jest kilka parametrów związanych z kompresją APEX.
W polu Android.bp
o tym, czy plik APEX można skompresować, decyduje 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 utworzony na podstawie źródła musi zawierać skompresowane pliki APEX.
W przypadku eksperymentów lokalnych możesz wymusić kompresję przez kompresję punktów APEX przez kompresję, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX=
na true
.
Skompresowane pliki APEX wygenerowane przez system kompilacji mają rozszerzenie .capex
.
Rozszerzenie ułatwia odróżnianie skompresowanych i nieskompresowanych wersji pliku APEX.
Obsługiwane algorytmy kompresji
Android 12 obsługuje tylko kompresję deflate-zip.
Aktywowanie skompresowanego pliku APEX podczas uruchamiania
Zanim będzie można aktywować skompresowany plik APEX, znajdujący się w nim plik original_apex
zostanie zdekompresowany do katalogu /data/apex/decompressed
. Wygenerowany plik APEX po rozpakowaniu jest twardo połączony z katalogiem /data/apex/active
.
Poniżej znajdziesz przykład ilustrujący proces opisany powyżej.
Potraktuj /system/apex/com.android.foo.capex
jako skompresowany APEX
z kodem wersji 37.
- Plik
original_apex
wewnątrz/system/apex/com.android.foo.capex
jest dekompresowany do formatu/data/apex/decompressed/com.android.foo@37.apex
. - Wykonywana jest operacja
restorecon /data/apex/decompressed/com.android.foo@37.apex
, aby sprawdzić, czy ma prawidłową etykietę SELinux. - Aby sprawdzić ważność
/data/apex/decompressed/com.android.foo@37.apex
, przeprowadzane są weryfikacje:apexd
sprawdza klucz publiczny w/data/apex/decompressed/com.android.foo@37.apex
, aby potwierdzić, że jest on taki sam jak w/system/apex/com.android.foo.capex
. - Plik
/data/apex/decompressed/com.android.foo@37.apex
jest połączony na stałe z katalogiem/data/apex/active/com.android.foo@37.apex
. - Zwykła logika aktywacji nieskompresowanych plików APEX jest wykonywana za pomocą
/data/apex/active/com.android.foo@37.apex
.
Interakcja z OTA
Kompresowane pliki APEX mają wpływ na dostarczanie i stosowanie OTA. Aktualizacja OTA może zawierać skompresowany plik APEX o wyższym poziomie wersji niż ten, który jest aktywny na urządzeniu, dlatego przed ponownym uruchomieniem urządzenia w celu zastosowania aktualizacji OTA należy zarezerwować pewną ilość wolnego miejsca.
Aby zapewnić obsługę systemu OTA, apexd
udostępnia 2 interfejsy API powiązań:
calculateSizeForCompressedApex
– oblicza rozmiar wymagany do rozpakowania plików APEX w pakiecie OTA. Można go użyć do sprawdzenia, czy na urządzeniu jest wystarczająco dużo miejsca przed pobraniem aktualizacji OTA.reserveSpaceForCompressedApex
– rezerwuje miejsce na dysku do wykorzystania w przyszłości przezapexd
do dekompresji skompresowanych plików APEX w pakiecie OTA.
W przypadku aktualizacji OTA A/B apexd
próbuje rozpakować pliki w tle w ramach rutyny OTA po instalacji. Jeśli dekompresja się nie powiedzie, apexd
wykona dekompresję podczas uruchamiania, co spowoduje zastosowanie aktualizacji OTA.
Rozważane alternatywy podczas opracowywania APEX
Oto niektóre opcje, które AOSP wzięło pod uwagę podczas projektowania formatu pliku APEX, oraz powody ich uwzględnienia lub wykluczenia.
zwykłe systemy zarządzania pakietami,
Dystrybucje Linuksa zawierają systemy zarządzania pakietami, takie jak dpkg
i rpm
, które są wydajne, dopracowane i niezawodne. Nie zostały jednak zastosowane w przypadku APEX, ponieważ nie zapewniają ochrony pakietów po ich zainstalowaniu. Weryfikacja jest przeprowadzana tylko podczas instalowania pakietów.
Hakerzy mogą niezauważyć naruszenia integralności 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 podczas każdego wejścia/wyjścia. Wszelkie ingerencje w elementy systemu muszą być zabronione lub możliwe do wykrycia, aby urządzenie mogło odmówić uruchomienia w przypadku przejęcia.
dm-crypt dla integralności.
Pliki w kontenerze APEX pochodzą z wbudowanych partycji (np. /system
) chronionych przez dm-verity, przy czym jakiekolwiek modyfikacje plików są zabronione nawet po podłączeniu partycji. Aby zapewnić taki sam poziom bezpieczeństwa plików, wszystkie pliki w APEX są przechowywane w obrazie systemu plików sparowanym z drzewem haszu i deskryptorem vbmeta. Bez funkcji dm-verity moduł APEX w partycji /data
jest narażony na niezamierzone modyfikacje wprowadzone po jego zweryfikowaniu i zainstalowaniu.
W rzeczywistości partycja /data
jest też chroniona przez warstwy szyfrowania, takie jak dm-crypt. Chociaż zapewnia to pewien poziom ochrony przed modyfikacją, jego głównym celem jest ochrona prywatności, a nie integralności. Gdy osoba przeprowadzająca atak uzyska dostęp do partycji /data
, nie będzie już można jej chronić – jest to regresja w porównaniu z tym, że każdy komponent systemu znajduje się na partycji /system
.
Drzewo skrótów w pliku APEX w połączeniu z dm-verity zapewnia ten sam poziom ochrony treści.
Przekieruj ścieżki z /system do /apex
Pliki komponentów systemowych zapakowane w APEX są dostępne za pomocą nowych ścieżek, takich jak /apex/<name>/lib/libfoo.so
. Gdy pliki znajdowały się w partycji /system
, były dostępne pod ścieżkami takimi jak /system/lib/libfoo.so
. Klient pliku APEX (inne pliki APEX lub platforma) musi używać nowych ścieżek. Zmiana ścieżki może wymagać zaktualizowania dotychczasowego kodu.
Chociaż jednym ze sposobów uniknięcia zmiany ścieżki jest nałożenie zawartości pliku na plik APEX na partycji /system
, zespół Androida zdecydował, że nie będzie nakładać plików na partycji /system
, ponieważ może to wpłynąć na wydajność, ponieważ liczba nakładanych plików (być może nawet ułożonych jeden na drugim) będzie rosł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 odpowiednich ścieżek w folderze /apex
. Zespół Androida odrzucił tę opcję,
ponieważ nie można zmienić wszystkich funkcji, które akceptują ścieżki.
Na przykład niektóre aplikacje statycznie łączą się z Bionic, który implementuje te funkcje.
W takich przypadkach aplikacje nie są przekierowywane.