Dynamiczne aktualizacje systemu

Dzięki funkcji Dynamiczne aktualizacje systemu (DSU) możesz utworzyć obraz systemu Android, który użytkownicy mogą pobrać z internetu i wypróbować bez ryzyka uszkodzenia obecnego obrazu systemu. Z tego dokumentu dowiesz się, jak obsługiwać DSU.

Wymagania jądra systemu

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

Ponadto DSU korzysta z funkcji jądra device-mapper-verity (dm-verity) do weryfikacji obrazu 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. Tryb F2FS zapewnia lepszą wydajność i jest zalecany, ale różnica powinna być znikoma.

Oto kilka przykładów tego, jak długo trwa aktualizacja systemu na urządzeniu Pixel:

  • Przy użyciu F2FS:
    • 109s, użytkownik 8G, system 867M, typ systemu plików: F2FS: encryption=aes-256-xts:aes-256-cts
    • 104 s, użytkownik 8 G, system 867 M, typ systemu plików: F2FS: encryption=ice
  • Używam ext4:
    • 135s, użytkownik 8G, system 867M, 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ę powodującą „synchronizację”, albo możesz bezpośrednio określić flagę „async”, aby zwiększyć wydajność.

Do przechowywania danych związanych z zainstalowanymi obrazami wymagana jest partycja metadata (co najmniej 16 MB). Należy ją zamontować podczas pierwszego etapu podłączania.

Partycja userdata musi korzystać z systemu plików F2FS lub ext4. Jeśli używasz F2FS, uwzględnij wszystkie poprawki związane z F2FS dostępne w wspólnym jądrze Androida.

DSU został opracowany i przetestowany z jądrem/common 4.9. Zalecamy używanie jądra w wersji 4.9 lub nowszej.

Zachowanie interfejsu HAL dostawcy

Weaver HAL

HAL Weaver zapewnia stałą liczbę przedziałów do przechowywania kluczy użytkownika. DSU zużywa 2 dodatkowe przedziały kluczy. Jeśli OEM ma interfejs HAL typu weaver, musi mieć wystarczającą liczbę slotów na ogólny obraz systemu (GSI) i obraz hosta.

HAL bramkarz

Identyfikator HAL bramy musi obsługiwać duże wartości USER_ID, ponieważ GSI kompensuje identyfikatory UID do HAL o +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)
  • Pusta partycja /data o pojemności 8 GB jako piaskownica do uruchamiania GSI

Zalecamy zarezerwowanie co najmniej 10 GB wolnego miejsca przed uruchomieniem instancji DSU. DSU obsługuje też przydział z karty SD. Gdy karta SD jest obecna, ma najwyższy priorytet przy przydzielaniu. Obsługa kart SD jest kluczowa w przypadku urządzeń o mniejszych możliwościach, które mogą nie mieć wystarczającej ilości pamięci wewnętrznej. Jeśli masz kartę SD, upewnij się, że nie jest wykorzystywana. DSU nie obsługuje przyjętych kart SD.

Dostępne frontendy

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

Uruchamianie DSU za pomocą narzędzia adb

Aby uruchomić DSU za pomocą narzędzia 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);
    }

Musisz spakować tę aplikację lub wstępnie zainstalować ją na urządzeniu. DynamicSystemClient to systemowy interfejs API, dlatego nie możesz utworzyć aplikacji za pomocą zwykłego interfejsu API pakietu SDK ani opublikować jej w Google Play. Celem tej aplikacji jest:

  1. Pobieranie listy obrazów i odpowiadających im adresów URL za pomocą schematu określonego przez dostawcę.
  2. Dopasuj obrazy z listy do urządzenia i wyświetlaj zgodne obrazy, które użytkownik może wybrać.
  3. Wywołaj funkcję 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

W Androidzie 11 wprowadzono jednym kliknięciem narzędzie ładujące DSU – frontend w ustawieniach programisty.

Uruchamiam narzędzie ładujące DSU

Rysunek 1. Uruchamianie programu ładującego DSU

Gdy deweloper kliknie przycisk Moduł ładujący DS, pobierze z internetu wstępnie skonfigurowany deskryptor JSON DSU i wyświetli wszystkie odpowiednie obrazy w pływającym menu. 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 ładowarka DSU wczytuje opis JSON zawierający obrazy GSI. W sekcjach poniżej pokazujemy, jak tworzyć pakiety DSU podpisane przez OEM i ładować je w module ładującym 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łączam flagę funkcji.

Rysunek 3. Włączanie flagi funkcji

Interfejs flag funkcji może być niedostępny na urządzeniach z kompilacją użytkownika. W takim przypadku zamiast tego użyj 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 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 publicznego udostępniania elementu jest dostępna w dokumentacji Google Cloud.

