Wdrażanie aktualizacji A/B

Producenci OEM i dostawcy SoC, którzy chcą zaimplementować aktualizacje systemu A/B, muszą upewnić się, że ich program ładujący implementuje warstwę HAL boot_control i przekazuje prawidłowe parametry do jądra.

Implementacja kontroli rozruchu HAL

Programy ładujące obsługujące A/B muszą implementować warstwę HAL boot_control w hardware/libhardware/include/hardware/boot_control.h . Implementacje można testować za pomocą narzędzia system/extras/bootctl i system/extras/tests/bootloader/ .

Musisz także zaimplementować maszynę stanów pokazaną poniżej:

Rysunek 1. Maszyna stanu bootloadera

Konfigurowanie jądra

Aby wdrożyć aktualizacje systemu A/B:

  1. Cherrypick następujące serie łat jądra (w razie potrzeby):
  2. Upewnij się, że argumenty wiersza poleceń jądra zawierają następujące dodatkowe argumenty:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... gdzie wartość <public-key-id> jest identyfikatorem klucza publicznego używanego do weryfikacji podpisu w tabeli Verity (szczegóły znajdziesz w dm-verity ) .
  3. Dodaj certyfikat .X509 zawierający klucz publiczny do pliku kluczy systemowych:
    1. Skopiuj certyfikat .X509 sformatowany w formacie .der do katalogu głównego katalogu kernel . Jeśli certyfikat .X509 jest sformatowany jako plik .pem , użyj następującego polecenia openssl , aby przekonwertować z .pem na .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. zImage , aby dołączyć certyfikat jako część systemowego pliku kluczy. Aby zweryfikować, sprawdź wpis procfs (wymaga KEYS_CONFIG_DEBUG_PROC_KEYS ):
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      Pomyślne włączenie certyfikatu .X509 wskazuje na obecność klucza publicznego w systemowym pęku kluczy (podświetlenie oznacza identyfikator klucza publicznego).
    3. Zamień spację na # i przekaż ją jako <public-key-id> w wierszu poleceń jądra. Na przykład przekaż Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f zamiast <public-key-id> .

Ustawianie zmiennych kompilacji

Programy ładujące obsługujące A/B muszą spełniać następujące kryteria zmiennych kompilacji:

Należy zdefiniować dla celu A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    i inne partycje aktualizowane przez update_engine (radio, bootloader itp.)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
Na przykład zobacz /device/google/marlin/+/android-7.1.0_r1/device-common.mk . Możesz opcjonalnie wykonać krok po instalacji (ale przed ponownym uruchomieniem) dex2oat opisany w Kompilacja .
Zdecydowanie zalecany do celów A/B
  • Zdefiniuj TARGET_NO_RECOVERY := true
  • Zdefiniuj BOARD_USES_RECOVERY_AS_BOOT := true
  • Nie określaj BOARD_RECOVERYIMAGE_PARTITION_SIZE
Nie można zdefiniować dla celu A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Opcjonalne dla kompilacji debugowania PRODUCT_PACKAGES_DEBUG += update_engine_client

Ustawianie partycji (slotów)

Urządzenia A/B nie potrzebują partycji odzyskiwania ani partycji pamięci podręcznej, ponieważ system Android nie używa już tych partycji. Partycja danych jest teraz używana dla pobranego pakietu OTA, a kod obrazu odzyskiwania znajduje się na partycji rozruchowej. Wszystkie partycje w wersji A/B powinny mieć następujące nazwy (sloty są zawsze nazywane a , b , itd.): boot_a , boot_b , system_a , system_b , vendor_a , vendor_b .

Pamięć podręczna

W przypadku aktualizacji innych niż A/B partycja pamięci podręcznej była używana do przechowywania pobranych pakietów OTA i tymczasowego przechowywania bloków podczas stosowania aktualizacji. Nigdy nie było dobrego sposobu na określenie rozmiaru partycji pamięci podręcznej: wielkość partycji musiała zależeć od tego, jakie aktualizacje chcesz zastosować. Najgorszym przypadkiem byłaby partycja pamięci podręcznej tak duża, jak obraz systemu. Dzięki aktualizacjom A/B nie ma potrzeby przechowywania bloków (ponieważ zawsze piszesz na partycji, która nie jest aktualnie używana), a przy strumieniowaniu A/B nie ma potrzeby pobierania całego pakietu OTA przed jego zastosowaniem.

Powrót do zdrowia

Dysk RAM odzyskiwania znajduje się teraz w pliku boot.img . Podczas odzyskiwania bootloader nie może umieścić opcji skip_initramfs w wierszu poleceń jądra.

