Dynamiczne aktualizacje systemu

Dynamiczne aktualizacje systemu (DSU) umożliwiają utworzenie obrazu systemu Android, który użytkownicy mogą pobrać z Internetu i wypróbować bez ryzyka uszkodzenia bieżącego obrazu systemu. W tym dokumencie opisano sposób obsługi DSU.

Wymagania jądra

Zobacz Implementowanie partycji dynamicznych, aby zapoznać się z wymaganiami jądra.

Ponadto DSU korzysta z funkcji jądra urządzenia-mapper-verity (dm-verity), aby zweryfikować obraz systemu Android. Musisz więc włączyć następujące konfiguracje 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 korzystała z systemu plików F2FS lub ext4. F2FS zapewnia lepszą wydajność i jest zalecany, ale różnica powinna być nieznaczna.

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

  • Używanie F2FS:
    • 109s, użytkownik 8G, system 867M, typ systemu plików: F2FS: szyfrowanie=aes-256-xts:aes-256-cts
    • 104s, użytkownik 8G, system 867M, typ systemu plików: F2FS: szyfrowanie=lód
  • Używając ext4:
    • 135s, użytkownik 8G, system 867M, typ systemu plików: ext4: szyfrowanie=aes-256-xts:aes-256-cts

Jeśli na Twojej platformie trwa to znacznie dłużej, możesz sprawdzić, czy flaga montowania zawiera flagę powodującą zapis „synchronizujący”, lub możesz jawnie określić flagę „asynchroniczną”, aby uzyskać lepszą wydajność.

Partycja metadata (16 MB lub większa) jest wymagana do przechowywania danych związanych z zainstalowanymi obrazami. Należy go zamontować podczas montażu pierwszego stopnia.

Partycja userdata musi używać systemu plików F2FS lub ext4. Używając F2FS, dołącz wszystkie poprawki związane z F2FS dostępne we wspólnym jądrze Androida .

DSU został opracowany i przetestowany z jądrem/wspólnym 4.9. Dla tej funkcji zaleca się używanie jądra 4.9 lub nowszego.

Zachowanie HAL dostawcy

Tkacz HAL

Weaver HAL zapewnia stałą liczbę miejsc do przechowywania kluczy użytkownika. DSU zajmuje dwa dodatkowe gniazda na klucze. Jeśli producent OEM ma warstwę HAL weaver, musi mieć wystarczającą liczbę gniazd na ogólny obraz systemu (GSI) i obraz hosta.

Strażnik HAL

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

Sprawdź rozruch

Jeśli chcesz obsługiwać uruchamianie obrazów GSI programisty w stanie ZABLOKOWANYM bez wyłączania zweryfikowanego rozruchu, dołącz klucze GSI programisty, dodając następujący wiersz do pliku device/<device_name>/device.mk :

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

Zabezpieczenie przed cofnięciem

W przypadku korzystania z DSU pobrany obraz systemu Android musi być nowszy niż bieżący obraz systemu na urządzeniu. Odbywa się to poprzez porównanie poziomów poprawek zabezpieczeń w deskryptorze właściwości AVB Android Verified Boot (AVB) obu obrazów systemu: Prop: com.android.build.system.security_patch -> '2019-04-05' .

W przypadku urządzeń nie korzystających z AVB, umieść poziom poprawki zabezpieczeń bieżącego obrazu systemu w wierszu poleceń jądra lub bootconfig za pomocą programu ładującego: androidboot.system.security_patch=2019-04-05 .

Wymagania sprzętowe

Po uruchomieniu instancji DSU przydzielane są dwa pliki tymczasowe:

  • Partycja logiczna do przechowywania GSI.img (1~1,5 G)
  • Pusta partycja /data 8 GB jako piaskownica do uruchamiania GSI

Zalecamy zarezerwowanie co najmniej 10 GB wolnego miejsca przed uruchomieniem instancji DSU. DSU obsługuje także alokację z karty SD. Jeśli dostępna jest karta SD, ma ona najwyższy priorytet przydziału. Obsługa kart SD ma kluczowe znaczenie w przypadku urządzeń o niższym poborze mocy, które mogą nie mieć wystarczającej ilości pamięci wewnętrznej. Jeśli karta SD jest dostępna, upewnij się, że nie jest adoptowana. DSU nie obsługuje przyjętych kart SD .

Dostępne frontendy

