Format pliku APEX

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Format kontenera Android Pony EXpress (APEX) został wprowadzony w systemie Android 10 i jest używany w przepływie instalacji dla modułów systemowych niższego poziomu. Ten format ułatwia aktualizowanie składników systemu, które nie pasują do standardowego modelu aplikacji systemu Android. Niektóre przykładowe komponenty to natywne usługi i biblioteki, warstwy abstrakcji sprzętu ( HAL ), środowisko wykonawcze ( ART ) i biblioteki klas.

Termin „APEX” może również odnosić się do pliku APEX.

Tło

Chociaż Android obsługuje aktualizacje modułów, które pasują do standardowego modelu aplikacji (na przykład usług, działań) za pośrednictwem aplikacji instalujących pakiety (takich jak aplikacja Sklep Google Play), używanie podobnego modelu dla komponentów systemu operacyjnego niższego poziomu ma następujące wady:

  • Modułów opartych na pakiecie APK nie można używać na początku sekwencji rozruchowej. Menedżer pakietów jest centralnym repozytorium informacji o aplikacjach i można go uruchomić tylko z menedżera aktywności, który jest gotowy na późniejszym etapie procedury rozruchu.
  • Format APK (w szczególności manifest) jest przeznaczony dla aplikacji na Androida, a moduły systemowe nie zawsze są odpowiednie.

Projekt

W tej sekcji opisano projekt wysokiego poziomu formatu pliku APEX i menedżera APEX, który jest usługą zarządzającą plikami APEX.

Aby uzyskać więcej informacji na temat tego, dlaczego wybrano ten projekt dla APEX, zobacz Alternatywy brane pod uwagę podczas opracowywania APEX .

Format APEX

To jest format pliku APEX.

Format pliku APEX

Rysunek 1. Format pliku APEX

Na najwyższym poziomie plik APEX to plik zip, w którym pliki są przechowywane w postaci nieskompresowanej i mieszczą się w granicach 4 KB.

Cztery pliki w pliku APEX to:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

Plik apex_manifest.json zawiera nazwę i wersję pakietu, które identyfikują plik APEX.

Plik AndroidManifest.xml umożliwia plikowi APEX korzystanie z narzędzi i infrastruktury związanej z pakietem APK, takich jak ADB, PackageManager i aplikacje do instalacji pakietów (takie jak Sklep Play). Na przykład plik APEX może używać istniejącego narzędzia, takiego jak aapt , do sprawdzania podstawowych metadanych z pliku. Plik zawiera informacje o nazwie i wersji pakietu. Ta informacja jest ogólnie dostępna również w apex_manifest.json .

apex_manifest.json jest zalecany zamiast AndroidManifest.xml w przypadku nowego kodu i systemów obsługujących APEX. AndroidManifest.xml może zawierać dodatkowe informacje dotyczące kierowania, które mogą być używane przez istniejące narzędzia do publikowania aplikacji.

apex_payload.img to obraz systemu plików ext4 wspierany przez dm-verity. Obraz jest montowany w czasie wykonywania za pomocą urządzenia pętli zwrotnej. W szczególności drzewo skrótów i blok metadanych są tworzone przy użyciu biblioteki libavb . Ładunek systemu plików nie jest analizowany (ponieważ obraz powinien dać się zamontować na miejscu). Zwykłe pliki są zawarte w pliku apex_payload.img .

apex_pubkey to klucz publiczny używany do podpisywania obrazu systemu plików. W czasie wykonywania ten klucz zapewnia, że ​​pobrany APEX jest podpisany za pomocą tej samej jednostki, która podpisuje ten sam APEX na wbudowanych partycjach.

Wytyczne nazewnictwa APEX

Aby zapobiec konfliktom nazewnictwa między nowymi APEX w miarę rozwoju platformy, skorzystaj z następujących wskazówek dotyczących nazewnictwa:

  • com.android.*
    • Zarezerwowane dla AOSP APEX. Nie unikalny dla żadnej firmy lub urządzenia.
  • com.<companyname>.*
    • Zarezerwowane dla firmy. Potencjalnie używany przez wiele urządzeń tej firmy.
  • com.<companyname>.<devicename>.*
    • Zarezerwowane dla APEX-ów unikalnych dla określonego urządzenia (lub podzbioru urządzeń).