W przypadku aktualizacji innych niż A/B partycja odzyskiwania zawiera kod używany do zastosowania aktualizacji. Aktualizacje A/B są stosowane przez update_engine uruchomioną w zwykłym obrazie systemu z rozruchem. Nadal istnieje tryb odzyskiwania używany do wdrażania przywracania danych fabrycznych i ładowania bocznego pakietów aktualizacji (stąd nazwa „odzyskiwanie”). Kod i dane do trybu odzyskiwania są przechowywane na zwykłej partycji rozruchowej w ramdysku; aby uruchomić system z obrazu systemu, bootloader mówi jądru, aby pominęło ramdysk (w przeciwnym razie urządzenie uruchomi się w trybie odzyskiwania. Tryb odzyskiwania jest mały (i większość z nich była już na partycji rozruchowej), więc partycja rozruchowa nie zwiększa się W rozmiarze.

Fstab

Argument slotselect musi znajdować się w wierszu dla partycji A/B-ed. Na przykład:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

Żadna partycja nie powinna być nazwana vendor . Zamiast tego partycja vendor_a lub vendor_b zostanie wybrana i zamontowana w punkcie montowania /vendor .

Argumenty gniazda jądra

Bieżący sufiks gniazda powinien być przekazywany przez określony węzeł drzewa urządzeń (DT) ( /firmware/android/slot_suffix ) lub przez wiersz polecenia jądra androidboot.slot_suffix lub argument bootconfig.

Domyślnie fastboot miga bieżące gniazdo na urządzeniu A/B. Jeśli pakiet aktualizacji zawiera również obrazy dla innego, nieaktualnego gniazda, fastboot również miga te obrazy. Dostępne opcje obejmują:

  • --slot SLOT . Zastąp domyślne zachowanie i poproś fastboot o flashowanie gniazda, które jest przekazywane jako argument.
  • --set-active [ SLOT ] . Ustaw gniazdo jako aktywne. Jeśli nie podano argumentu opcjonalnego, bieżący slot jest ustawiany jako aktywny.
  • fastboot --help . Uzyskaj szczegółowe informacje na temat poleceń.

Jeśli bootloader implementuje fastboot, powinien obsługiwać polecenie set_active <slot> , które ustawia bieżące aktywne gniazdo na dane gniazdo (musi to również wyczyścić flagę unbootable dla tego gniazda i zresetować licznik ponownych prób do wartości domyślnych). Bootloader powinien również obsługiwać następujące zmienne:

  • has-slot:<partition-base-name-without-suffix> . Zwraca „tak”, jeśli dana partycja obsługuje sloty, „nie” w przeciwnym razie.
  • current-slot . Zwraca sufiks slotu, który zostanie uruchomiony od następnego.
  • slot-count . Zwraca liczbę całkowitą reprezentującą liczbę dostępnych gniazd. Obecnie obsługiwane są dwa gniazda, więc ta wartość wynosi 2 .
  • slot-successful:<slot-suffix> . Zwraca „tak”, jeśli dane gniazdo zostało oznaczone jako pomyślnie uruchomione, „nie” w przeciwnym razie.
  • slot-unbootable:<slot-suffix> . Zwraca „tak”, jeśli dany slot jest oznaczony jako niebootowalny, „nie” w przeciwnym razie.
  • slot-retry-count . Liczba pozostałych prób uruchomienia danego gniazda.

Aby wyświetlić wszystkie zmienne, uruchom fastboot getvar all .

Generowanie pakietów OTA

Narzędzia pakietu OTA wykonują te same polecenia, co polecenia dla urządzeń innych niż A/B. Plik target_files.zip musi zostać wygenerowany przez zdefiniowanie zmiennych kompilacji dla celu A/B. Narzędzia pakietów OTA automatycznie identyfikują i generują pakiety w formacie dla aktualizatora A/B.

Przykłady:

  • Aby wygenerować pełną OTA:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Aby wygenerować przyrostowy OTA:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

Konfiguracja partycji

update_engine może aktualizować dowolną parę partycji A/B zdefiniowanych na tym samym dysku. Para partycji ma wspólny prefiks (taki jak system lub boot ) i sufiks na gniazdo (taki jak _a ). Lista partycji, dla których generator ładunku definiuje aktualizację, jest konfigurowana przez zmienną make AB_OTA_PARTITIONS .

Na przykład, jeśli dołączona jest para partycji bootloader_a i booloader_b ( _a i _b są przyrostkami gniazd), możesz zaktualizować te partycje, określając następujące elementy w konfiguracji produktu lub płyty:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Wszystkie partycje aktualizowane przez update_engine nie mogą być modyfikowane przez resztę systemu. Podczas aktualizacji przyrostowych lub różnicowych dane binarne z bieżącego gniazda są używane do generowania danych w nowym gnieździe. Wszelkie modyfikacje mogą spowodować, że weryfikacja danych nowego gniazda nie powiodła się podczas procesu aktualizacji, a zatem nie powiodła się aktualizacja.

Konfiguracja poinstalacyjna

Krok po instalacji można skonfigurować inaczej dla każdej zaktualizowanej partycji, używając zestawu par klucz-wartość. Aby uruchomić program znajdujący się w /system/usr/bin/postinst w nowym obrazie, określ ścieżkę względem katalogu głównego systemu plików na partycji systemowej.

Na przykład usr/bin/postinst to system/usr/bin/postinst (jeśli nie używasz dysku RAM). Dodatkowo określ typ systemu plików, który ma zostać przekazany do wywołania systemowego mount(2) . Dodaj następujące elementy do plików .mk produktu lub urządzenia (jeśli dotyczy):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

Kompilacja

Ze względów bezpieczeństwa system_server nie może używać kompilacji just-in-time (JIT) . Oznacza to, że musisz skompilować pliki odex z wyprzedzeniem dla serwera system_server i jego zależności co najmniej; wszystko inne jest opcjonalne.

Aby kompilować aplikacje w tle, należy dodać następujące elementy do konfiguracji urządzenia produktu (w pliku device.mk produktu):

  1. Dołącz natywne komponenty do kompilacji, aby upewnić się, że skrypt kompilacji i pliki binarne są skompilowane i uwzględnione w obrazie systemu.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Połącz skrypt kompilacji z update_engine tak, aby działał jako krok po instalacji.
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

Aby uzyskać pomoc dotyczącą instalowania wstępnie wybranych plików na nieużywanej drugiej partycji systemowej, zobacz Pierwsza instalacja rozruchowa plików DEX_PREOPT .