Ta strona opisuje zmiany wprowadzone w AOSP, aby ograniczyć niepotrzebne zmiany plików między kompilacjami. Implementatorzy urządzeń, którzy utrzymują własne systemy kompilacji, mogą korzystać z tych informacji jako wskazówki do zmniejszania rozmiaru aktualizacji OTA.
Aktualizacje OTA Androida czasami zawierają zmienione pliki, które nie odpowiadają zmianom w kodzie. To tak naprawdę elementy systemu. Może się tak zdarzyć, gdy ten sam kod skompilowany w różnych momentach, z różnych katalogów lub na różnych maszynach wygeneruje dużą liczbę zmienionych plików. Takie zbędne pliki zwiększają rozmiar poprawki OTA i utrudniają określenie, który kod się zmienił.
Aby zwiększyć przejrzystość treści OTA, AOSP zawiera zmiany w systemie kompilacji, które mają na celu zmniejszenie rozmiaru poprawek OTA. Niepotrzebne zmiany plików między kompilacjami zostały usunięte, a aktualizacje OTA zawierają tylko pliki związane z łatkami. AOSP zawiera też narzędzie do porównywania wersji kompilacji, które odfiltrowuje typowe zmiany plików związane z kompilacją, aby zapewnić czystsze porównanie plików kompilacji, oraz narzędzie do mapowania bloków, które pomaga zachować spójność przy przydzielaniu bloków.
System kompilacji może tworzyć niepotrzebnie duże łaty w kilka sposobów. Aby temu zaradzić, w Androidzie 8.0 i nowszych wprowadziliśmy nowe funkcje, które zmniejszają rozmiar poprawek dla każdego pliku. Ulepszone funkcje, które zmniejszyły rozmiary pakietów aktualizacji OTA:
-
Używanie ZSTD, czyli uniwersalnego algorytmu bezstratnej kompresji przeznaczonego do pełnych obrazów w przypadku aktualizacji innych niż A/B. ZSTD można dostosować do wyższych współczynników kompresji, zwiększając poziom kompresji. Poziom kompresji jest ustawiany podczas generowania OTA. Można go ustawić, przekazując flagę.
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
-
Zwiększenie rozmiaru okna kompresji używanego podczas OTA. Maksymalny rozmiar okna kompresji można ustawić, dostosowując parametr kompilacji w pliku
.mk
urządzenia. Ta zmienna jest ustawiona jako:PRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
- Używanie narzędzia Puffin do ponownego kompresowania, czyli narzędzia do deterministycznego łatania strumieni deflate, które obsługuje funkcje kompresji i różnicowania na potrzeby generowania aktualizacji OTA A/B.
-
Zmiany w użyciu narzędzia do generowania delty, np. sposób korzystania z biblioteki
bsdiff
do kompresowania poprawek. W Androidzie 9 i nowszych narzędziebsdiff
wybiera algorytm kompresji, który zapewni najlepsze wyniki kompresji dla danego pakietu poprawek. -
Ulepszenia w komponencie
update_engine
spowodowały, że podczas stosowania poprawek w ramach aktualizacji A/B na urządzeniach zużywa się mniej pamięci.
W następnych sekcjach omawiamy różne problemy, które wpływają na rozmiary aktualizacji OTA, ich rozwiązania oraz przykłady implementacji w AOSP.
Zlecenie
Problem: systemy plików nie gwarantują kolejności plików w przypadku żądania listy plików w katalogu, chociaż zwykle jest ona taka sama w przypadku tego samego procesu płatności. Narzędzia takie jak ls
domyślnie sortują wyniki, ale funkcja z symbolem wieloznacznym używana przez polecenia takie jak find
i make
nie sortuje. Zanim użyjesz tych narzędzi, musisz posortować dane wyjściowe.
Rozwiązanie: jeśli używasz narzędzi takich jak find
i make
z funkcją wieloznaczną, przed użyciem tych poleceń posortuj ich dane wyjściowe. Jeśli używasz w plikach Android.mk
znaków $(wildcard)
lub $(shell find)
, posortuj je. Niektóre narzędzia, takie jak Java, sortują dane wejściowe, więc zanim je posortujesz, sprawdź, czy nie zrobił tego już używane przez Ciebie narzędzie.
Przykłady: wiele wystąpień zostało poprawionych w głównym systemie kompilacji za pomocą wbudowanego makra all-*-files-under
, które obejmuje all-cpp-files-under
(ponieważ kilka definicji zostało rozproszonych w innych plikach make).
Szczegółowe informacje znajdziesz w artykułach:
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
Katalog kompilacji
Problem: zmiana katalogu, w którym są kompilowane pliki, może spowodować, że pliki binarne będą inne. Większość ścieżek w kompilacji na Androida to ścieżki względne, więc __FILE__
w C/C++ nie stanowi problemu. Jednak symbole debugowania domyślnie kodują pełną ścieżkę, a wartość .note.gnu.build-id
jest generowana na podstawie haszowania wcześniej usuniętego binarnego pliku, więc zmienia się, gdy zmieniają się symbole debugowania.
Rozwiązanie: AOSP teraz tworzy ścieżki debugowania względne. Szczegółowe informacje znajdziesz w CL: https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02.
Sygnatury czasowe
Problem: sygnatury czasowe w wyniku kompilacji powodują niepotrzebne zmiany w pliku. Może się to zdarzyć w tych lokalizacjach:
__DATE__/__TIME__/__TIMESTAMP__
makro w kodzie C lub C++.- sygnatury czasowe zawarte w archiwach ZIP;
Rozwiązania i przykłady: aby usunąć sygnatury czasowe z wyjścia kompilacji, skorzystaj z instrukcji podanych poniżej w sekcji __DATE__/__TIME__/__TIMESTAMP__ w C/C++ i Umieszczone w archiwach sygnatury czasowe.
__DATE__/__TIME__/__TIMESTAMP__ w C/C++
Makra te zawsze dają różne wyniki w przypadku różnych wersji, więc nie należy ich używać. Oto kilka sposobów na wyeliminowanie tych makr:
- Usuń je. Przykład: https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f.
- Aby jednoznacznie zidentyfikować uruchomiony plik binarny, odczytaj identyfikator kompilacji z nagłówka ELF.
-
Aby dowiedzieć się, kiedy system operacyjny został utworzony, przeczytaj
ro.build.date
(dotyczy to wszystkich wersji, z wyjątkiem wersji przyrostowych, które mogą nie aktualizować tej daty). Przykład: https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84.
Wbudowane sygnatury czasowe w archiwach (zip, jar)
W Androidzie 7.0 rozwiązaliśmy problem z osadzonymi sygnaturami czasowymi w archiwach ZIP, dodając -X
do wszystkich zastosowań polecenia zip
. Z pliku ZIP usunięto identyfikator UID/GID kompilatora oraz rozszerzoną sygnaturę czasową Unixa.
Nowe narzędzie ziptime
(znajdujące się w /platform/build/+/android16-release/tools/ziptime/
) resetuje normalne sygnatury czasowe w nagłówkach ZIP. Szczegółowe informacje znajdziesz w pliku README.
Narzędzie signapk
ustawia sygnatury czasowe plików APK, które mogą się różnić w zależności od strefy czasowej serwera. Szczegółowe informacje znajdziesz w CL
https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.
Narzędzie signapk
ustawia sygnatury czasowe plików APK, które mogą się różnić w zależności od strefy czasowej serwera. Szczegółowe informacje znajdziesz w CL https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028.
Ciągi tekstowe dotyczące wersji
Problem: ciągi znaków wersji pliku APK często zawierały BUILD_NUMBER
dodane do zakodowanych wersji. Nawet jeśli w pliku APK nie zmieni się nic poza tym, będzie on nadal inny.
Rozwiązanie: usuń numer kompilacji z ciągu znaków wersji pliku APK.
Przykłady:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
Włączanie weryfikacji na urządzeniu
Jeśli na urządzeniu jest włączona funkcja dm-verity, narzędzia OTA automatycznie pobierają konfigurację verity i włączają obliczanie verity na urządzeniu. Dzięki temu bloki weryfikacji są obliczane na urządzeniach z Androidem, zamiast być przechowywane jako surowe bajty w pakiecie OTA. Bloki Verity mogą zajmować około 16 MB na partycji 2 GB.
Obliczanie weryfikacji na urządzeniu może jednak zająć dużo czasu. W szczególności kod korekcji błędów w ramach przesyłania do przodu może zająć dużo czasu. Na urządzeniach Pixel trwa to zwykle do 10 minut. Na urządzeniach niskiej klasy może to potrwać dłużej. Jeśli chcesz wyłączyć obliczenia weryfikacji na urządzeniu, ale nadal chcesz włączyć dm-verity, możesz to zrobić, przekazując parametr --disable_fec_computation
do narzędzia ota_from_target_files
podczas generowania aktualizacji OTA. Ten parametr wyłącza weryfikację na urządzeniu podczas aktualizacji OTA.
Zmniejsza czas instalacji OTA, ale zwiększa rozmiar pakietu OTA. Jeśli na urządzeniu nie włączono funkcji dm-verity, przekazanie tej flagi nie przyniesie żadnego efektu.
spójne narzędzia do kompilacji,
Problem: narzędzia generujące zainstalowane pliki muszą być spójne (dany dane wejściowe powinny zawsze dawać ten sam wynik).
Rozwiązania/przykłady: wymagane były zmiany w tych narzędziach do kompilacji:
- Tworzenie pliku z komunikatem. Zmieniliśmy twórcę pliku NOTICE, aby umożliwić tworzenie powtarzalnych kolekcji NOTICE. Zobacz CL: https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack). Zestaw narzędzi Jack wymagał aktualizacji, aby obsługiwać sporadyczne zmiany w generowanym porządku konstruktorów. Do łańcucha narzędzi dodano deterministyczne metody dostępu do konstruktorów: https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- Kompilator ART AOT (dex2oat). Kompilator ART w wersji binarnej otrzymał aktualizację, która dodała opcję tworzenia obrazu deterministycznego: https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9.
-
Plik libpac.so (V8). Każda kompilacja tworzy inny plik
/system/lib/libpac.so
, ponieważ migawka V8 zmienia się w przypadku każdej kompilacji. Rozwiązaniem było usunięcie zrzutu: https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29. - pliki aplikacji w formie pre-dexopt (.odex). Pliki przed dexopt (.odex) zawierały nieinicjalizowaną wypełnienia w systemach 64-bitowych. Zostało to poprawione: https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
Używanie narzędzia do porównywania wersji
W przypadku, gdy nie można wyeliminować zmian w plikach związanych z kompilacją, AOSP zawiera narzędzie do porównywania kompilacji (target_files_diff.py
), które można wykorzystać do porównywania 2 pakietów plików. To narzędzie wykonuje rekurencyjne porównanie dwóch kompilacji, z wyłączeniem typowych zmian plików związanych z kompilacją, takich jak
- oczekiwane zmiany w wyniku kompilacji (np. z powodu zmiany numeru kompilacji).
- Zmiany z powodu znanych problemów w bieżącym systemie kompilacji.
Aby użyć narzędzia do porównywania wersji, uruchom to polecenie:
target_files_diff.py dir1 dir2
Katalogi dir1
i dir2
to katalogi podstawowe, które zawierają wyodrębnione pliki docelowe dla każdej kompilacji.
Zachowaj spójność przy przydzielaniu bloków
Chociaż zawartość danego pliku pozostaje taka sama w 2 wersjach, bloki zawierające dane mogły się zmienić. W rezultacie modyfikator musi przeprowadzić niepotrzebne operacje wejścia/wyjścia, aby przesunąć bloki w ramach aktualizacji OTA.
W przypadku aktualizacji OTA z użyciem kopii A/B niepotrzebne operacje wejścia/wyjścia mogą znacznie zwiększyć ilość miejsca na dane wymaganą do przechowywania kopii na potrzeby zapisu. W przypadku aktualizacji OTA bez A/B przemieszczanie bloków w ramach aktualizacji OTA wydłuża czas aktualizacji, ponieważ przemieszczanie bloków powoduje więcej operacji wejścia/wyjścia.
Aby rozwiązać ten problem, w Androidzie 7.0 rozszerzyliśmy narzędzie make_ext4fs
, aby zapewnić spójność przydziału bloków w różnych wersjach. Narzędzie make_ext4fs
akceptuje opcjonalny parametr -d base_fs
, który próbuje przypisać pliki do tych samych bloków podczas generowania obrazu ext4
. Pliki mapowania bloków (np. pliki mapy base_fs
) możesz wyodrębnić z pliku ZIP z plikami docelowymi z poprzedniej kompilacji. W przypadku każdej partycji ext4
w katalogu IMAGES
znajduje się plik .map
(na przykład IMAGES/system.map
odpowiada partycji system
). Te pliki base_fs
można następnie zaimportować i określić za pomocą elementu PRODUCT_<partition>_BASE_FS_PATH
, jak w tym przykładzie:
PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map
Nie pomaga to w zmniejszeniu ogólnego rozmiaru pakietu OTA, ale poprawia wydajność aktualizacji OTA poprzez zmniejszenie ilości operacji wejścia/wyjścia. W przypadku aktualizacji A/B w trybie wirtualnym znacznie zmniejsza ilość miejsca na dane potrzebną do zastosowania aktualizacji OTA.
Unikaj aktualizowania aplikacji
Oprócz minimalizowania różnic w kompilacji możesz zmniejszyć rozmiar aktualizacji OTA, wykluczając aktualizacje dotyczące aplikacji, które są aktualizowane w sklepach z aplikacjami. Pliki APK często zajmują znaczną część różnych partycji na urządzeniu. Uwzględnianie najnowszych wersji aplikacji, które są aktualizowane przez sklepy z aplikacjami, w ramach aktualizacji OTA może mieć duży wpływ na rozmiar pakietów OTA i nie przynosić użytkownikom większych korzyści. Zanim użytkownicy otrzymają pakiet OTA, mogą już mieć zaktualizowaną aplikację lub jeszcze nowszą wersję, którą pobrali bezpośrednio ze sklepu z aplikacjami.