Buforowanie plików APK

W tym dokumencie opisujemy projekt rozwiązania do buforowania plików APK, które umożliwia szybkie instalowanie wstępnie załadowanych aplikacji na urządzeniu obsługującym partycje A/B.

Producenci OEM mogą umieszczać wstępnie załadowane i popularne aplikacje w pamięci podręcznej APK przechowywanej w głównie pustej partycji B na nowych urządzeniach z partycjami A/B bez wpływu na przestrzeń danych widoczną dla użytkownika. Dzięki temu, że na urządzeniu jest dostępna pamięć podręczna APK, nowe urządzenia lub te, które zostały niedawno przywrócone do ustawień fabrycznych, są gotowe do użycia niemal natychmiast, bez konieczności pobierania plików APK z Google Play.

Przypadki użycia

  • Przechowywanie preinstalowanych aplikacji na partycji B w celu szybszej konfiguracji
  • Przechowywanie popularnych aplikacji w partycji B w celu szybszego przywracania

Wymagania wstępne

Aby korzystać z tej funkcji, urządzenie musi:

  • zainstalowana wersja Androida 8.1 (O MR1),
  • Wdrożono partycję A/B

Wstępnie załadowane treści można skopiować tylko podczas pierwszego uruchomienia. Dzieje się tak, ponieważ na urządzeniach obsługujących aktualizacje systemu A/B partycja B nie przechowuje plików obrazu systemu, ale wstępnie załadowane treści, takie jak zasoby wersji demonstracyjnej, pliki OAT i pamięć podręczna APK. Po skopiowaniu zasobów do partycji /data (co następuje przy pierwszym uruchomieniu) partycja B będzie używana przez aktualizacje bezprzewodowe (OTA) do pobierania zaktualizowanych wersji obrazu systemu.

Dlatego pamięci podręcznej APK nie można zaktualizować za pomocą OTA. Można ją wstępnie załadować tylko w fabryce. Przywracanie ustawień fabrycznych dotyczy tylko partycji /data. Partycja B systemu nadal zawiera wstępnie załadowane treści, dopóki nie zostanie pobrany obraz OTA. Po przywróceniu ustawień fabrycznych system ponownie przejdzie przez pierwszy rozruch. Oznacza to, że buforowanie plików APK nie jest dostępne, jeśli obraz OTA zostanie pobrany do partycji B, a następnie urządzenie zostanie przywrócone do ustawień fabrycznych.

Implementacja

Sposób 1. Treści na partycji system_other

Zaleta: wstępnie załadowane treści nie zostaną utracone po przywróceniu urządzenia do ustawień fabrycznych – po ponownym uruchomieniu zostaną skopiowane z partycji B.

Wada: wymaga miejsca na partycji B. Uruchomienie po przywróceniu ustawień fabrycznych wymaga dodatkowego czasu na skopiowanie wstępnie załadowanych treści.

Aby podczas pierwszego uruchomienia skopiować wstępnie załadowane aplikacje, system wywołuje skrypt w /system/bin/preloads_copy.sh. Skrypt jest wywoływany z jednym argumentem (ścieżką do punktu montowania tylko do odczytu dla partycji system_b):

Aby wdrożyć tę funkcję, wprowadź te zmiany dotyczące urządzenia. Oto przykład z Marlina:

  1. Dodaj skrypt, który wykonuje kopiowanie, do pliku device-common.mk (w tym przypadku device/google/marlin/device-common.mk) w ten sposób:
    # Script that copies preloads directory from system_other to data partition
    PRODUCT_COPY_FILES += \
        device/google/marlin/preloads_copy.sh:system/bin/preloads_copy.sh
    
    Przykładowy skrypt znajdziesz w tym miejscu: device/google/marlin/preloads_copy.sh
  2. Zmodyfikuj plik init.common.rc, aby utworzyć wymagany katalog /data/preloads i podkatalogi:
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    Przykład źródła pliku init znajdziesz w tym miejscu: device/google/marlin/init.common.rc
  3. Zdefiniuj nową domenę SELinux w pliku preloads_copy.te:
    type preloads_copy, domain, coredomain;
    type preloads_copy_exec, exec_type, vendor_file_type, file_type;
    
    init_daemon_domain(preloads_copy)
    
    allow preloads_copy shell_exec:file rx_file_perms;
    allow preloads_copy toolbox_exec:file rx_file_perms;
    allow preloads_copy preloads_data_file:dir create_dir_perms;
    allow preloads_copy preloads_data_file:file create_file_perms;
    allow preloads_copy preloads_media_file:dir create_dir_perms;
    allow preloads_copy preloads_media_file:file create_file_perms;
    
    # Allow to copy from /postinstall
    allow preloads_copy system_file:dir r_dir_perms;
    
    Przykładowy plik domeny SELinux znajdziesz tutaj: /device/google/marlin/+/android16-release/sepolicy/preloads_copy.te
  4. Zarejestruj domenę w nowym pliku /sepolicy/file_contexts:
    /system/bin/preloads_copy\.sh     u:object_r:preloads_copy_exec:s0
    
    Przykład pliku kontekstów SELinux znajdziesz tutaj: device/google/marlin/sepolicy/preloads_copy.te
  5. Podczas kompilacji katalog z preinstalowanymi treściami musi zostać skopiowany na partycję system_other:
    # Copy contents of preloads directory to system_other partition
    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,vendor/google_devices/marlin/preloads,system_other/preloads)
    
    To przykład zmiany w pliku Makefile, która umożliwia kopiowanie zasobów pamięci podręcznej APK z repozytorium Git dostawcy (w naszym przypadku było to vendor/google_devices/marlin/preloads) do lokalizacji na partycji system_other, która zostanie później skopiowana do /data/preloads, gdy urządzenie uruchomi się po raz pierwszy. Ten skrypt jest uruchamiany w czasie kompilacji, aby przygotować obraz system_other. Oczekuje, że wstępnie załadowane treści będą dostępne w folderze vendor/google_devices/marlin/preloads. OEM może wybrać rzeczywistą nazwę lub ścieżkę repozytorium.
  6. Pamięć podręczna APK znajduje się w /data/preloads/file_cache i ma ten układ:
    /data/preloads/file_cache/
        app.package.name.1/
              file1
              fileN
        app.package.name.N/
    
    To jest ostateczna struktura katalogów na urządzeniach. Producenci OEM mogą wybrać dowolne podejście do wdrożenia, o ile końcowa struktura pliku będzie zgodna z opisaną powyżej.

