Format kontenera Android Pony EXpress (APEX) został wprowadzony w wersji Android 10 i jest używany w procesie instalacji w przypadku modułów systemowych niskiego poziomu. Ten format ułatwia aktualizacje komponentów systemu, które nie pasują do standardowej 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 też odnosić się do pliku APEX.
Tło
Chociaż Android obsługuje aktualizacje modułów, które pasują do standardowego modelu aplikacji (np. usług, działań), za pomocą aplikacji instalujących pakiety (takich jak Sklep Google Play), stosowanie podobnego modelu w przypadku komponentów niskiego poziomu systemu operacyjnego ma te wady:
- Modułów opartych na pliku APK nie można używać we wczesnej fazie uruchamiania. Menedżer pakietów jest centralnym repozytorium informacji o aplikacjach i może być uruchamiany tylko z poziomu menedżera aktywności, który jest gotowy na późniejszym etapie procedury uruchamiania.
- Format pliku APK (zwłaszcza plik manifestu) jest przeznaczony do aplikacji na Androida, a moduły systemowe nie zawsze są odpowiednie.
Design
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 wybrano ten projekt APEX, znajdziesz w artykule Alternatywne rozwiązania rozważane podczas tworzenia APEX.
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:
apex_manifest.json
AndroidManifest.xml
apex_payload.img
apex_pubkey
Plik apex_manifest.json
zawiera nazwę i wersję pakietu, które identyfikują plik APEX. Oto 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ć istniejącego narzędzia, takiego jak aapt
, do sprawdzania podstawowych metadanych z pliku. Plik zawiera nazwę pakietu i informacje o wersji. Te informacje są zazwyczaj dostępne również w sekcji apex_manifest.json
.
W przypadku nowego kodu i systemów, które mają styczność z APEX, zalecamy użycie funkcji apex_manifest.json
zamiast AndroidManifest.xml
. AndroidManifest.xml
może zawierać dodatkowe informacje docelowe, których można używać w dotychczasowych narzędziach 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
. Dane systemu plików nie są analizowane (ponieważ obraz powinien być instalowany na miejscu). Zwykłe pliki są zawarte w pliku apex_payload.img
.
apex_pubkey
to klucz publiczny użyty do podpisania obrazu systemu plików. W czasie działania ten klucz zapewnia, że pobrany plik APEX jest podpisany tym samym podmiotem, który podpisuje ten sam plik APEX na wbudowanych partycjach.
Wytyczne dotyczące nazewnictwa w APEX
Aby uniknąć konfliktów nazw między nowymi Apexami w miarę rozwoju platformy, stosuj te wskazówki dotyczące nazewnictwa:
com.android.*
- Zarezerwowane dla AOSP APEX. nie jest unikalny dla żadnej firmy ani urządzenia;
com.<companyname>.*
- Zarezerwowany dla firmy. Może być używany przez wiele urządzeń tej firmy.
com.<companyname>.<devicename>.*
- Zarezerwowane dla APEX-ów unikalnych dla konkretnego 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 fabrycznie zainstalowane na urządzeniu w sekcji /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 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, która wysłała prośbę o instalację, otrzymuje powiadomienie po pomyślnej weryfikacji pakietu.
- Aby kontynuować instalację, musisz ponownie uruchomić system.
Podczas następnego uruchomienia menedżer APEX wczyta wewnętrzną bazę danych i wykonuje te czynności dla 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
).
Gdy wszystkie pliki APEX wymienione w bazie danych wewnętrznej są zamontowane, menedżer APEX udostępnia usługę binder innym składnikom systemu, aby mogli oni wysyłać zapytania dotyczące zainstalowanych plików APEX. Na przykład inne komponenty systemu mogą zapytać o listę plików APEX zainstalowanych na urządzeniu lub o dokładną ścieżkę do zamontowanego pliku APEX, aby uzyskać do niego dostęp.
Pliki APEX to pliki APK
Pliki APEX to prawidłowe pliki APK, ponieważ są to podpisane archiwa ZIP (za pomocą 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 minimalny i zawiera pakiet name
, versionCode
oraz opcjonalnie targetSdkVersion
, minSdkVersion
i maxSdkVersion
do celów precyzyjnego kierowania. 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 biblioteki współdzielone
- Natywne pliki wykonywalne
- Pliki JAR
- pliki danych,
- Pliki konfiguracji
Nie oznacza to jednak, ż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. Po pierwsze, plik apex_payload.img
(a dokładniej załączony do niego opis vbmeta) jest podpisany kluczem.apex_payload.img
Następnie cały plik APEX jest podpisywany przy użyciu schematu podpisu pliku APK w wersji 3. W tym procesie używane są 2 różne klucze.
Na urządzeniu jest zainstalowany klucz publiczny odpowiadający kluczowi prywatnemu użytemu do podpisania 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 w wbudowanych partycjach
Pliki APEX mogą znajdować się na wbudowanych partycjach, takich jak /system
. Partycja jest już objęta funkcją dm-verity, więc pliki APEX są montowane bezpośrednio na urządzeniu pętli.
Jeśli w wbudowanej partycji znajduje się plik APEX, można go zaktualizować, przesyłając pakiet APEX o tej samej nazwie i większym lub równym kodzie wersji. 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. Jednak 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 Linux: 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ść pętli zwrotnej i dm-verity ma duże znaczenie dla dobrej wydajności systemu przy użyciu modułów APEX.
Obsługiwane wersje jądra
Moduł głównego modułu 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 do obsługi modułów APEX są zawarte w drzewie wspólnym 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 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 poprawek dla wersji jądra 4.4.
- UPSTREAM: loop: dodaj ioctl do zmiany rozmiaru bloku logicznego (4.4)
- BACKPORT: block/loop: set hw_sectors (4.4)
- UPSTREAM: loop: dodaj LOOP_SET_BLOCK_SIZE w ioctl kompatybilności (4.4)
- ANDROID: mnt: poprawka next_descendent (4.4)
- ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
- ANDROID: mnt: prawidłowo rozpowszechniaj ponowne montowanie (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 je z gałęzi android-common
.
Wymagane opcje konfiguracji jądra
Poniżej znajdziesz wymagania dotyczące podstawowej konfiguracji modułów APEX, które zostały wprowadzone w Androidzie 10. Elementy oznaczone gwiazdką (*) to wymagania obowiązujące w przypadku Androida 9 i starszych.
(*) 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
W tej sekcji opisaliśmy, jak utworzyć plik APEX za pomocą systemu kompilacji Androida.
Oto przykład Android.bp
dla obiektu 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",
}
apex_manifest.json
przykład:
{
"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 zawierają zależności pośrednie bibliotek natywnych lub plików wykonywalnych. Jeśli na przykład biblioteka libFoo
zależy od biblioteki libBar
, obie biblioteki są uwzględniane, gdy w właściwości native_shared_libs
jest wymieniona tylko biblioteka libFoo
.
Obsługa wielu 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 APEX jest kierowany na urządzenia z jednym interfejsem ABI (czyli tylko 32- lub 64-bitowym), są instalowane tylko biblioteki z odpowiednim interfejsem ABI.
Zainstaluj usługę binaries
tylko dla podstawowego ABI urządzenia w ten sposób:
- Jeśli urządzenie jest tylko 32-bitowe, zostanie zainstalowana tylko 32-bitowa wersja pliku binarnego.
- Jeśli urządzenie jest tylko 64-bitowe, instalowana jest tylko 64-bitowa wersja pliku binarnego.
Aby uzyskać 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
: pasuje do głównego interfejsu ABI urządzenia. Jest to domyślne ustawienie dla plików binarnych.lib32
: pasuje do 32-bitowego interfejsu ABI urządzenia (jeśli jest 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 interfejs ABI 32-bitowy nie jest obsługiwany, dopasowuje interfejs ABI 64-bitowy.both
: pasuje do obu interfejsów ABI. Jest to wartość domyślna dla polanative_shared_libraries
.
Właściwości java
, libraries
i prebuilts
nie zależą 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 pliku APEX jest zapisany w pliku APEX. Podczas działania apexd
weryfikuje APEX za pomocą klucza publicznego z tym samym identyfikatorem na urządzeniu.
Podpisywanie APEX
Podpisuj pliki APEX w taki sam sposób jak pliki APK. Zapisz APEX 2 razy: 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
w 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>
: plik APEX jest podpisany certyfikatem zdefiniowanym przez moduł Soong 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)
}
Instalowanie 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 zainstalowana wersja APEX jest nieużywana (np. wszystkie usługi, które zawiera, zostały zatrzymane), nową wersję APEX można zainstalować bez ponownego uruchamiania z flagą --force-non-staged
.
adb install --force-non-staged apex_file_name
Używanie APEX
Po ponownym uruchomieniu urządzenia APEX zostanie zamontowany w katalogu /apex/<apex_name>@<version>
. Jednocześnie można zamontować wiele wersji tego samego Apex.
Spośród ścieżek do zamontowania ta, która odpowiada najnowszej wersji, jest zamontowana w miejscu /apex/<apex_name>
.
Klienci mogą używać ścieżki zamontowanej w ramach bind do odczytu lub wykonania plików z APEX.
Punkty końcowe APEX są zwykle używane w taki sposób:
- Producent OEM lub ODM wstępnie wczytuje APEX w ramach
/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 zaktualizowania. 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 zmienić definicję dotychczasowej usługi./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.
Konfigurowanie systemu na potrzeby 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
<device.mk>:
$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)
Rozdzielony APEX
W przypadku starszych urządzeń czasami nie można lub nie da się zaktualizować starego jądra, aby w pełni obsługiwało ono APEX. Na przykład jądro może być skompilowane bez CONFIG_BLK_DEV_LOOP=Y
, co jest niezbędne do 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 spłaszczonym pliku APEX są instalowane bezpośrednio w katalogu w ramach wbudowanej partycji. Na przykład lib/libFoo.so
w sprasowanym APEXmy.apex
jest zainstalowany w /system/apex/my.apex/lib/libFoo.so
.
Aktywacja spłaszczonego APEX nie wymaga użycia urządzenia pętlowego. 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ć. Spłaszczone APEX można aktualizować tylko za pomocą zwykłej aktualizacji OTA.
Konfiguracja sprasowanej APEX jest konfiguracją domyślną. Oznacza to, że wszystkie Apexy są domyślnie spłaszczone, chyba że skonfigurujesz urządzenie tak, aby tworzyć niespłaszczone Apexy, które będą obsługiwać aktualizacje Apexów (jak wyjaśniono powyżej).
Mieszanie spłaszczonych i niespłaszczonych APEX na urządzeniu NIE jest obsługiwane. Wszystkie wierzchołki APEX na urządzeniu muszą być niesprasowane lub wszystkie sprasowane.
Jest to szczególnie ważne w przypadku wysyłania podpisanych wcześniej wstępnie utworzonych pakietów APEX na potrzeby projektów takich jak Mainline. Pliki APEX, 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ć z poziomu updatable_apex.mk
, jak to opisano w artykule Aktualizowanie usługi za pomocą APEX.
Skompresowane APEX-y
Android 12 i nowsze wersje obsługują kompresję APEX, która zmniejsza wpływ na ilość miejsca 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. Zajęty pokój pozostaje niedostępny.
Kompresja APEX minimalizuje wpływ na miejsce na dane, ponieważ używa bardzo skompresowanego zestawu plików APEX na partycjach tylko do odczytu (takich jak partycja /system
). Android 12 i nowsze wersje używają algorytmu kompresji zip DEFLATE.
Kompresja nie zapewnia optymalizacji w tych przypadkach:
Bootstrap APEXes, które muszą być zamontowane bardzo wcześnie w sekwencji uruchamiania.
Nie można aktualizować pakietów APEX. Kompresja jest korzystna tylko wtedy, gdy na partycji
/data
jest zainstalowana zaktualizowana wersja APEX. Pełna lista komponentów APEX, które można aktualizować, jest dostępna na stronie Komponenty modularnego systemu.Dynamiczne szczyty APEX bibliotek współdzielonych. 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,
Na najwyższym poziomie skompresowany plik APEX to plik ZIP zawierający oryginalny plik apex w postaci skompresowanej z poziomem kompresji 9 oraz inne pliki nieskompresowane.
Plik APEX składa się z 4 plików:
original_apex
: spakowany z poziomem kompresji 9. Jest to oryginalny, niezpakowany plik APEX.apex_manifest.pb
: tylko przechowywaneAndroidManifest.xml
: tylko przechowywaneapex_pubkey
: tylko przechowywane
Pliki apex_manifest.pb
, AndroidManifest.xml
i apex_pubkey
to kopie odpowiednich plików w usłudze original_apex
.
Kompilowanie skompresowanego pliku APEX
Kompresowany plik APEX można skompilować za pomocą narzędzia apex_compression_tool.py
, które znajduje się na stronie system/apex/tools
.
W systemie kompilacji dostępnych jest kilka parametrów związanych z kompresją APEX.
W Android.bp
kompresowanie pliku APEX jest możliwe, jeśli właściwość compressible
ma wartość „true”:
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 zmusić kompilację do skompresowania APEX, ustawiając wartość 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 skompresowany plik APEX zostanie aktywowany, plik original_apex
znajdujący się w tym pliku musi zostać rozpakowany 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.
Rozważ /system/apex/com.android.foo.capex
jako skompresowany APEX, który został aktywowany, a jego kod wersji to 37.
- Plik
original_apex
znajdujący się w folderze/system/apex/com.android.foo.capex
jest rozpakowywany do folderu/data/apex/decompressed/com.android.foo@37.apex
. restorecon /data/apex/decompressed/com.android.foo@37.apex
jest wykonywane w celu sprawdzenia, czy ma ona prawidłową etykietę SELinux.- W celu sprawdzenia ważności
/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 ten w/system/apex/com.android.foo.capex
. - Plik
/data/apex/decompressed/com.android.foo@37.apex
jest twardo połączony z katalogiem/data/apex/active/com.android.foo@37.apex
. - Zwykła logika aktywacji w przypadku nieskompresowanych plików APEX jest wykonywana na serwerze
/data/apex/active/com.android.foo@37.apex
.
Interakcja z OTA
Skompresowane 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 obsługiwać system OTA, apexd
udostępnia te 2 interfejsy bindera:
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 na potrzeby przyszłego użycia przezapexd
do rozpakowywania 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 mają systemy zarządzania pakietami, takie jak dpkg
i rpm
, które są wydajne, dojrzałe 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.
Osoby przeprowadzające atak mogą niezauważenie naruszyć integralność zainstalowanych pakietów. Jest to regresja w Androidzie, w którym wszystkie komponenty systemu były przechowywane w systemach plików tylko do odczytu, których integralność jest chroniona przez dm-verity dla każdego operacji wejścia/wyjścia. Wszelkie manipulacje komponentami systemu muszą być zabronione lub wykrywalne, aby urządzenie mogło odmówić uruchomienia w przypadku naruszenia zabezpieczeń.
dm-crypt w celu zapewnienia integralności.
Pliki w kontenerze APEX pochodzą z wbudowanych partycji (na przykład partycji /system
), które są chronione przez dm-verity. W tym przypadku wszelkie modyfikacje plików są zabronione nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom zabezpieczeń dla plików, wszystkie pliki w pliku APEX są przechowywane w pliku obrazu systemu, który jest sparowany z drzewem skrótów 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 rzeczy samej 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 atakujący uzyska dostęp do partycji /data
, nie będzie już żadnych dodatkowych zabezpieczeń. Jest to regresja w porównaniu do sytuacji, gdy wszystkie komponenty systemu znajdują się na partycji /system
.
Drzewo skrótów w pliku APEX w połączeniu z dm-verity zapewnia ten sam poziom ochrony treści.
Przekierowywanie ścieżek z adresu /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. W związku ze zmianą ścieżki może być konieczna aktualizacja 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 da się zmienić wszystkich funkcji, które przyjmują ścieżki.
Na przykład niektóre aplikacje łączą statycznie Bionic, który implementuje funkcje.
W takich przypadkach aplikacje nie są przekierowywane.