Dynamiczne aktualizacje systemu

Dynamiczne aktualizacje systemu (DSU) umożliwiają utworzenie obrazu systemu Androida, który użytkownicy mogą pobrać z internetu i wypróbować bez ryzyka uszkodzenia bieżącego obrazu systemu. Z tego dokumentu dowiesz się, jak obsługiwać DSU.

Wymagania dotyczące jądra

Wymagania dotyczące jądra znajdziesz w artykule Wdrażanie partycji dynamicznych.

Ponadto DSU korzysta z funkcji jądra device-mapper-verity (dm-verity), aby zweryfikować obraz systemu Android. Musisz więc włączyć te ustawienia jądra:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Wymagania dotyczące partycji

Począwszy od Androida 11, DSU wymaga, aby partycja /data używała systemu plików F2FS lub ext4. F2FS zapewnia lepszą wydajność i jest zalecany, ale różnica powinna być nieistotna.

Oto kilka przykładów czasu trwania dynamicznej aktualizacji systemu na urządzeniu Pixel:

  • Korzystanie z F2FS:
    • 109 s, 8 GB użytkownika, 867 M system, typ systemu plików: F2FS: encryption=aes-256-xts:aes-256-cts
    • 104 s, użytkownik 8 GB, system 867 M, typ systemu plików: F2FS: encryption=ice
  • Używam ext4:
    • 135 s, 8 GB użytkownika, 867 MB systemu, typ systemu plików: ext4: encryption=aes-256-xts:aes-256-cts

Jeśli na Twojej platformie trwa to znacznie dłużej, sprawdź, czy flaga mount zawiera flagę, która powoduje zapisywanie w trybie „sync”, lub określ wyraźnie flagę „async”, aby uzyskać lepszą wydajność.

Do przechowywania danych związanych z zainstalowanymi obrazami wymagana jest partycja metadata (co najmniej 16 MB). Musi być zamontowany na pierwszym etapie montażu.

Partycja userdata musi używać systemu plików F2FS lub ext4. Jeśli używasz F2FS, uwzględnij wszystkie łaty do F2FS dostępne w wspólnym jądrze Androida.

DSU został opracowany i przetestowany z jądrem/common 4.9. Zalecana jest wersja jądra 4.9 lub nowsza.

Zachowanie interfejsu HAL dostawcy

Weaver HAL

Weaver HAL udostępnia stałą liczbę sekcji do przechowywania kluczy użytkownika. DSU zajmuje 2 dodatkowe sloty na klucze. Jeśli OEM ma interfejs HAL weaver, musi mieć wystarczającą liczbę slotów na ogólny obraz systemu (GSI) i obraz hosta.

interfejs HAL strażnika dostępu,

interfejs API interfejsu HAL usługi Gatekeeper musi obsługiwać duże wartości USER_ID, ponieważ GSI przesuwa identyfikatory UID do interfejsu HAL o wartość +1000000.

Weryfikacja podczas uruchamiania

Jeśli chcesz umożliwić uruchamianie obrazów GSI dla deweloperówzamkniętym stanie bez wyłączania weryfikowanego rozruchu, dodaj klucze GSI dla deweloperów, dodając ten wiersz do pliku device/<device_name>/device.mk:

$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)

Ochrona przed przywróceniem

W przypadku korzystania z DSU pobrany obraz systemu Android musi być nowszy niż bieżący obraz systemu na urządzeniu. Polega to na porównaniu poziomów poprawek zabezpieczeń w weryfikacji podczas uruchamiania Androida (AVB) opisie właściwości AVB obu obrazów systemu: Prop: com.android.build.system.security_patch -> '2019-04-05'.

W przypadku urządzeń, które nie korzystają z AVB, ustaw poziom poprawki zabezpieczeń bieżącego obrazu systemu w cmdline jądra lub w bootconfig za pomocą bootloadera:androidboot.system.security_patch=2019-04-05.

Wymagania sprzętowe

Gdy uruchomisz instancję DSU, przydzielone zostaną 2 pliki tymczasowe:

  • Partycja logiczna do przechowywania GSI.img (1–1,5 G)
  • pusty 8-gigabajtowy /data jako piaskownica do uruchamiania GSI;