Menedżer APEX

Menedżer APEX (lub apexd ) to samodzielny natywny proces odpowiedzialny za weryfikację, instalowanie i odinstalowywanie plików APEX. Ten proces jest uruchamiany i jest gotowy na wczesnym etapie sekwencji rozruchowej. Pliki APEX są zwykle preinstalowane na urządzeniu w katalogu /system/apex . Menedżer APEX domyślnie używa tych pakietów, jeśli nie są dostępne żadne aktualizacje.

Sekwencja aktualizacji APEX korzysta z klasy PackageManager i wygląda następująco.

  1. Plik APEX jest pobierany za pośrednictwem aplikacji do instalacji pakietów, ADB lub innego źródła.
  2. Menedżer pakietów rozpoczyna 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 odbiera transmisję po pomyślnej weryfikacji pakietu.
  6. Aby kontynuować instalację, system musi zostać ponownie uruchomiony.
  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 pętli zwrotnej.
    4. Montuje urządzenie blokowe programu mapującego urządzenia w unikatowej ścieżce (na przykład /apex/ name @ ver ).

Gdy wszystkie pliki APEX wymienione w wewnętrznej bazie danych są zamontowane, menedżer APEX udostępnia usługę spinacza dla innych komponentów systemu, aby przeszukiwać informacje o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wysyłać zapytania do listy plików APEX zainstalowanych w urządzeniu lub sprawdzać dokładną ścieżkę, w której zainstalowany jest określony APEX, aby można było uzyskać dostęp do plików.

Pliki APEX to pliki APK

Pliki APEX są prawidłowymi plikami APK, ponieważ są podpisanymi archiwami zip (przy użyciu schematu podpisu APK) zawierającymi plik AndroidManifest.xml . Dzięki temu pliki APEX mogą korzystać z infrastruktury dla plików APK, takich jak aplikacja do instalacji pakietów, narzędzie do podpisywania i menedżer pakietów.

Plik AndroidManifest.xml w pliku APEX jest minimalny i składa się z name pakietu , versionCode i opcjonalnych targetSdkVersion , minSdkVersion i maxSdkVersion dla precyzyjnego kierowania. Informacje te umożliwiają dostarczanie plików APEX za pośrednictwem istniejących kanałów, takich jak aplikacje do instalacji pakietów i ADB.

Obsługiwane typy plików

Format APEX obsługuje następujące typy plików:

  • Natywne biblioteki współdzielone
  • Natywne pliki wykonywalne
  • Pliki JAR
  • Pliki danych
  • Pliki konfiguracyjne

Nie oznacza to, że APEX może aktualizować wszystkie te typy plików. Możliwość aktualizacji typu pliku zależy od platformy i stabilności definicji interfejsów dla typów plików.

Podpisywanie

Pliki APEX są podpisywane na dwa sposoby. Najpierw apex_payload.img (w szczególności deskryptor vbmeta dołączony do apex_payload.img ) jest podpisany kluczem. Następnie cały APEX jest podpisany przy użyciu schematu podpisu APK v3 . W tym procesie używane są dwa różne klucze.

Po stronie urządzenia instalowany jest klucz publiczny odpowiadający kluczowi prywatnemu używanemu do podpisywania deskryptora vbmeta. Menedżer APEX używa klucza publicznego do weryfikacji APEX, które są wymagane do zainstalowania. Każdy APEX musi być podpisany różnymi kluczami i jest wymuszany zarówno w czasie kompilacji, jak i w czasie wykonywania.

APEX w przegrodach do zabudowy

Pliki APEX mogą znajdować się na wbudowanych partycjach, takich jak /system . Partycja jest już nad dm-verity, więc pliki APEX są montowane bezpośrednio nad urządzeniem pętli zwrotnej.

Jeśli APEX jest obecny we wbudowanej partycji, APEX można zaktualizować, dostarczając pakiet APEX o tej samej nazwie i kodzie wersji większym lub równym. Nowy APEX jest przechowywany w /data i podobnie jak w przypadku pakietów APK, nowo zainstalowana wersja przesłania wersję już obecną na wbudowanej partycji. Ale w przeciwieństwie do pakietów APK, nowo zainstalowana wersja APEX jest aktywowana dopiero po ponownym uruchomieniu.

