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:
- Pobierz listę obrazów i odpowiedni adres URL według schematu zdefiniowanego przez dostawcę.
- Dopasuj obrazy na liście do urządzenia i pokaż zgodne obrazy, które użytkownik może wybrać.
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.
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ń.
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.
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:
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.
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
idetails
to ciągi znaków wyświetlane w oknie dialogowym, które użytkownik może wybrać.Atrybuty
cpu_api
,vndk
ios_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.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 10os_version
wynosi10
, a w przypadku Androida 11os_version
wynosi11
. 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 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 systemowejro.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ą jestREVOKED
. -
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.
Dodaj wstępnie zbudowany moduł, aby skopiować plik
avbpubkey
. Na przykład dodajdevice/<company>/<board>/oem_cert.avbpubkey
idevice/<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)
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.
Rysunek 7. Łączenie opublikowanych metadanych DSU