Zalecamy zarezerwowanie co najmniej 10 GB wolnego miejsca przed uruchomieniem instancji DSU. DSU obsługuje też alokację z karty SD. Gdy karta SD jest obecna, ma najwyższy priorytet przy przydzielaniu. Obsługa kart SD jest kluczowa w przypadku słabszych urządzeń, które mogą nie mieć wystarczającej ilości pamięci wewnętrznej. Jeśli jest karta SD, sprawdź, czy nie jest ona adoptowana. DSU nie obsługuje przystosowanych kart SD.

Dostępne interfejsy

Możesz uruchomić DSU za pomocą adb, aplikacji OEM lub ładowarki DSU jednym kliknięciem (w Androidzie 11 lub nowszym).

Uruchamianie DSU za pomocą adb

Aby uruchomić DSU za pomocą adb, wpisz te polecenia:

$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity  \
-a android.os.image.action.START_INSTALL    \
-d file:///storage/emulated/0/Download/system.raw.gz  \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1)  \
--el KEY_USERDATA_SIZE 8589934592

Uruchamianie aktualizacji w tle za pomocą aplikacji

Głównym punktem wejścia do DSU jest interfejs API android.os.image.DynamicSystemClient.java:

public class DynamicSystemClient {


...
...

     /**
     * Start installing DynamicSystem from URL with default userdata size.
     *
     * @param systemUrl A network URL or a file URL to system image.
     * @param systemSize size of system image.
     */
    public void start(String systemUrl, long systemSize) {
        start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
    }

Aplikację należy dołączyć do urządzenia lub wstępnie zainstalować na nim. Ponieważ DynamicSystemClient to interfejs API systemu, nie możesz utworzyć aplikacji za pomocą zwykłego interfejsu API pakietu SDK ani jej opublikować w Google Play. Cel tej aplikacji:

  1. Pobieranie listy obrazów i odpowiadających im adresów URL za pomocą schematu określonego przez dostawcę.
  2. Dopasowuj obrazy na liście do urządzenia i pokaż użytkownikowi obrazy zgodne z nim.
  3. Wywołaj DynamicSystemClient.start w ten sposób:

    DynamicSystemClient aot = new DynamicSystemClient(...)
       aot.start(
            ...URL of the selected image...,
            ...uncompressed size of the selected image...);
    
    

Adres URL wskazuje skompresowany, nierzadki plik obrazu systemu, który możesz utworzyć za pomocą tych poleceń:

$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz

Nazwa pliku powinna mieć ten format:

<android version>.<lunch name>.<user defined title>.raw.gz

Przykłady:

  • o.aosp_taimen-userdebug.2018dev.raw.gz
  • p.aosp_taimen-userdebug.2018dev.raw.gz

Narzędzie do ładowania DSU jednym kliknięciem

Android 11 wprowadza ładowarkę DSU jednym kliknięciem, która jest frontendem w ustawieniach programisty.

Uruchamianie programu ładowania DSU

Rysunek 1. Uruchamianie programu ładowania DSU

Gdy deweloper kliknie przycisk DSU Loader, z sieci pobiera wstępnie skonfigurowany opis JSON usługi dynamicznej aktualizacji i wyświetla w menu przewijanym wszystkie odpowiednie obrazy. Wybierz obraz, aby rozpocząć instalację DSU. Postęp instalacji będzie widoczny na pasku powiadomień.

Postęp instalacji obrazu DSU

Rysunek 2. Postęp instalacji obrazu DSU

Domyślnie ładownik DSU wczytuje opis JSON zawierający obrazy GSI. W następnych sekcjach dowiesz się, jak tworzyć pakiety DSU podpisane przez OEM-a i ładować je za pomocą programu ładującego DSU.

Flaga funkcji

Funkcja DSU jest obsługiwana przez flagę funkcji settings_dynamic_android. Zanim zaczniesz używać DSU, upewnij się, że odpowiednia flaga funkcji jest włączona.

Włączanie flagi funkcji.

Rysunek 3. Włączanie flagi funkcji

Interfejs flagi funkcji może być niedostępny na urządzeniu z wersją użytkową. W takim przypadku użyj polecenia adb:

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

dostawca hostuje obrazy systemu w GCE (opcjonalnie);

Jednym z możliwych miejsc przechowywania obrazów systemu jest zasobnik Google Compute Engine (GCE). Administrator wersji korzysta z konsoli miejsca na dane GCP, aby dodawać, usuwać i zmieniać opublikowany obraz systemu.

Obrazy muszą być dostępne publicznie, jak tutaj:

Dostęp publiczny w GCE

Rysunek 4. Dostęp publiczny w GCE

Procedura udostępniania elementów jest dostępna w dokumentacji Google Cloud.

DSU z wieloma partycjami w pliku ZIP

Począwszy od Androida 11, DSU może mieć więcej niż 1 partycję. Może na przykład zawierać pole product.img oprócz pola system.img. Podczas uruchamiania urządzenia pierwszy etap init wykrywa zainstalowane partycje DSU i tymczasowo zastępuje partycję na urządzeniu, gdy zainstalowana partycja DSU jest włączona. Pakiet DSU może zawierać partycję, która nie ma odpowiadającej partycji na urządzeniu.

Proces DSU z wieloma partycjami

Rysunek 5. Proces DSU z wieloma partycjami

Pliki DSU podpisane przez OEM

Aby mieć pewność, że wszystkie obrazy wyświetlane na urządzeniu są autoryzowane przez producenta urządzenia, wszystkie obrazy w pakiecie DSU muszą być podpisane. Załóżmy, że mamy pakiet DSU zawierający 2 obrazy partycji:

dsu.zip {
    - system.img
    - product.img
}

Zarówno system.img, jak i product.img muszą być podpisane kluczem OEM, zanim zostaną umieszczone w pliku ZIP. Powszechną praktyką jest używanie algorytmu asymetrycznego, na przykład RSA, w którym klucz prywatny służy do podpisywania pakietu, a klucz publiczny do jego weryfikacji. Ramdysk pierwszego etapu musi zawierać klucz publiczny do parowania, na przykład /avb/*.avbpubkey. Jeśli urządzenie obsługuje już AVB, wystarczy użyć dotychczasowej procedury podpisywania. W kolejnych sekcjach przedstawiono proces podpisywania oraz wyróżniono miejsce docelowe klucza publicznego AVB, który służy do weryfikacji obrazów w pakiecie DSU.

Deskryptor JSON DSU

Deskryptor JSON DSU opisuje pakiety DSU. Obsługuje 2 elementy podstawowe. Po pierwsze, prymityw include zawiera dodatkowe deskryptory JSON lub przekierowuje ładowarkę DSU do nowej lokalizacji. Przykład:

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

Po drugie, prymityw image służy do opisywania opublikowanych pakietów DSU. Pierwotny obraz zawiera kilka atrybutów:

  • Atrybuty namedetails to ciągi tekstowe wyświetlane w oknie, aby użytkownik mógł dokonać wyboru.

  • Atrybuty cpu_api, vndk i os_version są używane do sprawdzania zgodności, co jest opisane w następnej sekcji.

  • Opcjonalny atrybut pubkey opisuje klucz publiczny, który tworzy parę z kluczem tajnym używanym do podpisywania pakietu DSU. Jeśli jest on określony, usługa DSU może sprawdzić, czy urządzenie ma klucz używany do weryfikacji pakietu DSU. Zapobiega to instalacji niezidentyfikowanego pakietu DSU, na przykład zainstalowania pakietu DSU podpisanego przez OEM-A na urządzeniu wyprodukowanym przez OEM-B.

  • Opcjonalny atrybut tos wskazuje plik tekstowy, który opisuje warunki korzystania z usługi dla odpowiedniego pakietu DSU. Gdy deweloper wybierze pakiet DSU z określonym atrybutem warunków korzystania z usługi, otworzy się okno dialogowe pokazane na rysunku 6. Przed zainstalowaniem pakietu DSU deweloper musi zaakceptować warunki korzystania z usługi.

    Okno Warunków korzystania z usługi

    Rysunek 6. Okno Warunków korzystania z usługi

Poniżej zamieszczamy przykład deskryptora JSON DSU dla GSI:

{
   "images":[
      {
         "name":"GSI+GMS x86",
         "os_version":"10",
         "cpu_abi": "x86",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI+GMS ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
         "uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI ARM64",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
      },
      {
         "name":"GSI x86_64",
         "os_version":"10",
         "cpu_abi": "x86_64",
         "details":"exp-QP1A.190711.020.C4-5928301",
         "vndk":[
            27,
            28,
            29
         ],
         "pubkey":"",
         "uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
      }
   ]
}

Zarządzanie zgodnością

Aby określić zgodność pakietu DSU z urządzeniem lokalnym, używa się kilku atrybutów:

  • cpu_api to ciąg tekstowy opisujący architekturę urządzenia. Ten atrybut jest wymagany i jest porównywany z właściwością systemową ro.product.cpu.abi. Ich wartości muszą być identyczne.

  • os_version to opcjonalna liczba całkowita określająca wersję Androida. Na przykład w Androidzie 10 wartość os_version to 10, a w Androidzie 11 – os_version to 11. Jeśli ten atrybut jest określony, musi być równy lub większy od wartości właściwości systemowej ro.system.build.version.release. To sprawdzanie służy do zapobiegania uruchamianiu obrazu GSI Androida 10 na urządzeniu z Androidem 11, które nie jest obecnie obsługiwane. Rozruchowy obraz GSI Androida 11 jest dozwolony na urządzeniu z Androidem 10.

  • vndk to opcjonalna tablica, która określa wszystkie VNDK uwzględnione w pakiecie DSU. Gdy jest ona określona, ładowarka DSU sprawdza, czy podany numer jest zawarty w liczbie wyodrębnionej z właściwości systemowej ro.vndk.version.

Unieważnianie kluczy DSU ze względów bezpieczeństwa

W bardzo rzadkich przypadkach, gdy klucz RSA używany do podpisywania obrazów DSU zostanie skompromitowany, należy jak najszybciej zaktualizować dysk RAM, aby usunąć skompromitowany klucz. Oprócz aktualizacji partycji rozruchowej możesz zablokować klucze zagrożone za pomocą listy odwołania klucza DSU (lista kluczy na czarnej liście) z adresu URL HTTPS.

Lista wycofania kluczy DSU zawiera listę wycofanych kluczy publicznych AVB. Podczas instalacji DSU klucze publiczne w obrazach DSU są weryfikowane za pomocą listy odwołanych certyfikatów. Jeśli okaże się, że obrazy zawierają unieważniony klucz publiczny, proces instalacji DSU zostanie przerwany.

Aby zapewnić bezpieczeństwo, adres URL listy kluczy musi być adresem URL HTTPS. Jest on określony w ciągu zasobu:

frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url

Wartość ciągu tekstowego to https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json, czyli lista odwołań kluczy GSI udostępnionych przez Google. Ten ciąg zasobów można nakładać i dostosowywać, aby producenci OEM, którzy korzystają z funkcji DSU, mogli udostępniać i utrzymywać własną listę kluczy zakazanych. Dzięki temu producent OEM może blokować określone klucze publiczne bez aktualizowania obrazu dysku RAM urządzenia.

Format listy odwołań:

{
   "entries":[
      {
         "public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      },
      {
         "public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
         "status":"REVOKED",
         "reason":"Key revocation test key"
      }
   ]
}
  • public_key to skrót SHA-1 odwołanego klucza w formacie opisanym w sekcji generowania publicznego klucza AVB.
  • status wskazuje stan odwołania klucza. Obecnie jedyną obsługiwaną wartością jest REVOKED.
  • reason to opcjonalny ciąg znaków opisujący przyczynę odwołania.

procedury DSU,

W tej sekcji opisano, jak wykonać kilka procedur konfiguracji DSU.

Generowanie nowej pary kluczy

Aby wygenerować parę kluczy (publiczny i prywatny) RSA w formacie .pem (np. o rozmiarze 2048 bitów), użyj polecenia openssl:

$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem

Klucz prywatny może być niedostępny i przechowywany tylko w sprzętowym module zabezpieczeń (HSM). W takim przypadku po wygenerowaniu klucza może być dostępny certyfikat klucza publicznego X.509. Więcej informacji o generowaniu klucza publicznego AVB z certyfikatu x509 znajdziesz w sekcji Dodawanie klucza publicznego parowania do dysku RAM.

Aby przekonwertować certyfikat x509 na format PEM:

$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem

Pomiń ten krok, jeśli certyfikat jest już plikiem PEM.

Dodaj klucz publiczny parowania do pamięci RAM.

Aby zweryfikować podpisany pakiet DSU, musisz umieścić oem_cert.avbpubkey w folderze /avb/*.avbpubkey. Najpierw przekonwertuj klucz publiczny w formacie PEM na klucz publiczny AVB:

$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey

Następnie dodaj klucz publiczny do dysku RAM pierwszego etapu, wykonując te czynności.

  1. Dodaj gotowy moduł, aby skopiować avbpubkey. Na przykład dodaj pola device/<company>/<board>/oem_cert.avbpubkeydevice/<company>/<board>/avb/Android.mk z treściami takimi jak te:

    include $(CLEAR_VARS)
    
    LOCAL_MODULE := oem_cert.avbpubkey
    LOCAL_MODULE_CLASS := ETC
    LOCAL_SRC_FILES := $(LOCAL_MODULE)
    ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
    LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb
    else
    LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb
    endif
    
    include $(BUILD_PREBUILT)
    
  2. Ustaw zależność celu droidcore od dodanego oem_cert.avbpubkey:

    droidcore: oem_cert.avbpubkey
    

Wygeneruj atrybut klucza publicznego AVB w określniku JSON

oem_cert.avbpubkey jest w formacie binarnym klucza publicznego AVB. Użyj funkcji SHA-1, aby uczynić go czytelnym przed umieszczeniem w deskryptorze JSON:

$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20

Będzie to zawartość atrybutu pubkey w określniku JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

Podpisywanie pakietu DSU

Aby podpisać pakiet DSU, użyj jednej z tych metod:

  • Metoda 1. Użyj artefaktu utworzonego przez pierwotny proces podpisywania AVB, aby utworzyć pakiet DSU. Alternatywnym podejściem jest wyodrębnienie już podpisanych obrazów z opakowania wersji i użycie wyodrębnionych obrazów do utworzenia pliku ZIP bezpośrednio.

  • Metoda 2. Jeśli klucz prywatny jest dostępny, podpisz partycje DSU za pomocą tych poleceń. Każdy img w pakiecie DSU (pliku ZIP) jest podpisywany osobno:

    $ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/')
    $ for partition in system product; do
        avbtool add_hashtree_footer \
            --image ${OUT}/${partition}.img \
            --partition_name ${partition} \
            --algorithm SHA256_RSA${key_len} \
            --key oem_cert_pri.pem
    done
    

Więcej informacji o dodawaniu add_hashtree_footer za pomocą avbtool znajdziesz w artykule Używanie narzędzia avbtool.

Weryfikowanie pakietu DSU lokalnie

Zalecamy zweryfikowanie wszystkich lokalnych obrazów za pomocą tych poleceń:


for partition in system product; do
    avbtool verify_image --image ${OUT}/${partition}.img  --key oem_cert_pub.pem
done

Oczekiwany wynik:

Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes

Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes

Tworzenie pakietu DSU

W tym przykładzie tworzymy pakiet DSU zawierający system.img i product.img:

dsu.zip {
    - system.img
    - product.img
}

Po podpisaniu obu obrazów utwórz plik ZIP za pomocą tego polecenia:

$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -

Dostosowywanie funkcji DSU jednym kliknięciem

Domyślnie ładowarka DSU wskazuje metadane obrazów GSI, które są w pliku https://...google.com/.../gsi-src.json.

Producenci OEM mogą zastąpić tę listę, definiując właściwość persist.sys.fflag.override.settings_dynamic_system.list, która wskazuje na ich własny opis JSON. Na przykład OEM może udostępnić metadane w formacie JSON, które obejmują GSI oraz obrazy własne OEM, takie jak te:

{
    "include": ["https://dl.google.com/.../gsi-src.JSON"]
    "images":[
      {
         "name":"OEM image",
         "os_version":"10",
         "cpu_abi": "arm64-v8a",
         "details":"...",
         "vndk":[
            27,
            28,
            29
         ],
         "spl":"...",
         "pubkey":"",
         "uri":"https://.../....zip"
      },

}

Producent OEM może połączyć opublikowane metadane DSU, jak pokazano na rysunku 7.

Łańcuchowanie opublikowanych metadanych DSU

Rysunek 7. Łańcuchowanie opublikowanych metadanych DSU