Sposób 2. Treści na danych użytkownika obraz wgrany w fabryce

To alternatywne podejście zakłada, że wstępnie załadowane treści są już zawarte w katalogu /data/preloads na partycji /data.

Zaleta: działa od razu po wyjęciu z pudełka – nie trzeba dostosowywać urządzenia, aby skopiować pliki przy pierwszym uruchomieniu. Treści znajdują się już w podziale /data.

Wada: po przywróceniu ustawień fabrycznych wstępnie załadowane treści zostaną utracone. Chociaż w niektórych przypadkach może to być dopuszczalne, nie zawsze sprawdza się w przypadku producentów OEM, którzy po kontroli jakości przywracają urządzenia do ustawień fabrycznych.

Do interfejsu android.content.Context dodano nową metodę @SystemApi: getPreloadsFileCache(). Zwraca ścieżkę bezwzględną do katalogu specyficznego dla aplikacji w wstępnie załadowanej pamięci podręcznej.

Dodaliśmy nową metodę IPackageManager.deletePreloadsFileCache, która umożliwia usunięcie katalogu wstępnego wczytywania, aby odzyskać całą przestrzeń. Metoda może być wywoływana tylko przez aplikacje z identyfikatorem SYSTEM_UID, czyli serwer systemowy lub Ustawienia.

Przygotowanie aplikacji

Dostęp do katalogu pamięci podręcznej wstępnego wczytywania mają tylko aplikacje z uprawnieniami. Aby uzyskać taki dostęp, aplikacje muszą być zainstalowane w katalogu /system/priv-app.

Weryfikacja

  • Po pierwszym uruchomieniu urządzenia w katalogu /data/preloads/file_cache powinny znajdować się treści.
  • Jeśli na urządzeniu zaczyna brakować miejsca, zawartość katalogu file_cache/ musi zostać usunięta.

Do testowania pamięci podręcznej APK użyj przykładowej aplikacji ApkCacheTest.

  1. Skompiluj aplikację, uruchamiając to polecenie w katalogu głównym:
    make ApkCacheTest
    
  2. Zainstaluj aplikację jako aplikację uprzywilejowaną. (Pamiętaj, że tylko aplikacje uprzywilejowane mają dostęp do pamięci podręcznej APK). Wymaga to urządzenia z dostępem do roota:
    adb root && adb remount
    adb shell mkdir /system/priv-app/ApkCacheTest
    adb push $ANDROID_PRODUCT_OUT/data/app/ApkCacheTest/ApkCacheTest.apk /system/priv-app/ApkCacheTest/
    adb shell stop && adb shell start
    
  3. W razie potrzeby symuluj katalog pamięci podręcznej plików i jego zawartość (wymaga to też uprawnień roota):
    adb shell mkdir -p /data/preloads/file_cache/com.android.apkcachetest
    adb shell restorecon -r /data/preloads
    adb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt"
    
  4. Przetestuj aplikację. Po zainstalowaniu aplikacji i utworzeniu katalogu testowego file_cache otwórz aplikację ApkCacheTest. Powinien się w niej wyświetlić 1 plik test.txt i jego zawartość. Na tym zrzucie ekranu możesz zobaczyć, jak te wyniki wyglądają w interfejsie.

    Rysunek 1. Wyniki testu ApkCacheTest.