Wielopartycjonalna jednostka DSU w pliku ZIP

Od Androida 11 DSU może mieć więcej niż 1 partycję. Na przykład oprócz system.img może zawierać też znak product.img. Po uruchomieniu urządzenia pierwszy etap (init) wykrywa zainstalowane partycje DSU i tymczasowo zastępuje partycję na urządzeniu, gdy zainstalowana jest zainstalowana platforma DSU. 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

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 na przykład, że istnieje pakiet DSU zawierający 2 obrazy partycji, jak poniżej:

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. Zazwyczaj stosuje się algorytm asymetryczny, np. RSA, w którym tajny klucz jest używany 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 już zastosowało AVB, wystarczy istniejąca procedura podpisywania. W sekcjach poniżej ilustrujemy proces podpisywania i wyróżniamy umiejscowienie klucza publicznego AVB, który jest używany do weryfikowania obrazów w pakiecie DSU.

Deskryptor JSON DSU

Deskryptor JSON DSU opisuje pakiety DSU. Obsługuje 2 elementy podstawowe. Pierwszy: element 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: podstawowy image służy do opisywania udostępnionych pakietów DSU. Pierwotny obraz zawiera kilka atrybutów:

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

  • Atrybuty cpu_api, vndk i os_version służą do sprawdzania zgodności, które opisaliśmy 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. Pozwala to uniknąć instalowania nierozpoznanego pakietu DSU, np. instalowania dokumentu DSU podpisanego przez OEM-A na urządzeniu OEM-B.

  • Opcjonalny atrybut tos wskazuje plik tekstowy z opisem warunków korzystania z 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 będzie musiał 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ą

Do określenia zgodności pakietu DSU z urządzeniem lokalnym służy kilka atrybutów:

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

  • os_version to opcjonalna liczba całkowita, która określa wersję Androida. Na przykład w Androidzie 10 wartość os_version to 10, a w Androidzie 11 – os_version to 11. Jeśli jest określony ten atrybut, musi on być równy lub większy od 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 sprzedawcy z Androidem 11, które obecnie nie jest obsługiwane. Dozwolone jest uruchomienie obrazu GSI Androida 11 na urządzeniu z Androidem 10.

  • vndk to opcjonalna tablica, która określa wszystkie klucze VNDK zawarte 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.

Ze względów bezpieczeństwa unieważnianie kluczy DSU

W bardzo rzadkich przypadkach, gdy para kluczy RSA używana do podpisywania obrazów DSU zostanie przejęta, plik Ramdisk należy zaktualizować jak najszybciej, aby usunąć przejęty klucz. Oprócz aktualizowania partycji rozruchowej możesz zablokować przejęte klucze, korzystając z listy odwołanych kluczy DSU (czarnej listy kluczy) dostępnej pod adresem URL HTTPS.

Lista wycofania kluczy DSU zawiera listę wycofania kluczy publicznych AVB. Podczas instalacji DSU klucze publiczne w obrazach DSU są weryfikowane za pomocą listy unieważnień. 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 protokołu 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 zablokować określone klucze publiczne bez aktualizowania obrazu ramki 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 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 opisujemy, jak wykonać różne procedury konfiguracji DSU.

Generowanie nowej pary kluczy

Za pomocą polecenia openssl wygeneruj parę kluczy RSA (prywatny/publiczny) w formacie .pem (np. 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 być niedostępny i jest 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ż w formacie PEM.

Dodaj klucz publiczny parowania do dysku ramdy

Aby zweryfikować podpisany pakiet DSU, musisz umieścić oem_cert.avbpubkey w folderze /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 dysku RAM pierwszego etapu, wykonując czynności opisane poniżej.

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

    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 ma format binarny klucza publicznego AVB. Użyj SHA-1, aby zapewnić czytelność przed umieszczeniem go 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",
         ...
      },

Podpisz pakiet DSU

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

  • Metoda 1. Wykorzystaj ponownie artefakt utworzony w ramach pierwotnego procesu 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 sekcji Korzystanie z narzędzia avbtool.

Weryfikacja pakietu DSU lokalnie

Zalecamy zweryfikowanie wszystkich obrazów lokalnych pod kątem zgodności z kluczem publicznym parowania za pomocą tych poleceń:


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

Oczekiwane dane wyjściowe wyglądają tak:

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 użyj tego 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 -

Dostosowywanie usługi DSU jednym kliknięciem

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

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

{
    "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, tak jak to widać na rys. 7.

Łańcuch opublikowanych metadanych DSU

Rysunek 7. Łańcuchowanie opublikowanych metadanych DSU