Wymagania jądra

Aby obsługiwać moduły APEX mainline na urządzeniu z systemem Android, wymagane są następujące funkcje jądra Linux: sterownik pętli zwrotnej i dm-verity. Sterownik pętli zwrotnej montuje obraz systemu plików w module APEX, a dm-verity weryfikuje moduł APEX.

Wydajność sterownika pętli zwrotnej i dm-verity jest ważna dla osiągnięcia dobrej wydajności systemu podczas korzystania z modułów APEX.

Obsługiwane wersje jądra

Główne moduły APEX są obsługiwane na urządzeniach korzystających z jądra w wersji 4.4 lub wyższej. Nowe urządzenia uruchamiane z systemem Android 10 lub nowszym muszą używać jądra w wersji 4.9 lub nowszej, aby obsługiwać moduły APEX.

Wymagane poprawki jądra

Wymagane poprawki jądra do obsługi modułów APEX są zawarte we wspólnym drzewie Androida. Aby uzyskać łatki obsługujące APEX, użyj najnowszej wersji wspólnego drzewa Androida.

Wersja jądra 4.4

Ta wersja jest obsługiwana tylko dla urządzeń, które zostały zaktualizowane z Androida 9 do Androida 10 i chcą obsługiwać moduły APEX. Aby uzyskać wymagane poprawki, zdecydowanie zaleca się downmerge z gałęzi android-4.4 . Poniżej znajduje się lista wymaganych pojedynczych łat dla wersji jądra 4.4.

  • WYPRZEDAŻ: pętla: dodaj ioctl do zmiany rozmiaru bloku logicznego ( 4.4 )
  • BACKPORT: blok/pętla: ustaw hw_sectors ( 4.4 )
  • UPSTREAM: pętla: Dodaj LOOP_SET_BLOCK_SIZE w compat ioctl ( 4.4 )
  • ANDROID: mnt: Napraw next_descendent ( 4.4 )
  • ANDROID: mnt: remount powinien rozprzestrzeniać się na niewolników niewolników ( 4.4 )
  • ANDROID: mnt: Propaguj ponowne montowanie poprawnie ( 4.4 )
  • Cofnij „ANDROID: dm verity: dodaj minimalny rozmiar pobierania wstępnego” ( 4.4 )
  • WYDOBYCIE: pętla: upuść pamięci podręczne, jeśli zmieniono przesunięcie lub rozmiar bloku ( 4.4 )

Wersje jądra 4.9/4.14/4.19

Aby uzyskać wymagane łaty dla wersji jądra 4.9/4.14/4.19, połącz w dół z gałęzi android-common .

Wymagane opcje konfiguracji jądra

Poniższa lista przedstawia podstawowe wymagania konfiguracyjne dotyczące obsługi modułów APEX, które zostały wprowadzone w systemie Android 10. Elementy oznaczone gwiazdką (*) są istniejącymi wymaganiami z systemu Android 9 i niższych.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

Wymagania dotyczące parametrów wiersza polecenia jądra

Aby obsługiwać APEX, upewnij się, że parametry wiersza poleceń jądra spełniają następujące wymagania:

  • loop.max_loop NIE może być ustawiony
  • loop.max_part musi być <= 8

Budowanie APEX

W tej sekcji opisano, jak zbudować APEX przy użyciu systemu kompilacji Android. Poniżej znajduje się przykład Android.bp dla APEX o nazwie apex.test .

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

przykład apex_manifest.json :

{
  "name": "com.android.example.apex",
  "version": 1
}

przykład file_contexts :

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

Typy plików i lokalizacje w APEX

Typ pliku Lokalizacja w APEX
Biblioteki udostępnione /lib i /lib64 ( /lib/arm dla przetłumaczonego ramienia w x86)
Pliki wykonywalne /bin
Biblioteki Java /javalib
Prefabrykowane /etc

Zależności przechodnie

