Wdrażanie aktualizacji testów A/B

Producenci OEM i dostawcy SoC, którzy chcą wdrożyć aktualizacje systemu A/B, muszą zadbać o to, aby program rozruchowy implementował HAL boot_control i przekazywał do jądra odpowiednie parametry.

Implementowanie HAL sterowania rozruchem

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

Musisz też wdrożyć automat stanowy pokazany poniżej:

Rysunek 1. Automat stanów programu rozruchowego

Konfigurowanie jądra

Aby wdrożyć aktualizacje systemu A/B:

  1. Wybierz te serie poprawek jądra (w razie potrzeby):
  2. Sprawdź, czy 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 weryfikacji (szczegółowe informacje znajdziesz w artykule dm-verity).
  3. Dodaj certyfikat X .509 zawierający klucz publiczny do pęku kluczy systemu:
    1. Skopiuj certyfikat .X509 sformatowany w formacie .der do katalogu głównego kernel. Jeśli certyfikat X .509 jest sformatowany jako plik .pem, użyj tego polecenia openssl, aby przekonwertować go z formatu .pem na format .der:
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Skonstruuj zImage, aby uwzględnić certyfikat w pęku kluczy systemu. Aby to sprawdzić,poszukaj wpisu procfs (wymaga włączenia 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 dodanie certyfikatu X .509 oznacza obecność klucza publicznego w pęku kluczy systemu (podświetlenie oznacza identyfikator klucza publicznego).
    3. Zastąp spację znakiem # 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 rozruchowe obsługujące aktualizacje A/B muszą spełniać te kryteria zmiennych kompilacji:

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

Ustawianie partycji (miejsc)

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 w przypadku pobranego pakietu OTA, a kod obrazu przywracania znajduje się na partycji rozruchowej. Wszystkie partycje, które są testowane A/B, powinny mieć nazwy w tym formacie (sloty zawsze mają nazwy a, b itp.): 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 służyła do przechowywania pobranych pakietów OTA i tymczasowego przechowywania bloków podczas stosowania aktualizacji. Nie było dobrego sposobu na określenie rozmiaru partycji pamięci podręcznej: jej wielkość zależała od tego, jakie aktualizacje chcesz zastosować. Najgorszym przypadkiem byłaby partycja pamięci podręcznej tak duża jak obraz systemu. W przypadku aktualizacji A/B nie trzeba zapisywać bloków (ponieważ zawsze zapisujesz dane w partycji, która nie jest obecnie używana), a w przypadku aktualizacji A/B strumieniowych nie trzeba pobierać całego pakietu OTA przed jego zastosowaniem.

Odzyskiwanie

Dysk RAM odzyskiwania jest teraz zawarty w pliku boot.img. Podczas przechodzenia do trybu odzyskiwania program rozruchowy 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 regularnym obrazie systemu. Nadal istnieje tryb odzyskiwania, który służy do przywracania danych fabrycznych i instalowania pakietów aktualizacji z zewnątrz (stąd nazwa „odzyskiwanie”). Kod i dane trybu odzyskiwania są przechowywane w zwykłej partycji rozruchowej w ramdysku. Aby uruchomić obraz systemu, program rozruchowy informuje jądro, aby pominęło ramdysk (w przeciwnym razie urządzenie uruchomi się w trybie odzyskiwania). Tryb odzyskiwania jest niewielki (a większość jego zawartości była już na partycji rozruchowej), więc rozmiar partycji rozruchowej nie zwiększa się.

Fstab

Argument slotselect musi znajdować się w wierszu partycji poddanych testom A/B. Na przykład:

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

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

Argumenty przedziału jądra

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

Domyślnie fastboot flashuje bieżące gniazdo na urządzeniu A/B. Jeśli pakiet aktualizacji zawiera też obrazy dla drugiego, nieużywanego obecnie slotu, fastboot też je flashuje. Dostępne opcje:

  • --slot SLOT. Zastąp domyślne zachowanie i poproś fastboot o flashowanie gniazda przekazanego jako argument.
  • --set-active [SLOT]. Ustaw slot jako aktywny. Jeśli nie podasz żadnego argumentu opcjonalnego, bieżący slot zostanie ustawiony jako aktywny.
  • fastboot --help. Uzyskaj szczegółowe informacje o poleceniach.

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

  • has-slot:<partition-base-name-without-suffix>. Zwraca „yes”, jeśli podana partycja obsługuje boksy, a w przeciwnym razie „no”.
  • current-slot. Zwraca sufiks gniazda, z którego nastąpi następne uruchomienie.
  • slot-count. Zwraca liczbę całkowitą reprezentującą liczbę dostępnych miejsc. Obecnie obsługiwane są 2 miejsca, więc ta wartość to 2.
  • slot-successful:<slot-suffix>. Zwraca „yes”, jeśli dany slot został oznaczony jako uruchomiony, a w przeciwnym razie „no”.
  • slot-unbootable:<slot-suffix>. Zwraca „yes”, jeśli dane gniazdo jest oznaczone jako nieuruchamialne, a w przeciwnym razie „no”.
  • slot-retry-count:<slot-suffix>. Liczba pozostałych ponownych prób uruchomienia danego gniazda.

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

Generowanie pakietów OTA

Narzędzia pakietu OTA używają tych samych poleceń co polecenia w przypadku urządzeń innych niż A/B. Plik target_files.zip musi zostać wygenerowany przez zdefiniowanie zmiennych kompilacji dla elementu docelowego testu A/B. Narzędzia pakietu OTA automatycznie identyfikują i generują pakiety w formacie aktualizatora A/B.

Przykłady:

  • Aby wygenerować pełną aktualizację OTA:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Aby wygenerować przyrostową aktualizację 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 aktualizować dowolną parę partycji A/B zdefiniowanych na tym samym dysku. Para partycji ma wspólny prefiks (np. system lub boot) i sufiks dla każdego gniazda (np. _a). Listę partycji, dla których generator ładunku definiuje aktualizację, konfiguruje zmienna AB_OTA_PARTITIONS make.

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

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Wszystkie partycje zaktualizowane przez update_engine nie mogą być modyfikowane przez pozostałą część systemu. Podczas aktualizacji przyrostowych lub różnicowych dane binarne z bieżącego boksu są używane do generowania danych w nowym boksie. Wszelkie modyfikacje mogą spowodować, że nowe dane przedziału nie przejdą weryfikacji podczas procesu aktualizacji, a tym samym aktualizacja się nie powiedzie.

Konfigurowanie po instalacji

Możesz skonfigurować krok po instalacji dla każdej zaktualizowanej partycji w inny sposób, używając zestawu par klucz-wartość. Aby uruchomić program znajdujący się w lokalizacji /system/usr/bin/postinst w nowym obrazie, podaj ś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 systemowego mount(2). Dodaj do plików .mk produktu lub urządzenia (w stosownych przypadkach):

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

Kompilowanie aplikacji

Aplikacje można skompilować w tle przed ponownym uruchomieniem z nowym obrazem systemu. Aby kompilować aplikacje w tle, dodaj do konfiguracji urządzenia produktu (w pliku device.mk produktu) następujący kod:

  1. Dołącz komponenty natywne do kompilacji, aby skrypt kompilacji i pliki binarne zostały skompilowane i uwzględnione w obrazie systemu.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Połącz skrypt kompilacji z update_engine, aby był uruchamiany 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 zoptymalizowanych plików na nieużywanej drugiej partycji systemowej, zapoznaj się z sekcją Instalacja plików DEX_PREOPT podczas pierwszego uruchomienia.