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ów w zamknię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:
- Pobieranie listy obrazów i odpowiadających im adresów URL za pomocą schematu określonego przez dostawcę.
- Dopasuj obrazy z listy do urządzenia i wyświetlaj zgodne obrazy, które użytkownik może wybrać.
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.
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ń.
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.
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:
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.
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
idetails
to ciągi tekstowe wyświetlane w oknie, które użytkownik może wybrać.Atrybuty
cpu_api
,vndk
ios_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.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
to10
, a w Androidzie 11 –os_version
to11
. Jeśli jest określony ten atrybut, musi on być równy lub większy od właściwości systemowejro.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 systemowejro.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ą jestREVOKED
.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.
Dodaj gotowy moduł, aby skopiować
avbpubkey
. Na przykład dodaj poladevice/<company>/<board>/oem_cert.avbpubkey
idevice/<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)
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.
Rysunek 7. Łańcuchowanie opublikowanych metadanych DSU