Możesz uruchomić DSU za pomocą adb , aplikacji OEM lub modułu ładującego DSU jednym kliknięciem (w systemie Android 11 lub nowszym).

Uruchom DSU za pomocą adb

Aby uruchomić DSU za pomocą adb, wprowadź następujące 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

Uruchom DSU za pomocą aplikacji

Głównym punktem wejścia do DSU jest 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);
    }

Musisz spakować/preinstalować tę aplikację na urządzeniu. Ponieważ DynamicSystemClient jest interfejsem API systemu, nie można zbudować aplikacji przy użyciu zwykłego interfejsu API pakietu SDK ani opublikować jej w Google Play. Celem tej aplikacji jest:

  1. Pobierz listę obrazów i odpowiedni adres URL według schematu zdefiniowanego przez dostawcę.
  2. Dopasuj obrazy na liście do urządzenia i pokaż zgodne obrazy, które użytkownik może wybrać.
  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 spakowany gzimem, nierozrzedzony plik obrazu systemu, który można utworzyć za pomocą następujących poleceń:

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

Nazwa pliku powinna mieć następujący 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

Ładowarka DSU jednym kliknięciem

Android 11 wprowadza moduł ładujący DSU uruchamiany jednym kliknięciem, będący nakładką w ustawieniach programisty.

Uruchamianie modułu ładującego DSU

Rysunek 1. Uruchamianie modułu ładującego DSU

Gdy programista kliknie przycisk modułu ładującego DSU , pobierze z Internetu wstępnie skonfigurowany deskryptor DSU JSON i wyświetli wszystkie odpowiednie obrazy w pływającym menu. Wybierz obraz, aby rozpocząć instalację DSU, a postęp będzie widoczny na pasku powiadomień.

Postęp instalacji obrazu DSU

Rysunek 2. Postęp instalacji obrazu DSU

Domyślnie moduł ładujący DSU ładuje deskryptor JSON zawierający obrazy GSI. W poniższych sekcjach pokazano, jak tworzyć pakiety DSU podpisane przez OEM i ładować je z modułu ładującego DSU.

Flaga funkcji

Funkcja DSU znajduje się pod flagą funkcji settings_dynamic_android . Przed użyciem DSU upewnij się, że odpowiednia flaga funkcji jest włączona.

Włączanie flagi funkcji.

Rysunek 3. Włączanie flagi funkcji

Interfejs użytkownika z flagą funkcji może być niedostępny na urządzeniu z uruchomioną kompilacją użytkownika. W takim przypadku użyj zamiast tego polecenia adb :

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

Obrazy systemu hosta dostawcy w GCE (opcjonalnie)

Jedną z możliwych lokalizacji przechowywania obrazów systemu jest segment Google Compute Engine (GCE). Administrator wersji używa konsoli pamięci GCP do dodawania/usuwania/zmiany zwolnionego obrazu systemu.

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

Dostęp publiczny w GCE

Rysunek 4. Dostęp publiczny w GCE

Procedura upublicznienia elementu 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ż jedną partycję. Na przykład może zawierać plik product.img oprócz pliku system.img . Po uruchomieniu urządzenia, init pierwszego etapu wykrywa zainstalowane partycje DSU i tymczasowo zastępuje partycję na urządzeniu, gdy zainstalowana DSU jest włączona. Pakiet DSU może zawierać partycję, która nie ma odpowiedniej partycji na urządzeniu.

Proces DSU z wieloma partycjami

Rysunek 5. Proces DSU z wieloma partycjami

DSU z podpisem OEM

Aby mieć pewność, że wszystkie obrazy uruchomione na urządzeniu są autoryzowane przez producenta urządzenia, wszystkie obrazy w pakiecie DSU muszą być podpisane. Załóżmy na przykład, że istnieje pakiet DSU zawierający dwa obrazy partycji, jak poniżej:

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