Pliki APEX automatycznie zawierają przechodnie zależności natywnych bibliotek współdzielonych lub plików wykonywalnych. Na przykład, jeśli libFoo zależy od libBar , dwie biblioteki są uwzględniane, gdy tylko libFoo jest wymienione we właściwości native_shared_libs .

Obsługa wielu ABI

Zainstaluj właściwość native_shared_libs zarówno dla podstawowego, jak i pomocniczego interfejsu binarnego aplikacji (ABI) urządzenia. Jeśli APEX jest przeznaczony dla urządzeń z pojedynczym ABI (czyli tylko 32-bitowym lub 64-bitowym), instalowane są tylko biblioteki z odpowiednim ABI.

Zainstaluj właściwość binaries tylko dla podstawowego ABI urządzenia, jak opisano poniżej:

  • Jeśli urządzenie jest tylko 32-bitowe, instalowany jest tylko 32-bitowy wariant pliku binarnego.
  • Jeśli urządzenie jest tylko 64-bitowe, instalowany jest tylko 64-bitowy wariant pliku binarnego.

Aby dodać szczegółową kontrolę nad ABI natywnych bibliotek i plików binarnych, użyj właściwości multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] .

  • first : Dopasowuje podstawowy ABI urządzenia. Jest to ustawienie domyślne dla plików binarnych.
  • lib32 : Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwany.
  • lib64 : Odpowiada 64-bitowemu ABI urządzenia, które jest obsługiwane.
  • prefer32 : Dopasowuje 32-bitowy ABI urządzenia, jeśli jest obsługiwany. Jeśli 32-bitowy ABI nie jest obsługiwany, odpowiada 64-bitowemu ABI.
  • both : Pasuje do obu ABI. Jest to ustawienie domyślne dla native_shared_libraries .

Właściwości java , libraries i prebuilts są niezależne od ABI.

Ten przykład dotyczy urządzenia obsługującego 32/64 i nie preferującego 32:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

podpisywanie vbmeta

Podpisz każdy APEX za pomocą różnych kluczy. Gdy wymagany jest nowy klucz, utwórz parę kluczy publiczny-prywatny i utwórz moduł apex_key . Użyj właściwości key , aby podpisać APEX za pomocą klucza. Klucz publiczny jest automatycznie dołączany do APEX pod nazwą avb_pubkey .

# create an rsa key 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żytego do podpisania APEX jest zapisany w APEX. W czasie wykonywania apexd weryfikuje APEX za pomocą klucza publicznego o tym samym identyfikatorze w urządzeniu.

Podpisywanie ZIP

Podpisuj pliki APEX w taki sam sposób, jak podpisujesz pakiety APK. Dwukrotnie podpisuj APEX; raz dla systemu plików mini (plik apex_payload.img ) i raz dla całego pliku.

Aby podpisać APEX na poziomie pliku, ustaw właściwość certificate na jeden z trzech sposobów:

  • Nieustawione: jeśli nie ustawiono żadnej wartości, APEX jest podpisany certyfikatem znajdującym się pod adresem PRODUCT_DEFAULT_DEV_CERTIFICATE . Jeśli żadna flaga nie jest ustawiona, ścieżka domyślnie 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)
}

Instalowanie APEX

Aby zainstalować APEX, użyj ADB.

adb install apex_file_name
adb reboot

Korzystanie z APEX

Po ponownym uruchomieniu APEX jest montowany w katalogu /apex/<apex_name>@<version> . Wiele wersji tego samego APEX-a może być montowanych w tym samym czasie. Wśród ścieżek montowania ta, która odpowiada najnowszej wersji, jest montowana przez wiązanie w /apex/<apex_name> .

Klienci mogą używać ścieżki montowanej przez powiązanie do odczytywania lub wykonywania plików z APEX.

APEX są zwykle używane w następujący sposób:

  1. OEM lub ODM wstępnie ładuje APEX w /system/apex , gdy urządzenie jest dostarczane.
  2. Pliki w APEX są dostępne poprzez ścieżkę /apex/<apex_name>/ .
  3. Gdy zaktualizowana wersja APEX jest zainstalowana w /data/apex , ścieżka wskazuje nowy APEX po ponownym uruchomieniu.

