Format pliku APEX

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.

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.

  1. Plik APEX jest pobierany za pomocą aplikacji do instalowania pakietów, ADB lub innego źródła.
  2. Menedżer pakietów rozpocznie procedurę instalacji. Po rozpoznaniu, że plik jest APEX, menedżer pakietów przekazuje kontrolę do menedżera APEX.
  3. Menedżer APEX weryfikuje plik APEX.
  4. 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.
  5. Żądający instalacji otrzymuje rozgłoszenie po pomyślnej weryfikacji pakietu.
  6. Aby kontynuować instalację, należy ponownie uruchomić system.
  7. 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:

    1. Weryfikuje plik APEX.
    2. Tworzy urządzenie pętli zwrotnej z pliku APEX.
    3. Tworzy urządzenie blokowe mapowania urządzeń na urządzeniu sprzężenia zwrotnego.
    4. 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 dla native_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 pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_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 to build/target/product/security/testkey .
  • <name> : APEX jest podpisany certyfikatem <name> w tym samym katalogu co PRODUCT_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:

  1. Producent OEM lub ODM wstępnie ładuje APEX w katalogu /system/apex gdy urządzenie jest dostarczane.
  2. Pliki w APEX są dostępne za pośrednictwem ścieżki /apex/<apex_name>/ .
  3. 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:

  1. 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
    
  2. Utwórz nowy plik .rc dla zaktualizowanej usługi. Użyj opcji override , 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.

Diagram shows the format of a compressed APEX file

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.

  1. original_apex plik_apex wewnątrz /system/apex/com.android.foo.capex jest dekompresowany do /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex jest wykonywane w celu sprawdzenia, czy ma poprawną etykietę SELinux.
  3. 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 .
  4. 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 .
  5. 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 przez apexd 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.