Zmniejsz rozmiar OTA

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ędzie bsdiff 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 findmake 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:

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

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:

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:

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 dir1dir2 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.