Aktualizacja usługi za pomocą APEX

Aby zaktualizować usługę za pomocą APEX:

  1. Oznacz usługę na partycji systemowej jako zdatną do aktualizacji. Dodaj opcję z możliwością updatable do definicji usługi.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. Utwórz nowy plik .rc dla zaktualizowanej usługi. Użyj opcji override , aby przedefiniować istniejącą usługę.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

Definicje usług można zdefiniować tylko w pliku .rc APEX. Wyzwalacze akcji nie są obsługiwane w APEX.

Jeżeli usługa oznaczona jako aktualizowalna rozpoczyna się przed aktywacją APEX-ów, start jest opóźniany do momentu zakończenia aktywacji APEX-ów.

Konfiguracja systemu do obsługi aktualizacji APEX

Ustaw następującą właściwość systemową na true aby obsługiwać aktualizacje plików APEX.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

Lub tylko

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

Spłaszczony APEX

W przypadku starszych urządzeń aktualizacja starego jądra do pełnej obsługi APEX jest czasami niemożliwa lub niewykonalna. Na przykład jądro mogło zostać zbudowane bez CONFIG_BLK_DEV_LOOP=Y , co jest kluczowe dla montowania obrazu systemu plików wewnątrz APEX.

Flattened APEX to specjalnie zbudowany APEX, który można aktywować na urządzeniach ze starszym jądrem. Pliki w spłaszczonym APEX są instalowane bezpośrednio w katalogu na wbudowanej partycji. Na przykład lib/libFoo.so w spłaszczonym APEX my.apex jest instalowany w /system/apex/my.apex/lib/libFoo.so .

Aktywacja spłaszczonego APEX nie obejmuje urządzenia pętli. Cały katalog /system/apex/my.apex jest bezpośrednio podłączony do /apex/name@ver .

Spłaszczonych APEX-ów nie można zaktualizować, pobierając zaktualizowane wersje APEX-ów z sieci, ponieważ pobranych APEX-ów nie można spłaszczyć. Spłaszczone APEX mogą być aktualizowane tylko przez zwykłą OTA.

Spłaszczony APEX jest konfiguracją domyślną. Oznacza to, że wszystkie APEX są domyślnie spłaszczone, chyba że jawnie skonfigurujesz urządzenie do budowania niespłaszczonych APEX-ów do obsługi aktualizacji APEX (jak wyjaśniono powyżej).

Mieszanie spłaszczonych i niespłaszczonych APEX w urządzeniu NIE jest obsługiwane. Punkty APEX w urządzeniu muszą być albo niespłaszczone, albo wszystkie spłaszczone. Jest to szczególnie ważne w przypadku wysyłania wstępnie podpisanych prekompilacji APEX dla projektów takich jak Mainline. APEX, które nie są wstępnie podpisane (czyli zbudowane ze źródła), również powinny być niespłaszczone i podpisane odpowiednimi kluczami. Urządzenie powinno dziedziczyć z updatable_apex.mk , jak wyjaśniono w Aktualizowanie usługi za pomocą APEX .

Skompresowane APEX

Android 12 i nowsze obsługują kompresję APEX w celu zmniejszenia wpływu pamięci masowej aktualizowanych pakietów APEX. Po zainstalowaniu aktualizacji APEX, mimo że jej preinstalowana wersja nie jest już używana, nadal zajmuje tyle samo miejsca. Ta zajęta przestrzeń pozostaje niedostępna.

Kompresja APEX minimalizuje ten wpływ na pamięć, używając mocno skompresowanego zestawu plików APEX na partycjach tylko do odczytu (takich jak partycja /system ). Android 12 i nowsze używają algorytmu kompresji zip DEFLATE.

Kompresja nie zapewnia optymalizacji:

  • Bootstrap APEX, które należy zamontować na bardzo wczesnym etapie sekwencji rozruchu.

  • Nieaktualizowalne APEX. Kompresja jest korzystna tylko wtedy, gdy na partycji /data jest zainstalowana zaktualizowana wersja APEX. Pełna lista APEX-ów, które można aktualizować, jest dostępna na stronie Komponenty systemu modułowego .

  • Dynamiczne biblioteki współdzielone APEX. Ponieważ apexd zawsze aktywuje obie wersje takich APEX-ów (preinstalowanych i zaktualizowanych), ich kompresja nie dodaje wartości.

