Format pliku APEX

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.

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 ApexManifestbufor 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:

  1. Plik APEX jest pobierany za pomocą aplikacji do instalowania 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, która wysłała prośbę o instalację, otrzymuje powiadomienie po pomyślnej weryfikacji pakietu.
  6. Aby kontynuować instalację, musisz ponownie uruchomić system.
  7. Podczas następnego uruchomienia menedżer APEX wczyta wewnętrzną bazę danych i wykonuje te czynności dla każdego wymienionego pliku APEX:

    1. Weryfikuje plik APEX.
    2. Tworzy urządzenie pętli z pliku APEX.
    3. Tworzy urządzenie blokujące mapowanie urządzenia na urządzeniu pętli zwrotnej.
    4. 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, minSdkVersionmaxSdkVersion 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ć ustawiony
  • loop.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/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 pola native_shared_libraries.

Właściwości java, librariesprebuilts 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 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 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 na build/target/product/security/testkey.
  • <name>: plik APEX jest podpisany certyfikatem <name> znajdującym się w tym samym katalogu co plik PRODUCT_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:

  1. Producent OEM lub ODM wstępnie wczytuje APEX w ramach /system/apex podczas wysyłki urządzenia.
  2. Do plików w APEX można uzyskać dostęp przez ścieżkę /apex/<apex_name>/.
  3. 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:

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

Diagram pokazujący 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 przechowywane
  • AndroidManifest.xml: tylko przechowywane
  • apex_pubkey: tylko przechowywane

Pliki apex_manifest.pb, AndroidManifest.xmlapex_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.

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.

  1. 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.
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex jest wykonywane w celu sprawdzenia, czy ma ona prawidłową etykietę SELinux.
  3. 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.
  4. Plik /data/apex/decompressed/com.android.foo@37.apex jest twardo połączony z katalogiem /data/apex/active/com.android.foo@37.apex.
  5. 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 przez apexd 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, statreadlink, 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.