Buforowanie pliku 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 urządzeń mogą umieszczać w pamięci podręcznej APK popularne aplikacje przechowywane na prawie pustej partycji B na nowych urządzeniach z partycjami A/B bez wpływu na przestrzeń danych przeznaczoną dla użytkownika. Dzięki pamięci podręcznej plików APK na urządzeniu nowe urządzenia lub urządzenia, 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.

Przykłady zastosowań

  • przechowywanie preinstalowanych aplikacji na partycji B w celu szybszego konfigurowania;
  • przechowywanie popularnych aplikacji na partycji B w celu szybszego przywracania;

Wymagania wstępne

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

  • Zainstalowana wersja Androida 8.1 (O MR1)
  • Wdrożona partycja A/B

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

Dlatego pamięć podręczna APK nie może być aktualizowana za pomocą OTA; może być wstępnie wczytana tylko w fabryce. Przywracanie ustawień fabrycznych ma wpływ tylko na partycję /data. Do czasu pobrania obrazu OTA system B nadal ma wstępnie załadowane treści. Po przywróceniu do ustawień fabrycznych system ponownie przejdzie przez pierwszy proces uruchamiania. Oznacza to, że buforowanie APK jest niedostępne, jeśli obraz OTA został pobrany na partycję B, a następnie urządzenie zostało zresetowane do ustawień fabrycznych.

Implementacja

Podejście 1. Treści na partycji system_other

Porada: w przypadku przywrócenia ustawień fabrycznych nie tracisz zainstalowanych wcześniej treści. Po ponownym uruchomieniu urządzenia zostaną one skopiowane z partycji B.

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

Aby dane wstępne zostały skopiowane podczas pierwszego uruchomienia, system wywołuje skrypt w /system/bin/preloads_copy.sh. Skrypt jest wywoływany z jednym argumentem (ścieżką do punktu zamontowania tylko do odczytu dla partycji system_b):

Aby wdrożyć tę funkcję, wprowadź te zmiany na urządzeniu. Oto przykład z Marlin:

  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 źródłowy znajdziesz tutaj: device/google/marlin/preloads_copy.sh
  2. Zmień plik init.common.rc, aby utworzyć potrzebny 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ładowy plik źródłowy init znajdziesz w folderze: 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 w folderze /device/google/marlin/+/main/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ładowy plik kontekstów SELinux znajdziesz w folderze: device/google/marlin/sepolicy/preloads_copy.te
  5. Podczas kompilacji katalog z zainstalowanymi 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 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 katalogu /data/preloads podczas pierwszego uruchamiania urządzenia. Ten skrypt jest uruchamiany w czasie kompilacji w celu przygotowania obrazu system_other. Załadowane treści powinny być dostępne w folderze vendor/google_devices/marlin/preloads. OEM może dowolnie wybrać nazwę lub ścieżkę repozytorium.
  6. Pamięć podręczna APK znajduje się w folderze /data/preloads/file_cache i ma taki 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 implementacji, o ile końcowa struktura pliku odzwierciedla strukturę opisaną powyżej.

Sposób 2. Treści dotyczące danych użytkownika (flash image)

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

Pro: działa od razu po zainstalowaniu – nie trzeba wprowadzać żadnych zmian na urządzeniu, aby skopiować pliki przy pierwszym uruchomieniu. Treści znajdują się już w partycji /data.

Wady: po przywróceniu ustawień fabrycznych utracisz zapisane wcześniej treści. Chociaż może to być akceptowalne w niektórych przypadkach, nie zawsze sprawdza się w przypadku producentów OEM, którzy resetują urządzenia do ustawień fabrycznych po przeprowadzeniu kontroli jakości.

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

Dodano nową metodę IPackageManager.deletePreloadsFileCache, która umożliwia usunięcie katalogu z zawartością do pobrania, aby odzyskać całą przestrzeń. Metoda może być wywoływana tylko przez aplikacje z SYSTEM_UID, np. serwer systemowy lub Ustawienia.

Przygotowanie aplikacji

Dostęp do katalogu pamięci podręcznej z zawartością do wstępnego załadowania mają tylko aplikacje z przywilejami. Aby uzyskać dostęp, aplikacje muszą być zainstalowane w katalogu /system/priv-app.

Weryfikacja

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

Użyj przykładowej aplikacji ApkCacheTest do testowania pamięci podręcznej pliku APK.

  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 rootem:
    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. Symulowanie katalogu pamięci podręcznej plików i jego zawartości w razie potrzeby (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 testowego katalogu file_cache otwórz aplikację ApkCacheTest. Powinna ona wyświetlić 1 plik test.txt i jego zawartość. Na zrzucie ekranu widać, jak te wyniki wyglądają w interfejsie.

    Rysunek 1. Wyniki ApkCacheTest.