Skompresowany format pliku APEX

To jest format skompresowanego pliku APEX.

Diagram shows the format of a compressed APEX file

Rysunek 2. Skompresowany format pliku APEX

Na najwyższym poziomie skompresowany plik APEX jest plikiem zip zawierającym oryginalny plik apex w nieskompresowanej formie z poziomem kompresji 9 oraz innymi plikami przechowywanymi w postaci nieskompresowanej.

Na plik APEX składają się cztery pliki:

  • original_apex : deflated z poziomem kompresji 9 To jest oryginalny, nieskompresowany plik APEX .
  • apex_manifest.pb : tylko przechowywane
  • AndroidManifest.xml : tylko przechowywane
  • apex_pubkey : tylko przechowywane

apex_manifest.pb , AndroidManifest.xml i apex_pubkey są kopiami odpowiadających im plików w pliku original_apex .

Budowanie skompresowanego APEX

Skompresowany APEX można zbudować za pomocą narzędzia apex_compression_tool.py znajdującego się w system/apex/tools .

W systemie kompilacji dostępnych jest kilka parametrów związanych z kompresją APEX.

W Android.bp to, czy plik APEX jest kompresowalny, jest kontrolowane przez właściwość compressible :

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Flaga produktu PRODUCT_COMPRESSED_APEX , czy obraz systemu utworzony ze źródła musi zawierać skompresowane pliki APEX.

Aby przeprowadzić lokalne eksperymenty, możesz wymusić w kompilacji kompresję APEX, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX= na true .

Skompresowane pliki APEX generowane przez system budowania mają rozszerzenie .capex . Rozszerzenie ułatwia rozróżnienie między skompresowanymi i nieskompresowanymi wersjami pliku APEX.

Obsługiwane algorytmy kompresji

Android 12 obsługuje tylko kompresję deflate-zip.

Aktywacja skompresowanego pliku APEX podczas rozruchu

Zanim skompresowany APEX będzie mógł zostać aktywowany, znajdujący się w nim plik original_apex jest dekompresowany do katalogu /data/apex/decompressed . Wynikowy zdekompresowany plik APEX jest na stałe powiązany z katalogiem /data/apex/active .

Rozważmy następujący przykład jako ilustrację procesu opisanego powyżej.

Rozważ /system/apex/com.android.foo.capex jako aktywowany skompresowany APEX z kodem wersji 37.

  1. Plik original_apex wewnątrz /system/apex/com.android.foo.capex jest zdekompresowany do /data/apex/decompressed/com.android.foo@37.apex .
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex jest wykonywany w celu sprawdzenia, czy ma poprawną etykietę SELinux.
  3. Kontrole weryfikacyjne są przeprowadzane na /data/apex/decompressed/com.android.foo@37.apex , aby zapewnić jego ważność: apexd sprawdza klucz publiczny zawarty w /data/apex/decompressed/com.android.foo@37.apex pod kątem sprawdź, czy jest równy temu zawartemu w /system/apex/com.android.foo.capex .
  4. Plik /data/apex/decompressed/com.android.foo@37.apex jest na stałe powiązany z katalogiem /data/apex/active/com.android.foo@37.apex .
  5. Zwykła logika aktywacji dla nieskompresowanych plików APEX jest wykonywana na /data/apex/active/com.android.foo@37.apex .

Interakcja z OTA

Skompresowane pliki APEX mają wpływ na dostarczanie i stosowanie OTA. Ponieważ aktualizacja OTA może zawierać skompresowany plik APEX z wersją wyższą niż ta, która jest aktywna na urządzeniu, pewna ilość wolnego miejsca musi zostać zarezerwowana przed ponownym uruchomieniem urządzenia w celu zastosowania aktualizacji OTA.

