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. Z tego dokumentu dowiesz się, jak obsługiwać DSU.
Wymagania dotyczące jądra
Wymagania dotyczące jądra znajdziesz w sekcji Wdrażanie partycji dynamicznych.
DSU korzysta też z funkcji jądra device-mapper-verity (dm-verity), aby weryfikować obraz systemu Androida. Musisz więc włączyć te konfiguracje jądra:
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
Wymagania dotyczące partycji
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ć nieznaczna.
Oto kilka przykładów czasu trwania dynamicznej aktualizacji systemu na urządzeniu Pixel:
- Korzystanie z F2FS:
- 109 s, 8 G użytkownika, 867 M systemu, typ systemu plików: F2FS: encryption=aes-256-xts:aes-256-cts
- 104 s, 8 G użytkownika, 867 M system, typ systemu plików: F2FS: encryption=ice
- Korzystanie z 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 montowania zawiera flagę, która powoduje zapis „sync”, lub możesz jawnie określić flagę „async”, aby uzyskać lepszą wydajność.
Partycja metadata
(o rozmiarze co najmniej 16 MB) jest wymagana do przechowywania danych związanych z zainstalowanymi obrazami. Musi być zamontowany podczas montażu w pierwszej fazie.
Partycja userdata
musi używać systemu plików F2FS lub ext4. Jeśli używasz systemu F2FS, uwzględnij wszystkie poprawki związane z tym systemem dostępne w wspólnym jądrze Androida.
DSU został opracowany i przetestowany na jądrze/wspólnej wersji 4.9. W przypadku tej funkcji zalecamy używanie jądra w wersji 4.9 lub nowszej.
Zachowanie interfejsu HAL dostawcy
Warstwa HAL Weaver
Warstwa HAL Weaver udostępnia stałą liczbę miejsc na klucze użytkownika. DSU zajmuje 2 dodatkowe miejsca na klucze. Jeśli producent OEM ma HAL tkacza, musi mieć wystarczającą liczbę gniazd na podstawowy obraz systemu (GSI) i obraz hosta.
Warstwa HAL funkcji Gatekeeper
Gatekeeper HAL musi obsługiwać duże wartości USER_ID
, ponieważ GSI przesuwa identyfikatory UID do HAL o +1000000.
Weryfikacja podczas uruchamiania
Jeśli chcesz obsługiwać uruchamianie obrazów GSI dla deweloperów w stanie ZABLOKOWANYM bez wyłączania weryfikacji rozruchu, uwzględnij 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 cofnięciem
W przypadku korzystania z DSU pobrany obraz systemu Android musi być nowszy niż obecny obraz systemu na urządzeniu. Odbywa się to przez porównanie poziomów poprawek zabezpieczeń w zweryfikowanym rozruchu Androida (AVB) deskryptora 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, umieść poziom aktualizacji zabezpieczeń bieżącego obrazu systemu w wierszu poleceń jądra lub w konfiguracji rozruchu za pomocą programu ładującego:androidboot.system.security_patch=2019-04-05
.
Wymagania sprzętowe
Podczas uruchamiania instancji DSU przydzielane są 2 pliki tymczasowe:
- Partycja logiczna do przechowywania
GSI.img
(1–1,5 GB) - pustą partycję
/data
GB jako piaskownicę do uruchamiania GSI;
Przed uruchomieniem instancji DSU zalecamy zarezerwowanie co najmniej 10 GB wolnego miejsca. DSU obsługuje też przydzielanie miejsca na karcie SD. Gdy karta SD jest obecna, ma najwyższy priorytet w przydzielaniu miejsca. Obsługa kart SD jest kluczowa w przypadku urządzeń o mniejszej mocy, które mogą nie mieć wystarczającej ilości pamięci wewnętrznej. Jeśli karta SD jest włożona, upewnij się, że nie jest zaadoptowana. DSU nie obsługuje zaadoptowanych kart SD.
Dostępne interfejsy
DSU możesz uruchomić za pomocą adb
, aplikacji OEM lub narzędzia do uruchamiania 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 DSU za pomocą aplikacji
Głównym punktem wejścia do DSU jest android.os.image.DynamicSystemClient.java
interfejs API:
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 dołączyć tę aplikację do urządzenia lub wstępnie ją na nim zainstalować. Ponieważ
DynamicSystemClient
jest interfejsem API systemu, nie możesz skompilować aplikacji za pomocą zwykłego interfejsu API pakietu SDK ani opublikować jej w Google Play. Cel tej aplikacji:
- Pobieranie listy obrazów i odpowiedniego adresu URL ze schematem zdefiniowanym przez dostawcę.
- Dopasuj obrazy na liście do urządzenia i wyświetl użytkownikowi zgodne obrazy do wyboru.
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 plik obrazu systemu, który nie jest rzadki. Możesz go 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ć 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
Ładowanie DSU jednym kliknięciem
Android 11 wprowadza ładowarkę DSU, która jest interfejsem w ustawieniach programisty.
Rysunek 1. Uruchamianie narzędzia do ładowania DSU
Gdy deweloper kliknie przycisk DSU Loader, pobierze z internetu wstępnie skonfigurowany deskryptor JSON DSU i wyświetli wszystkie odpowiednie obrazy w menu pływającym. Wybierz obraz, aby rozpocząć instalację DSU. Postęp będzie widoczny na pasku powiadomień.
Rysunek 2. Postęp instalacji obrazu DSU
Domyślnie program wczytujący DSU wczytuje deskryptor JSON zawierający obrazy GSI. W sekcjach poniżej pokazujemy, jak tworzyć pakiety DSU podpisane przez producenta OEM i ładować je z programu do ładowania DSU.
Flaga funkcji
Funkcja DSU jest dostępna pod settings_dynamic_android
flagą funkcji. Zanim zaczniesz korzystać z DSU, upewnij się, że odpowiednia flaga funkcji jest włączona.
Rysunek 3. Włączanie flagi funkcji
Interfejs flagi funkcji może być niedostępny na urządzeniu z wersją użytkownika. W takim przypadku 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 używa konsoli pamięci GCP, aby dodawać, usuwać i zmieniać wydany obraz systemu.
Obrazy muszą być publicznie dostępne, jak pokazano tutaj:
Rysunek 4. Dostęp publiczny w GCE
Procedura udostępniania elementu publicznie jest opisana w dokumentacji Google Cloud.
DSU z wieloma partycjami w pliku ZIP
Od Androida 11 DSU może mieć więcej niż 1 partycję. Może na przykład zawierać product.img
oprócz system.img
. Podczas uruchamiania urządzenia pierwszy etap init
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 odpowiednika na urządzeniu.
Rysunek 5. Proces DSU z wieloma partycjami
DSU podpisany przez producenta OEM
Aby mieć pewność, że wszystkie obrazy działające 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, który zawiera 2 obrazy partycji, jak poniżej:
dsu.zip {
- system.img
- product.img
}
Zarówno plik system.img
, jak i product.img
muszą być podpisane kluczem OEM, zanim zostaną umieszczone w pliku ZIP. Zwykle używa się algorytmu asymetrycznego, np. RSA, w którym klucz tajny służy do podpisywania pakietu, a klucz publiczny do jego weryfikacji. Pierwszy etap ramdysku musi zawierać klucz publiczny parowania, np. /avb/*.avbpubkey
. Jeśli urządzenie obsługuje już AVB, wystarczy dotychczasowa procedura podpisywania. W kolejnych sekcjach przedstawiamy proces podpisywania i wyjaśniamy, gdzie znajduje się klucz publiczny 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 typy danych.
Po pierwsze, include
zawiera dodatkowe deskryptory JSON lub przekierowuje moduł wczytujący DSU do nowej lokalizacji. Na przykład:
{
"include": ["https://.../gsi-release/gsi-src.json"]
}
Po drugie, do opisywania opublikowanych pakietów DSU używany jest element image
. W elemencie obrazu znajduje się kilka atrybutów:
Atrybuty
name
idetails
to ciągi tekstowe, które są wyświetlane w oknie dialogowym, aby użytkownik mógł je wybrać.Atrybuty
cpu_api
,vndk
ios_version
są używane do sprawdzania zgodności, które opisujemy w następnej sekcji.Opcjonalny atrybut
pubkey
opisuje klucz publiczny, który jest powiązany z kluczem tajnym używanym do podpisywania pakietu DSU. Gdy jest określony, usługa DSU może sprawdzić, czy urządzenie ma klucz używany do weryfikacji pakietu DSU. Zapobiega to instalowaniu nierozpoznanego pakietu DSU, np. instalowaniu 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 usługi, otworzy się okno dialogowe widoczne na ilustracji 6, w którym deweloper zostanie poproszony o zaakceptowanie warunków usługi przed zainstalowaniem pakietu DSU.Rysunek 6. Okno Warunków korzystania z usługi
Oto przykładowy deskryptor 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ślania 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 wymagany i jest porównywany z właściwością systemowąro.product.cpu.abi
. Wartości muszą być dokładnie takie same.os_version
to opcjonalna liczba całkowita określająca wersję Androida. Na przykład w Androidzie 10os_version
to10
, a w Androidzie 11os_version
to11
. Jeśli ten atrybut jest określony, musi być równy lub większy niż właściwość systemowaro.system.build.version.release
. To sprawdzenie zapobiega uruchamianiu obrazu GSI Androida 10 na urządzeniu dostawcy z Androidem 11, co obecnie nie jest obsługiwane. Uruchamianie obrazu GSI Androida 11 na urządzeniu z Androidem 10 jest dozwolone.vndk
to opcjonalna tablica, która określa wszystkie VNDK zawarte w pakiecie DSU. Gdy jest określony, program wczytujący DSU sprawdza, czy numer wyodrębniony z właściwości systemowejro.vndk.version
jest uwzględniony.
Unieważnianie kluczy DSU ze względów bezpieczeństwa
W bardzo rzadkich przypadkach, gdy para kluczy RSA używana do podpisywania obrazów DSU zostanie naruszona, dysk RAM powinien zostać jak najszybciej zaktualizowany, aby usunąć naruszony klucz. Oprócz aktualizowania partycji rozruchowej możesz blokować naruszone klucze za pomocą listy odwołań 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 w obrazach DSU są weryfikowane za pomocą listy odwołań. Jeśli obrazy zawierają odwołany klucz publiczny, proces instalacji DSU zostanie zatrzymany.
Adres URL listy unieważnionych kluczy powinien być adresem URL HTTPS, aby zapewnić bezpieczeństwo. Jest on 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
, czyli lista unieważnień kluczy GSI wydanych przez Google. Ten ciąg zasobu można nakładać i dostosowywać, dzięki czemu producenci OEM, którzy korzystają z funkcji DSU, mogą udostępniać i utrzymywać własną czarną listę kluczy. Dzięki temu producent OEM może blokować niektóre klucze publiczne bez aktualizowania obrazu dysku RAM urządzenia.
Format listy odwołań to:
{
"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 generowanie klucza publicznego AVB.status
wskazuje stan unieważnienia klucza. Obecnie jedyną obsługiwaną wartością jestREVOKED
.reason
to opcjonalny ciąg znaków opisujący przyczynę unieważnienia.
Procedury DSU
W tej sekcji opisujemy, jak wykonać kilka procedur konfiguracji DSU.
Generowanie nowej pary kluczy
Aby wygenerować parę kluczy RSA (prywatny i publiczny) w formacie .pem
, użyj polecenia openssl
(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 x509. Instrukcje generowania 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 dysku RAM.
Aby zweryfikować podpisany pakiet DSU, element oem_cert.avbpubkey
musi być umieszczony w obrębie elementu /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 wykonaj te czynności, aby dodać klucz publiczny do dysku RAM pierwszego etapu.
Dodaj gotowy moduł, aby skopiować
avbpubkey
. Na przykład dodaj poladevice/<company>/<board>/oem_cert.avbpubkey
idevice/<company>/<board>/avb/Android.mk
z treściami w tym stylu: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)
Spraw, aby cel droidcore zależał od dodanego
oem_cert.avbpubkey
:droidcore: oem_cert.avbpubkey
Wygeneruj atrybut klucza publicznego AVB w deskryptorze JSON.
oem_cert.avbpubkey
jest w binarnym formacie klucza publicznego AVB. Użyj SHA-1, aby przed umieszczeniem w deskryptorze JSON uczynić go czytelnym:
$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20
Będzie to zawartość atrybutu pubkey
deskryptora JSON.
"images":[
{
...
"pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
...
},
Podpisywanie pakietu DSU
Aby podpisać pakiet DSU, użyj jednej z tych metod:
Metoda 1. Ponowne użycie artefaktu utworzonego w procesie podpisywania AVB w celu utworzenia pakietu DSU. Innym rozwiązaniem jest wyodrębnienie z pakietu wersji już podpisanych obrazów i użycie ich do bezpośredniego utworzenia pliku ZIP.
Metoda 2. Jeśli klucz prywatny jest dostępny, użyj tych poleceń, aby podpisać partycje DSU. Każdy plik
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
Więcej informacji o dodawaniu add_hashtree_footer
za pomocą avbtool
znajdziesz w artykule Korzystanie z narzędzia avbtool.
Weryfikowanie pakietu DSU lokalnie
Zalecamy sprawdzenie wszystkich lokalnych obrazów za pomocą klucza publicznego 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, który zawiera 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 DSU jednym kliknięciem
Domyślnie program wczytujący DSU wskazuje metadane obrazów GSI, które sąhttps://...google.com/.../gsi-src.json
.
Producenci OEM mogą nadpisać 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 producent OEM może udostępnić metadane JSON, które zawierają GSI oraz obrazy własne producenta 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 w łańcuch, jak pokazano na rysunku 7.
Rysunek 7. Łączenie opublikowanych metadanych DSU