Format pliku APEX

Format kontenera Android Pony EXpress (APEX) został wprowadzony w systemie Android 10 i jest używany podczas instalacji modułów systemowych niższego poziomu. Ten format ułatwia aktualizację komponentów systemu, które nie mieszczą się w standardowym modelu aplikacji 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ż system Android obsługuje aktualizacje modułów pasujących 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 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ć jedynie z poziomu 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ą 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.

Więcej informacji o tym, dlaczego wybrano ten projekt dla APEX, można znaleźć w artykule Alternatywy brane pod uwagę przy opracowywaniu APEX .

formacie APEX

Jest to format pliku APEX.

Format pliku APEX

Rysunek 1. Format pliku APEX

Na najwyższym poziomie plik APEX jest plikiem zip, w którym przechowywane są pliki nieskompresowane i rozmieszczone 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. To jest bufor protokołu ApexManifest w formacie JSON.

Plik AndroidManifest.xml umożliwia plikowi APEX korzystanie z narzędzi i infrastruktury związanej z plikiem APK, takiej jak ADB, PackageManager i aplikacje instalujące pakiety (takie jak Sklep Play). 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. Informacje te są ogólnie dostępne 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 o kierowaniu, z których mogą korzystać 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 z pętlą zwrotną. 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 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 gwarantuje, że pobrany plik APEX zostanie podpisany tą samą jednostką, która podpisuje ten sam plik APEX na wbudowanych partycjach.

Wytyczne dotyczące nazewnictwa APEX

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

  • com.android.*
    • Zarezerwowane dla APEXó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ó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. Proces ten jest uruchamiany i gotowy na początku 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 wykorzystuje klasę PackageManager i jest następująca.

  1. Plik APEX jest pobierany za pośrednictwem aplikacji instalatora pakietów, ADB lub innego źródła.
  2. Menedżer pakietów rozpoczyna procedurę instalacji. Po rozpoznaniu, że plik jest plikiem APEX, menedżer pakietów przekazuje kontrolę menedżerowi 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ć fakt, że plik APEX zostanie aktywowany przy następnym uruchomieniu.
  5. Osoba żądająca instalacji odbiera transmisję po pomyślnej weryfikacji pakietu.
  6. Aby kontynuować instalację, należy ponownie uruchomić system.
  7. Przy następnym uruchomieniu uruchamia się menedżer APEX, 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 z pętlą zwrotną.
    4. Montuje urządzenie blokowe mapowania urządzeń na unikalnej ścieżce (na przykład /apex/ name @ ver ).

Po zamontowaniu wszystkich plików APEX znajdujących się w wewnętrznej bazie danych menedżer APEX zapewnia usługę segregatora dla innych komponentów systemu w celu uzyskania informacji o zainstalowanych plikach APEX. Na przykład inne komponenty systemu mogą wysyłać zapytania o listę plików APEX zainstalowanych w urządzeniu lub o dokładną ścieżkę, w której zamontowany jest określony plik 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 instalująca pakiety, 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 na potrzeby precyzyjnego kierowania. Informacje te umożliwiają dostarczanie plików APEX za pośrednictwem istniejących kanałów, takich jak aplikacje instalujące pakiety i ADB.

Obsługiwane typy plików

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

  • Natywne udostępnione biblioteki
  • Natywne pliki wykonywalne
  • pliki JAR
  • Pliki danych
  • Pliki konfiguracyjne

Nie oznacza to, że APEX może aktualizować wszystkie typy plików. To, czy typ pliku można zaktualizować, 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 (w szczególności deskryptor vbmeta dołączony do apex_payload.img ) jest podpisywany kluczem. Następnie cały APEX jest podpisywany 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żytemu do podpisania deskryptora vbmeta. Menedżer APEX używa klucza publicznego do weryfikacji plików APEX, które mają zostać zainstalowane. Każdy plik APEX musi być podpisany różnymi kluczami i jest wymuszany 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ż na poziomie dm-verity, więc pliki APEX są montowane bezpośrednio nad urządzeniem zwrotnym.

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

Wymagania jądra

Aby obsługiwać główne moduły APEX na urządzeniu z Androidem, wymagane są następujące funkcje jądra Linuksa: 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

Moduły główne 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 znajdują się 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 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ę połączenie w dół z gałęzi android-4.4 . Poniżej znajduje się lista wymaganych poszczególnych poprawek dla wersji jądra 4.4.

  • GÓRA: pętla: dodaj ioctl do zmiany rozmiaru bloku logicznego ( 4.4 )
  • BACKPORT: blok/pętla: ustaw hw_sectors ( 4.4 )
  • GÓRA: pętla: Dodaj LOOP_SET_BLOCK_SIZE w zgodnym 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: Poprawne propagowanie ponownego montażu ( 4.4 )
  • Przywróć „ANDROID: dm verity: dodaj minimalny rozmiar pobierania wstępnego” ( 4.4 )
  • GÓRA: 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 poprawki dla wersji jądra 4.9/4.14/4.19, wykonaj połączenie 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. Pozycje oznaczone gwiazdką (*) oznaczają 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 poleceń jądra

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

  • NIE można ustawić loop.max_loop
  • loop.max_part musi wynosić <= 8

