Na tej stronie opisujemy zmiany dodane do AOSP, aby ograniczyć niepotrzebne zmiany w plikach między kompilacjami. Producenci urządzeń, którzy utrzymują własne systemy kompilacji, mogą wykorzystać te informacje jako wskazówki dotyczące zmniejszania rozmiaru aktualizacji bezprzewodowych (OTA).
Aktualizacje OTA na Androida czasami zawierają zmienione pliki, które nie odpowiadają zmianom w kodzie. Są to artefakty systemu kompilacji. Może się to zdarzyć, gdy ten sam kod, skompilowany w różnych momentach, w różnych katalogach lub na różnych komputerach, generuje dużą liczbę zmienionych plików. Takie nadmiarowe pliki zwiększają rozmiar poprawki OTA i utrudniają określenie, który kod został zmieniony.
Aby zwiększyć przejrzystość zawartości aktualizacji OTA, AOSP zawiera zmiany w systemie kompilacji, które mają na celu zmniejszenie rozmiaru poprawek OTA. Niepotrzebne zmiany w plikach między kompilacjami zostały wyeliminowane, a aktualizacje OTA zawierają tylko pliki związane z łatkami. AOSP zawiera też narzędzie do porównywania kompilacji, które odfiltrowuje typowe zmiany w plikach związane z kompilacją, aby zapewnić bardziej przejrzyste porównanie plików kompilacji, oraz narzędzie do mapowania bloków, które pomaga zachować spójność przydzielania bloków.
System kompilacji może tworzyć niepotrzebnie duże poprawki na kilka sposobów. Aby temu zapobiec, w Androidzie 8.0 i nowszych wersjach wprowadziliśmy nowe funkcje, które zmniejszają rozmiar poprawki dla każdej różnicy w pliku. Udoskonalenia, które zmniejszyły rozmiar pakietów aktualizacji OTA, obejmują:
-
Używanie ZSTD, uniwersalnego algorytmu bezstratnej kompresji pełnych obrazów w przypadku aktualizacji urządzeń innych niż A/B. Algorytm ZSTD można dostosować, aby uzyskać wyższe współczynniki kompresji, zwiększając poziom kompresji. Poziom kompresji jest ustawiany podczas generowania aktualizacji OTA i można go określić, przekazując flagę
--vabc_compression_param=zstd,$COMPRESSION_LEVEL
-
Zwiększenie rozmiaru okna kompresji używanego podczas aktualizacji OTA. Maksymalny rozmiar okna kompresji można ustawić, dostosowując parametr kompilacji w pliku
.mk
urządzenia. Ta zmienna jest ustawiona jakoPRODUCT_VIRTUAL_AB_COMPRESSION_FACTOR := 262144
- Korzystanie z narzędzia Puffin do ponownej kompresji, które jest deterministycznym narzędziem do tworzenia poprawek strumieni deflate i obsługuje funkcje kompresji i różnicowania na potrzeby generowania aktualizacji OTA w ramach testów A/B.
-
Zmiany w sposobie używania narzędzia do generowania różnic, np. w sposobie używania biblioteki
bsdiff
do kompresowania poprawek. W Androidzie 9 i nowszych narzędziebsdiff
wybiera algorytm kompresji, który zapewnia najlepsze wyniki kompresji w przypadku poprawki. -
Ulepszenia w zakresie
update_engine
spowodowały, że podczas stosowania poprawek w przypadku aktualizacji urządzeń w ramach testów A/B zużywana jest mniejsza ilość pamięci.
W kolejnych sekcjach omówimy różne problemy wpływające na rozmiary aktualizacji OTA, ich rozwiązania i przykłady implementacji w AOSP.
Kolejność plików
Problem: systemy plików nie gwarantują kolejności plików, gdy proszą o listę plików w katalogu, chociaż zwykle jest ona taka sama w przypadku tego samego wyewidencjonowania. Narzędzia takie jak ls
domyślnie sortują wyniki, ale funkcja wieloznaczna używana przez polecenia takie jak find
i make
nie sortuje wyników. Zanim użyjesz tych narzędzi, musisz posortować dane wyjściowe.
Rozwiązanie: gdy używasz narzędzi takich jak find
i make
z funkcją wieloznaczną, przed ich użyciem posortuj dane wyjściowe tych poleceń. Jeśli w plikach $(wildcard)
lub $(shell find)
używasz znaków Android.mk
, posortuj je również. Niektóre narzędzia, np. Java, sortują dane wejściowe, więc przed posortowaniem plików sprawdź, czy używane narzędzie nie zrobiło tego już wcześniej.
Przykłady: wiele przypadków zostało naprawionych w podstawowym systemie kompilacji za pomocą wbudowanego makra all-*-files-under
, które zawiera all-cpp-files-under
(ponieważ kilka definicji było rozproszonych w innych plikach makefile).
Więcej informacji znajdziesz w tych 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ą tworzone elementy, może spowodować, że pliki binarne będą się różnić. Większość ścieżek w kompilacji Androida to ścieżki względne, więc __FILE__
w C/C++ nie stanowi problemu. Symbole debugowania domyślnie kodują jednak pełną ścieżkę, a .note.gnu.build-id
jest generowany na podstawie skrótu wstępnie usuniętego pliku binarnego, więc zmieni się, jeśli zmienią 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 danych wyjściowych kompilacji powodują niepotrzebne zmiany w plikach. Prawdopodobnie będzie to rozległa, rzadko występująca powódź. Jest ona prognozowana w tych lokalizacjach:
__DATE__/__TIME__/__TIMESTAMP__
makra w kodzie C lub C++;- Sygnatury czasowe osadzone w archiwach ZIP.
Rozwiązania/przykłady: aby usunąć sygnatury czasowe z danych wyjściowych kompilacji, postępuj zgodnie z instrukcjami podanymi poniżej w sekcjach __DATE__/__TIME__/__TIMESTAMP__ w C/C++ i Osadzone sygnatury czasowe w archiwach.
__DATE__/__TIME__/__TIMESTAMP__ w C/C++
Te makra zawsze generują różne dane wyjściowe w przypadku różnych kompilacji, więc nie używaj ich. Oto kilka opcji eliminowania tych makr:
- Usuń je. Przykład znajdziesz na stronie 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 został utworzony system operacyjny, przeczytaj
ro.build.date
(dotyczy to wszystkich wersji z wyjątkiem wersji przyrostowych, w których ta data może nie być aktualizowana). Przykład znajdziesz na stronie https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84.
Sygnatury czasowe wbudowane w archiwach (zip, jar)
W Androidzie 7.0 rozwiązano problem z osadzonymi sygnaturami czasowymi w archiwach ZIP, dodając parametr
-X
do wszystkich zastosowań polecenia zip
. Spowodowało to usunięcie z pliku ZIP identyfikatora UID/GID
kompilatora i rozszerzonej sygnatury czasowej w formacie Unix.
Nowe narzędzie ziptime
(znajdujące się w /platform/build/+/android16-release/tools/ziptime/
) resetuje normalne sygnatury czasowe w nagłówkach plików 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 wersji
Problem: ciągi wersji APK często miały dołączony znak BUILD_NUMBER
do zakodowanych na stałe wersji. Nawet jeśli w pliku APK nic innego się nie zmieni, będzie on inny.
Rozwiązanie: usuń numer kompilacji z ciągu wersji APK.
Przykłady:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
Włączanie obliczania weryfikacji na urządzeniu
Jeśli na urządzeniu jest włączona funkcja dm-verity, narzędzia OTA automatycznie wykrywają konfigurację weryfikacji i włączają obliczenia weryfikacyjne na urządzeniu. Umożliwia to obliczanie bloków weryfikacji na urządzeniach z Androidem zamiast przechowywania ich jako surowych bajtów w pakiecie OTA. Bloki weryfikacji mogą zajmować około 16 MB w przypadku partycji o rozmiarze 2 GB.
Obliczenie wiarygodności na urządzeniu może jednak zająć dużo czasu. W szczególności kod korekcji błędów może zająć dużo czasu. Na urządzeniach Pixel trwa to zwykle do 10 minut. Na urządzeniach niższej klasy może to potrwać dłużej. Jeśli chcesz wyłączyć obliczenia weryfikacji na urządzeniu, ale nadal włączyć dm-verity, możesz to zrobić, przekazując --disable_fec_computation
do narzędzia ota_from_target_files
podczas generowania aktualizacji OTA. Ten flag wyłącza obliczanie weryfikacji na urządzeniu podczas aktualizacji OTA.
Skraca czas instalacji OTA, ale zwiększa rozmiar pakietu OTA. Jeśli na urządzeniu nie jest włączona weryfikacja dm-verity, przekazanie tego flagi nie ma żadnego wpływu.
Spójne narzędzia do kompilacji
Problem: narzędzia, które generują zainstalowane pliki, muszą być spójne (dane wejściowe powinny zawsze dawać te same dane wyjściowe).
Rozwiązania/przykłady: zmiany były wymagane w tych narzędziach do kompilacji:
- Tworzenie pliku NOTICE. Zmieniliśmy sposób tworzenia pliku NOTICE, aby można było tworzyć powtarzalne kolekcje NOTICE. Zobacz CL:https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64.
- Java Android Compiler Kit (Jack). Łańcuch narzędzi Jack wymagał aktualizacji, aby obsługiwać sporadyczne zmiany w kolejności generowanych konstruktorów. Do łańcucha narzędzi dodano deterministyczne akcesory konstruktorów:https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b.
- kompilator ART AOT (dex2oat). Binarny kompilator ART został zaktualizowany. Dodano opcję tworzenia deterministycznego obrazu: 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 wstępnej optymalizacji aplikacji (.odex). Pliki przed optymalizacją DEX (.odex) zawierały niezainicjowane wypełnienie w systemach 64-bitowych. Zostało to poprawione:https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029.
Używanie narzędzia do porównywania wersji
W przypadkach, w których nie można wyeliminować zmian w plikach związanych z kompilacją, AOSP zawiera narzędzie do porównywania kompilacjitarget_files_diff.py
, które służy do porównywania dwóch pakietów plików. To narzędzie wykonuje rekursywne porównanie dwóch kompilacji, wykluczając typowe zmiany w plikach związane z kompilacją, takie jak
- Oczekiwane zmiany w danych wyjściowych kompilacji (np. z powodu zmiany numeru kompilacji).
- Zmiany wynikające ze znanych problemów w bieżącym systemie kompilacji.
Aby użyć narzędzia do porównywania kompilacji, uruchom to polecenie:
target_files_diff.py dir1 dir2
dir1
i dir2
to katalogi podstawowe, które zawierają wyodrębnione pliki docelowe dla każdej kompilacji.
Zachowaj spójność przydzielania bloków
W przypadku danego pliku, mimo że jego zawartość pozostaje taka sama w dwóch kompilacjach, rzeczywiste bloki, które zawierają dane, mogą się zmienić. W rezultacie program do aktualizacji musi wykonywać niepotrzebne operacje wejścia/wyjścia, aby przenieść bloki w ramach aktualizacji OTA.
W przypadku aktualizacji OTA w ramach wirtualnego testu A/B niepotrzebne operacje wejścia/wyjścia mogą znacznie zwiększyć ilość miejsca na dane potrzebną do przechowywania migawki copy-on-write. W przypadku aktualizacji OTA bez testów A/B przenoszenie bloków w celu przeprowadzenia aktualizacji OTA wydłuża czas aktualizacji, ponieważ ze względu na przenoszenie bloków występuje więcej operacji wejścia/wyjścia.
Aby rozwiązać ten problem, w Androidzie 7.0 Google rozszerzył narzędzie make_ext4fs
, które zapewnia spójność przydzielania bloków w różnych kompilacjach. Narzędzie make_ext4fs
akceptuje opcjonalną flagę -d base_fs
, która podczas generowania obrazu ext4
próbuje przypisać pliki do tych samych bloków. Możesz wyodrębnić pliki mapowania bloków (np. base_fs
pliki map) z pliku ZIP z plikami docelowymi poprzedniej kompilacji. Dla każdej partycji ext4
w katalogu IMAGES
znajduje się plik .map
(np. IMAGES/system.map
odpowiada partycji system
). Te pliki base_fs
można następnie zarejestrować 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 zmniejsza to ogólnego rozmiaru pakietu OTA, ale poprawia wydajność aktualizacji OTA, ponieważ zmniejsza ilość operacji wejścia/wyjścia. W przypadku aktualizacji wirtualnych A/B znacznie zmniejsza ilość miejsca potrzebnego do zastosowania aktualizacji OTA.
Unikanie aktualizowania aplikacji
Oprócz minimalizowania różnic w kompilacjach możesz zmniejszyć rozmiar aktualizacji OTA, wykluczając aktualizacje aplikacji, które są aktualizowane w sklepach z aplikacjami. Pliki APK często zajmują znaczną część różnych partycji na urządzeniu. Uwzględnianie w aktualizacji OTA najnowszych wersji aplikacji, które są aktualizowane przez sklepy z aplikacjami, może znacznie zwiększyć rozmiar pakietów OTA i nie przynosić użytkownikom większych korzyści. Gdy użytkownicy otrzymają pakiet OTA, mogą już mieć zaktualizowaną aplikację lub jeszcze nowszą wersję pobraną bezpośrednio ze sklepów z aplikacjami.