Zmniejsz rozmiar OTA

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 jako PRODUCT_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ędzie bsdiff wybiera algorytm kompresji, który zapewnia najlepsze wyniki kompresji w przypadku poprawki.
  • Ulepszenia w zakresie update_enginespowodował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 findmake nie sortuje wyników. Zanim użyjesz tych narzędzi, musisz posortować dane wyjściowe.

Rozwiązanie: gdy używasz narzędzi takich jak findmake 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:

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++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:

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:

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:

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.