Aby obsługiwać system OTA, apexd udostępnia te dwa interfejsy API spinaczy:

  • calculateSizeForCompressedApex — oblicza rozmiar wymagany do dekompresji plików APEX w pakiecie OTA. Można to wykorzystać do sprawdzenia, czy urządzenie ma wystarczającą ilość miejsca przed pobraniem OTA.
  • reserveSpaceForCompressedApex - rezerwuje miejsce na dysku do wykorzystania w przyszłości przez apexd do dekompresji skompresowanych plików APEX wewnątrz pakietu OTA.

W przypadku aktualizacji A/B OTA, apexd próbuje dokonać dekompresji w tle w ramach procedury poinstalacyjnej OTA. Jeśli dekompresja się nie powiedzie, apexd wykonuje dekompresję podczas rozruchu, który stosuje aktualizację OTA.

Alternatywy brane pod uwagę przy opracowywaniu APEX

Oto kilka opcji, które firma AOSP rozważyła podczas projektowania formatu pliku APEX, i dlaczego zostały one uwzględnione lub wykluczone.

Regularne systemy zarządzania pakietami

Dystrybucje Linuksa mają systemy zarządzania pakietami, takie jak dpkg i rpm , które są potężne, dojrzałe i solidne. Jednak nie zostały one zaadoptowane do APEX, ponieważ nie mogą chronić pakietów po instalacji. Weryfikacja jest wykonywana tylko wtedy, gdy pakiety są instalowane. Atakujący mogą niepostrzeżenie złamać integralność zainstalowanych pakietów. Jest to regresja dla Androida, w której wszystkie komponenty systemu były przechowywane w systemach plików tylko do odczytu, których integralność jest chroniona przez dm-verity dla każdego I/O. Wszelkie ingerencje w składniki systemu muszą być zabronione lub być wykrywalne, aby urządzenie mogło odmówić uruchomienia w przypadku naruszenia bezpieczeństwa.

dm-crypt dla integralności

Pliki w kontenerze APEX pochodzą z wbudowanych partycji (na przykład partycji /system ), które są chronione przez dm-verity, gdzie wszelkie modyfikacje plików są zabronione nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom bezpieczeństwa plików, wszystkie pliki w APEX są przechowywane w obrazie systemu plików, który jest sparowany z drzewem mieszającym i deskryptorem vbmeta. Bez dm-verity APEX w partycji /data jest podatny na niezamierzone modyfikacje, które są wprowadzane po jego zweryfikowaniu i zainstalowaniu.

W rzeczywistości partycja /data jest również chroniona przez warstwy szyfrowania, takie jak dm-crypt. Chociaż zapewnia to pewien poziom ochrony przed manipulacją, jego głównym celem jest prywatność, a nie integralność. Gdy atakujący uzyska dostęp do partycji /data , nie może być dalszej ochrony, a to znowu jest regresją w porównaniu z każdym komponentem systemu znajdującym się na partycji /system . Drzewo skrótów w pliku APEX wraz z dm-verity zapewnia ten sam poziom ochrony treści.

Przekierowywanie ścieżek z /system do /apex

Pliki komponentów systemowych spakowane w APEX są dostępne za pośrednictwem nowych ścieżek, takich jak /apex/<name>/lib/libfoo.so . Gdy pliki były częścią partycji /system , były dostępne za pośrednictwem ścieżek takich jak /system/lib/libfoo.so . Klient pliku APEX (inne pliki APEX lub platforma) musi korzystać z nowych ścieżek. W wyniku zmiany ścieżki może być konieczna aktualizacja istniejącego kodu.

Chociaż jednym ze sposobów uniknięcia zmiany ścieżki jest nałożenie zawartości pliku APEX na partycję /system , zespół Androida postanowił nie nakładać plików na partycję /system ponieważ może to wpłynąć na wydajność, ponieważ liczba nakładanych plików ( być może nawet ułożone jeden po drugim) wzrosła.

Inną opcją było przejęcie funkcji dostępu do plików, takich jak open , stat i readlink , tak aby ścieżki zaczynające się od /system były przekierowywane do odpowiadających im ścieżek w /apex . Zespół Androida odrzucił tę opcję, ponieważ zmiana wszystkich funkcji akceptujących ścieżki jest niewykonalna. Na przykład niektóre aplikacje statycznie łączą Bionic, który implementuje funkcje. W takich przypadkach te aplikacje nie są przekierowywane.