Wdrażanie aktualizacji typu A/B

Producenci OEM i producenci układów SoC, którzy chcą wdrażać aktualizacje systemu A/B, muszą zadbać o to, aby ich bootloader implementował interfejs HAL boot_control i przekazywał prawidłowe parametry do jądra.

Zaimplementuj interfejs HAL sterowania rozruchem

Bootloadery obsługujące A/B muszą implementować interfejs boot_control HAL w ramach hardware/libhardware/include/hardware/boot_control.h. Implementacje możesz testować za pomocą narzędzia system/extras/bootctlsystem/extras/tests/bootloader/.

Musisz też zaimplementować maszynę stanów przedstawioną poniżej:

Rysunek 1. Maszyna stanów programu rozruchowego

Konfigurowanie jądra

Aby wdrożyć aktualizacje systemu A/B:

  1. Wybierz z nich te serie poprawek do jądra (w razie potrzeby):
  2. Upewnij się, że argumenty wiersza poleceń jądra zawierają te 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> to identyfikator klucza publicznego użytego do zweryfikowania podpisu tabeli verity (szczegóły znajdziesz w artykule dm-verity).
  3. Dodaj certyfikat .X509 zawierający klucz publiczny do pęku kluczy systemowego:
    1. Skopiuj certyfikat X509 sformatowany w formacie .der do katalogu kernel. Jeśli certyfikat X509 ma format pliku .pem, użyj polecenia openssl, aby przekonwertować format .pem na .der:
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Utwórz zImage, aby uwzględnić certyfikat w ramach pęku kluczy systemu. Aby to sprawdzić,sprawdź wpis procfs (wymaga włączenia opcji 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
      Udane uwzględnienie certyfikatu X .509 wskazuje obecność klucza publicznego w pęku kluczy systemu (zaznaczone na żółto pole to identyfikator klucza publicznego).
    3. Zastąp spację znakiem # i przekaż jako <public-key-id> w wierszu poleceń jądra. Na przykład zamiast <public-key-id> możesz podać Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f.

Ustawianie zmiennych kompilacji

Bootloadery obsługujące A/B muszą spełniać te kryteria zmiennych kompilacji:

Musisz określić docelową grupę w ramach testu A/B.
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    oraz inne partycje zaktualizowane przez update_engine (radio, bootloader, itp.)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
Przykład: /device/google/marlin/+/android-7.1.0_r1/device-common.mk Opcjonalnie możesz wykonać po instalacji (ale przed ponownym uruchomieniem) krok dex2oat opisany w sekcji Kompilowanie.
Zdecydowanie zalecane w przypadku testu A/B
  • Zdefiniuj TARGET_NO_RECOVERY := true
  • Zdefiniuj BOARD_USES_RECOVERY_AS_BOOT := true
  • Nie definiuj BOARD_RECOVERYIMAGE_PARTITION_SIZE
Nie można zdefiniować celu testu A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Opcjonalne w przypadku wersji debugowania PRODUCT_PACKAGES_DEBUG += update_engine_client

Ustaw partycje (gniazda)

Urządzenia A/B nie potrzebują partycji odzyskiwania ani partycji pamięci podręcznej, ponieważ Android nie używa już tych partycji. Partycja danych jest teraz używana do pobranego pakietu OTA, a kod obrazu odzyskiwania jest na partycji rozruchowej. Wszystkie partycje, które są testowane metodą A/B, powinny mieć nazwy podane poniżej (sloty mają zawsze nazwy 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 oraz do tymczasowego przechowywania bloków podczas stosowania aktualizacji. Nigdy nie było dobrego sposobu na określenie rozmiaru partycji pamięci podręcznej: jej wielkość zależała od tego, jakie aktualizacje chcesz wprowadzić. Najgorszym przypadkiem jest partycja pamięci podręcznej o rozmiarze równym obrazowi systemu. W przypadku aktualizacji A/B nie trzeba ukrywać bloków (ponieważ zawsze zapisujesz na partycji, która nie jest obecnie używana), a w przypadku strumieniowego przesyłania aktualizacji A/B nie trzeba pobierać całego pakietu OTA przed jego zastosowaniem.

Odzyskiwanie

Dysk RAM do przywracania jest teraz zawarty w pliku boot.img. Podczas przechodzenia do trybu 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 stosowania aktualizacji. Aktualizacje A/B są stosowane przez update_engine działający w ramach zwykłego obrazu systemu. Nadal istnieje tryb odzyskiwania, który służy do przywracania danych do ustawień fabrycznych i instalowania pakietów aktualizacji (stąd nazwa „recovery”). Kod i dane trybu odzyskiwania są przechowywane na zwykłym dysku rozruchowym w ramdisku. Aby uruchomić obraz systemu, program rozruchowy informuje jądro, aby pominęło ramdisk (w przeciwnym razie urządzenie uruchomi się w trybie odzyskiwania). Tryb odzyskiwania zajmuje niewiele miejsca (a duża część była już na partycji rozruchowej), więc rozmiar partycji rozruchowej nie zwiększy się.

Fstab

Argument slotselect musi znajdować się na linii dotyczącej partycji A/B. Przykład:

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

Żadna partycja nie może mieć nazwy vendor. Zamiast tego partycja vendor_a lub vendor_b zostanie wybrana i zamontowana w punkcie montażowym /vendor.

Argumenty przedziału jądra

Obecny przyrostek slotu powinien być przekazywany przez konkretny węzeł drzewa urządzenia (DT) (/firmware/android/slot_suffix) lub przez wiersz poleceń jądra (androidboot.slot_suffix) lub argument bootconfig.

Domyślnie fastboot flashuje bieżący slot na urządzeniu A/B. Jeśli pakiet aktualizacji zawiera również obrazy dla innego, nieaktualnego slotu, fastboot flashuje również te obrazy. Dostępne opcje:

  • --slot SLOT. Zastąpić domyślne zachowanie i poprosić fastboot o przeflashowanie slotu przekazanego jako argument.
  • --set-active [SLOT]. Ustaw slot jako aktywny. Jeśli nie zostanie podany żaden argument opcjonalny, bieżący slot zostanie ustawiony jako aktywny.
  • fastboot --help. Uzyskaj informacje o poleceniach.

Jeśli bootloader obsługuje fastboot, powinien obsługiwać polecenie set_active <slot>, które ustawia bieżący aktywny slot na dany slot (musi też usunąć flagę unbootable dla tego slotu i zresetować liczbę prób do wartości domyślnych). Program rozruchowy powinien też obsługiwać te zmienne:

  • has-slot:<partition-base-name-without-suffix>. Zwraca „yes” (tak), jeśli dana partycja obsługuje sloty, w przeciwnym razie zwraca „no” (nie).
  • current-slot. Zwraca sufiks przedziału, z którego zostanie uruchomiony następny system.
  • slot-count. Zwraca liczbę całkowitą odpowiadającą liczbie dostępnych miejsc. Obecnie obsługiwane są 2 miejsca, więc ta wartość wynosi 2.
  • slot-successful:<slot-suffix>. Zwraca wartość „yes” (tak), jeśli dany slot został oznaczony jako uruchomiony, w przeciwnym razie zwraca „no” (nie).
  • slot-unbootable:<slot-suffix>. Zwraca „yes” (tak), jeśli dany slot jest oznaczony jako nieuruchamialny, w przeciwnym razie zwraca „no” (nie).
  • slot-retry-count:<slot-suffix>. Liczba pozostałych prób uruchomienia danego gniazda.

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

Generowanie pakietów OTA

Narzędzia do pakietów OTA działają na tych samych zasadach co polecenia dotyczące innych urządzeń niż A/B. Plik target_files.zip musi być wygenerowany przez definiowanie zmiennych kompilacji dla celu A/B. Narzędzia do pakietów OTA automatycznie identyfikują i generują pakiety w formacie dla aktualizatora A/B.

Przykłady:

  • Aby wygenerować pełny OTA:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Aby wygenerować przyrostową 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
    

Konfigurowanie partycji

update_engine może zaktualizować dowolną parę partycji A/B zdefiniowaną na tym samym dysku. Para partycji ma wspólny prefiks (np. system lub boot) oraz sufiks na potrzeby poszczególnych slotów (np. _a). Lista partycji, dla których generator payload definiuje aktualizację, jest konfigurowana za pomocą zmiennej AB_OTA_PARTITIONS make.

Jeśli na przykład uwzględnisz parę partycji bootloader_abooloader_b (_a_b to sufiksy slotu), możesz zaktualizować te partycje, określając w konfiguracji produktu lub płytki:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Wszystkie partycje aktualizowane przez update_engine nie mogą być modyfikowane przez pozostałą część systemu. Podczas aktualizacji cząstkowych lub delta dane binarne z bieżącego slotu są wykorzystywane do generowania danych w nowym slocie. Wszelkie zmiany mogą spowodować, że dane nowego slotu nie przejdą weryfikacji podczas procesu aktualizacji, przez co aktualizacja się nie powiedzie.

Konfigurowanie po instalacji

Możesz skonfigurować krok po instalacji inaczej w przypadku każdej zaktualizowanej partycji, używając zestawu par klucz-wartość. Aby uruchomić program znajdujący się w pliku /system/usr/bin/postinst na nowej partycji, określ ścieżkę względną do 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 systemu mount(2). Dodaj do plików .mk (jeśli są dostępne) te informacje o produkcie lub urządzeniu:

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

Kompilowanie aplikacji

Aplikacje mogą być kompilowane w tle przed ponownym uruchomieniem z nową wersją obrazu systemu. Aby kompilować aplikacje w tle, dodaj do konfiguracji urządzenia produktu (w pliku product.mk) następujące informacje:

  1. Uwzględnij w kompilacji komponenty natywne, aby mieć pewność, że skrypt kompilacji i pliki binarne zostaną skompilowane i zawarowane w obrazie systemu.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Połącz skrypt kompilacji z parametrem update_engine, aby uruchamiał się 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 dowiedzieć się, jak zainstalować w nieużywanym drugim partycji systemu pliki z opcją wczesnszego optymalizowania, zapoznaj się z artykułem Pierwsze uruchomienie z zainstalowanymi plikami DEX_PREOPT.