Zbuduj APEX

W tej sekcji opisano, jak zbudować APEX przy użyciu systemu kompilacji Androida. 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 APEXie
Wspólne biblioteki /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 udostępnionych bibliotek lub plików wykonywalnych. Na przykład, jeśli libFoo zależy od libBar , te dwie biblioteki są uwzględniane, gdy we właściwości native_shared_libs wymieniona jest tylko libFoo .

Obsługuj wiele ABI

Zainstaluj właściwość native_shared_libs dla podstawowych i dodatkowych interfejsów binarnych aplikacji (ABI) urządzenia. Jeśli APEX jest przeznaczony dla urządzeń z pojedynczym ABI (to znaczy tylko 32-bitowym lub tylko 64-bitowym), instalowane są tylko biblioteki z odpowiednim ABI.

Zainstaluj właściwość binaries tylko dla podstawowego interfejsu 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 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 : odpowiada podstawowemu interfejsowi ABI urządzenia. Jest to ustawienie domyślne dla plików binarnych.
  • lib32 : Pasuje do 32-bitowego ABI urządzenia, jeśli jest obsługiwane.
  • lib64 : Pasuje do 64-bitowego ABI urządzenia, które obsługuje.
  • prefer32 : Pasuje do 32-bitowego ABI urządzenia, jeśli jest obsługiwane. 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, 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 pliku 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 przy użyciu klucza publicznego o tym samym identyfikatorze w urządzeniu.

Podpisanie APEX

Podpisuj pliki APEX w taki sam sposób, w jaki podpisujesz pliki APK. Podpisz dwukrotnie APEXy; 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 na jeden z trzech sposobów:

  • Nie ustawiono: Jeśli nie ustawiono żadnej wartości, plik APEX jest podpisany certyfikatem znajdującym się pod PRODUCT_DEFAULT_DEV_CERTIFICATE . Jeśli nie ustawiono żadnej flagi, 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 supportsRebootlessUpdate ma wartość true i aktualnie zainstalowany plik APEX jest nieużywany (na przykład wszystkie zawarte w nim usługi zostały zatrzymane), nowy plik APEX można zainstalować bez ponownego uruchamiania za pomocą flagi --force-non-staged .

adb install --force-non-staged apex_file_name

Użyj APEX-a

Po ponownym uruchomieniu APEX jest montowany w katalogu /apex/<apex_name>@<version> . Jednocześnie można zamontować wiele wersji tego samego APEX-a. Wśród ścieżek montowania ta odpowiadająca najnowszej wersji jest montowana przez powiązanie w /apex/<apex_name> .

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

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

  1. Producent OEM lub ODM ładuje wstępnie plik APEX w katalogu /system/apex po dostarczeniu urządzenia.
  2. Dostęp do plików w APEX można uzyskać poprzez ścieżkę /apex/<apex_name>/ .
  3. Gdy zaktualizowana wersja APEX jest zainstalowana w /data/apex , ścieżka wskazuje na 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 nadającą się do aktualizacji. 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 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 pliku APEX. Wyzwalacze akcji nie są obsługiwane w plikach APEX.

Jeśli usługa oznaczona jako możliwa do aktualizacji zostanie uruchomiona przed aktywacją APEXów, start zostanie opóźniony do momentu zakończenia aktywacji APEXów.

Skonfiguruj system 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 wierzchołek

W przypadku starszych urządzeń aktualizacja starego jądra, aby w pełni obsługiwała 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 zamontowania 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 APEXie są instalowane bezpośrednio w katalogu pod wbudowaną partycją. Na przykład lib/libFoo.so w spłaszczonym pliku APEX my.apex jest instalowany w /system/apex/my.apex/lib/libFoo.so .

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

Spłaszczonych APEXów nie można zaktualizować poprzez pobranie zaktualizowanych wersji APEXów z sieci, ponieważ pobranych APEXów nie można spłaszczyć. Spłaszczone APEXy można aktualizować tylko za pośrednictwem zwykłego OTA.

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

Mieszanie spłaszczonych i niespłaszczonych APEXów w urządzeniu NIE jest obsługiwane. Wierzchołki 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, gotowych wersji APEX dla 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 sekcji Aktualizowanie usługi za pomocą pliku APEX .