Zarówno system.img jak i product.img muszą być podpisane kluczem OEM przed umieszczeniem ich w pliku ZIP. Powszechną praktyką jest stosowanie algorytmu asymetrycznego, np. RSA, gdzie do podpisania pakietu używany jest tajny klucz, a klucz publiczny służy do jego weryfikacji. Ramdysk pierwszego etapu musi zawierać klucz publiczny parowania, na przykład /avb/*.avbpubkey . Jeśli urządzenie jest już przystosowane do AVB, wystarczy istniejąca procedura podpisywania. Poniższe sekcje ilustrują proces podpisywania i podkreślają położenie klucza pubowego AVB, który jest używany do weryfikacji obrazów w pakiecie DSU.

Deskryptor JSON DSU

Deskryptor DSU JSON opisuje pakiety DSU. Obsługuje dwa prymitywy. Po pierwsze, operacja podstawowa include zawiera dodatkowe deskryptory JSON lub przekierowuje moduł ładujący DSU do nowej lokalizacji. Na przykład:

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

Po drugie, prymityw image jest używany do opisu wydanych pakietów DSU. Wewnątrz prymitywu obrazu znajduje się kilka atrybutów:

  • Atrybuty name i details to ciągi znaków wyświetlane w oknie dialogowym, które użytkownik może wybrać.

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

  • Opcjonalny atrybut pubkey opisuje klucz publiczny, który łączy się z tajnym kluczem używanym do podpisywania pakietu DSU. Jeśli jest to określone, usługa DSU może sprawdzić, czy urządzenie ma klucz używany do weryfikacji pakietu DSU. Pozwala to uniknąć instalowania nierozpoznanego pakietu DSU, na przykład instalowania DSU podpisanego przez OEM-A na urządzeniu wyprodukowanym przez OEM-B.

  • Opcjonalny atrybut tos wskazuje plik tekstowy opisujący warunki korzystania z usługi dla odpowiedniego pakietu DSU. Gdy programista wybierze pakiet DSU z określonym atrybutem warunków usługi, otworzy się okno dialogowe pokazane na rysunku 6, w którym programista prosi o zaakceptowanie warunków usługi przed zainstalowaniem pakietu DSU.

    Okno dialogowe Warunki korzystania z usługi

    Rysunek 6. Okno dialogowe Warunki korzystania z usługi

Dla porównania, oto deskryptor DSU JSON 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 kompatybilnością

Do określenia zgodności pomiędzy pakietem DSU a urządzeniem lokalnym służy kilka atrybutów:

  • cpu_api to ciąg znaków opisujący architekturę urządzenia. Atrybut ten jest obowiązkowy i jest porównywany z właściwością systemową ro.product.cpu.abi . Ich wartości muszą się dokładnie zgadzać.

  • os_version to opcjonalna liczba całkowita określająca wersję Androida. Na przykład w przypadku Androida 10 os_version wynosi 10 , a w przypadku Androida 11 os_version wynosi 11 . Jeśli ten atrybut jest określony, musi być równy lub większy niż właściwość systemowa ro.system.build.version.release . To sprawdzenie służy do zapobiegania uruchamianiu obrazu GSI systemu Android 10 na urządzeniu dostawcy Android 11, które obecnie nie jest obsługiwane. Uruchamianie obrazu GSI systemu Android 11 na urządzeniu z systemem Android 10 jest dozwolone.

  • vndk to opcjonalna tablica określająca wszystkie VNDK zawarte w pakiecie DSU. Jeśli jest określona, ​​moduł ładujący DSU sprawdza, czy uwzględniono numer wyodrębniony z właściwości systemowej ro.vndk.version .

Unieważnij klucze DSU ze względów bezpieczeństwa

W niezwykle rzadkim przypadku, gdy para kluczy RSA używana do podpisywania obrazów DSU zostanie naruszona, należy jak najszybciej zaktualizować ramdysk, aby usunąć skompromitowany klucz. Oprócz aktualizacji partycji rozruchowej można blokować skompromitowane klucze, korzystając z listy unieważnień kluczy DSU (czarnej listy kluczy) z adresu URL HTTPS.

Lista unieważnionych kluczy DSU zawiera listę unieważnionych kluczy publicznych AVB. Podczas instalacji DSU klucze publiczne znajdujące się w obrazach DSU są sprawdzane za pomocą listy odwołań. Jeśli okaże się, że obrazy zawierają unieważniony klucz publiczny, proces instalacji DSU zostanie zatrzymany.

Aby zapewnić bezpieczeństwo, adres URL listy odwołań kluczy powinien być adresem URL protokołu HTTPS i jest określony w ciągu zasobu:

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

Wartość ciągu to https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json i jest to lista unieważnień kluczy GSI wydanych przez firmę Google. Ten ciąg zasobów można nakładać i dostosowywać, dzięki czemu producenci OEM korzystający z funkcji DSU mogą udostępniać i utrzymywać własną czarną listę kluczy. Dzięki temu producent OEM może blokować określone klucze publiczne bez aktualizowania obrazu ramdysku urządzenia.

Format listy odwołań jest następujący:

{
   "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 unieważnionego klucza, w formacie opisanym w sekcji generowania klucza pubkey AVB .
  • status wskazuje status unieważnienia klucza. Obecnie jedyną obsługiwaną wartością jest REVOKED .
  • reason to opcjonalny ciąg znaków opisujący przyczynę unieważnienia.

Procedury DSU

W tej sekcji opisano sposób wykonywania kilku procedur konfiguracji DSU.

Wygeneruj nową parę kluczy

Użyj polecenia openssl , aby wygenerować parę kluczy prywatny/publiczny RSA w formacie .pem (na przykład o rozmiarze 2048 bitów):

$ 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 nie być dostępny i jest przechowywany wyłącznie w sprzętowym module bezpieczeństwa (HSM) . W takim przypadku po wygenerowaniu klucza może być dostępny certyfikat klucza publicznego x509. Aby uzyskać instrukcje dotyczące generowania klucza publicznego AVB z certyfikatu x509, zobacz sekcję Dodawanie klucza pubowego parowania do ramdysku .

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 pubowy parowania do ramdysku

Aby zweryfikować podpisany pakiet DSU, należy umieścić oem_cert.avbpubkey w katalogu /avb/*.avbpubkey . Najpierw przekonwertuj klucz publiczny w formacie PEM na format klucza publicznego AVB:

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

Następnie dołącz klucz publiczny do ramdysku pierwszego etapu, wykonując następujące kroki.

  1. Dodaj wstępnie zbudowany moduł, aby skopiować plik avbpubkey . Na przykład dodaj device/<company>/<board>/oem_cert.avbpubkey i device/<company>/<board>/avb/Android.mk z następującą zawartością:

    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. Spraw, aby cel droidcore był zależny od dodanego oem_cert.avbpubkey :

    droidcore: oem_cert.avbpubkey
    

Wygeneruj atrybut pubkey AVB w deskryptorze JSON

Klucz oem_cert.avbpubkey ma format binarny klucza publicznego AVB. Użyj SHA-1, aby uczynić go czytelnym przed umieszczeniem go w deskryptorze JSON:

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

Będzie to treść atrybutu pubkey deskryptora JSON.

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

Podpisz pakiet DSU

Użyj jednej z poniższych metod, aby podpisać pakiet DSU:

  • Metoda 1: Ponownie użyj artefaktu powstałego w wyniku oryginalnego procesu podpisywania AVB, aby utworzyć pakiet DSU. Alternatywnym podejściem jest wyodrębnienie już podpisanych obrazów z pakietu wydania i użycie wyodrębnionych obrazów do bezpośredniego utworzenia pliku ZIP.

  • Metoda 2: Użyj następujących poleceń, aby podpisać partycje DSU, jeśli dostępny jest klucz prywatny. Każdy img w pakiecie DSU (plik ZIP) jest podpisany 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
    

Aby uzyskać więcej informacji na temat dodawania add_hashtree_footer przy użyciu avbtool , zobacz Korzystanie z avbtool .

Sprawdź lokalnie pakiet DSU

Zaleca się sprawdzenie wszystkich lokalnych obrazów względem klucza publicznego parowania za pomocą następujących poleceń:


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

Oczekiwany wynik wygląda następująco:

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

Zrób pakiet DSU

Poniższy przykład tworzy pakiet DSU zawierający pliki system.img i product.img :

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

Po podpisaniu obu obrazów użyj następującego polecenia, aby utworzyć plik ZIP:

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

Dostosuj DSU jednym kliknięciem

Domyślnie moduł ładujący DSU wskazuje metadane obrazów GSI, którymi są https://...google.com/.../gsi-src.json .

Producenci OEM mogą zastąpić listę, definiując właściwość persist.sys.fflag.override.settings_dynamic_system.list , która wskazuje na ich własny deskryptor JSON. Na przykład producent OEM może udostępnić metadane JSON zawierające GSI, a także zastrzeżone obrazy OEM, takie jak to:

{
    "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.

Łączenie opublikowanych metadanych DSU

Rysunek 7. Łączenie opublikowanych metadanych DSU