Skompresowane APEXy

Android 12 i nowsze wersje obsługują kompresję APEX, aby zmniejszyć wpływ aktualizowalnych pakietów APEX na pamięć. Po zainstalowaniu aktualizacji APEX, mimo że jego preinstalowana wersja nie jest już używana, nadal zajmuje tę samą ilość miejsca. Zajęte miejsce pozostaje niedostępne.

Kompresja APEX minimalizuje wpływ na pamięć, 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 należy zamontować na bardzo wczesnym etapie sekwencji rozruchowej.

  • Nieaktualizowalne APEXy. Kompresja jest korzystna tylko wtedy, gdy na partycji /data jest zainstalowana zaktualizowana wersja APEX. Pełna lista aktualizowalnych APEXów jest dostępna na stronie Modułowe komponenty systemu .

  • Dynamiczne biblioteki współdzielone APEX. Ponieważ apexd zawsze aktywuje obie wersje takich APEXów (preinstalowane i uaktualnione), 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 wierzchołka w postaci deflowanej z poziomem kompresji 9 i innymi plikami przechowywanymi w stanie nieskompresowanym.

Cztery pliki składają się na plik APEX:

  • original_apex : deflowany przy poziomie kompresji 9. To jest oryginalny, nieskompresowany plik APEX .
  • apex_manifest.pb : tylko przechowywane
  • AndroidManifest.xml : tylko przechowywane
  • 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 plik 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 skompresowany, 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 określa, czy obraz systemu zbudowany ze źródła musi zawierać skompresowane pliki APEX.

W przypadku eksperymentów lokalnych możesz wymusić kompresję APEXów przez kompilację, ustawiając OVERRIDE_PRODUCT_COMPRESSED_APEX= na true .

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

Obsługiwane algorytmy kompresji

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

Aktywuj skompresowany plik APEX podczas uruchamiania

Zanim będzie można aktywować skompresowany plik APEX, znajdujący się w nim plik original_apex jest dekompresowany do katalogu /data/apex/decompressed . Powstały 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 plik APEX z kodem wersji 37.

  1. Plik original_apex znajdujący się w /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ą 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 , aby 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 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 aplikacje 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 należy zarezerwować pewną ilość wolnego miejsca, aby można było zastosować aktualizację OTA.

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

  • calculateSizeForCompressedApex - oblicza rozmiar wymagany do dekompresji plików APEX w pakiecie OTA. Można to wykorzystać do sprawdzenia, czy na urządzeniu jest wystarczająco dużo miejsca, zanim zostanie pobrana 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 w ramach procedury OTA po instalacji. Jeśli dekompresja się nie powiedzie, apexd wykona dekompresję podczas rozruchu, który zastosuje aktualizację OTA.

Alternatywy brane pod uwagę przy opracowywaniu APEX

Oto kilka opcji, które firma AOSP wzięła pod uwagę podczas projektowania formatu pliku APEX oraz 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ą wydajne, dojrzałe i niezawodne. Nie zostały one jednak zaadaptowane dla APEX, ponieważ nie chronią pakietów po instalacji. Weryfikacja jest wykonywana tylko podczas instalacji pakietów. Atakujący mogą niezauważenie złamać integralność zainstalowanych pakietów. Jest to regresja dla Androida, gdzie 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 jakakolwiek modyfikacja plików jest zabroniona nawet po zamontowaniu partycji. Aby zapewnić ten sam poziom bezpieczeństwa plików, wszystkie pliki w pliku APEX są przechowywane w obrazie systemu plików sparowanym z drzewem skrótów i deskryptorem vbmeta. Bez dm-verity plik APEX w partycji /data jest podatny na niezamierzone modyfikacje wprowadzone 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ść. Kiedy osoba atakująca uzyska dostęp do partycji /data , nie będzie już żadnej dalszej ochrony, a to znowu oznacza regres w porównaniu do sytuacji, w której każdy komponent systemu znajduje 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 systemowych spakowane w APEX są dostępne poprzez nowe ścieżki, takie jak /apex/<name>/lib/libfoo.so . Gdy pliki znajdowały się na partycji /system , były dostępne poprzez ścieżki takie 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 zdecydował się nie nakładać plików na partycję /system , ponieważ mogłoby to mieć wpływ na wydajność ze względu na liczbę nakładanych plików ( ewentualnie nawet ułożone 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 rozpoczynające się od /system były przekierowywane do odpowiadających im ścieżek w /apex . Zespół Androida odrzucił tę opcję, ponieważ nie można zmienić wszystkich funkcji akceptujących ścieżki. Na przykład niektóre aplikacje statycznie łączą Bionic, który implementuje te funkcje. W takich przypadkach te aplikacje nie są przekierowywane.