Buforowanie plików APK

W tym dokumencie opisujemy projekt rozwiązania do buforowania plików APK, które umożliwia szybką instalację preinstalowanych aplikacji na urządzeniu obsługującym partycje A/B.

Producenci OEM mogą umieszczać preinstalowane i popularne aplikacje w pamięci podręcznej plików APK przechowywanej na prawie pustej partycji B na nowych urządzeniach z partycjami A/B bez wpływu na przestrzeń danych użytkownika. Dzięki pamięci podręcznej plików APK dostępnej na urządzeniu nowe lub niedawno przywrócone do ustawień fabrycznych urządzenia 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 na partycji B w celu szybszego przywracania

Wymagania wstępne

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

  • zainstalowaną wersję Androida 8.1 (O MR1),
  • wdrożoną partycję A/B.

Preinstalowane treści można kopiować 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 preinstalowane treści, takie jak zasoby trybu demo dla sklepów, pliki OAT i pamięć podręczna plików APK. Po skopiowaniu zasobów na partycję /data (co następuje podczas pierwszego uruchomienia) partycja B będzie używana przez aktualizacje OTA do pobierania zaktualizowanych wersji obrazu systemu.

Dlatego pamięci podręcznej plików APK nie można aktualizować przez OTA. Można ją preinstalować tylko w fabryce. Przywracanie ustawień fabrycznych wpływa tylko na partycję /data. Partycja systemowa B nadal zawiera preinstalowane treści, dopóki nie zostanie pobrany obraz OTA. Po przywróceniu ustawień fabrycznych system ponownie przejdzie przez pierwsze uruchomienie. Oznacza to, że buforowanie plików APK nie jest dostępne, jeśli obraz OTA zostanie pobrany na partycję B, a następnie urządzenie zostanie przywrócone do ustawień fabrycznych.

Implementacja

Sposób nr 1. Treści na partycji system_other

Zaleta: preinstalowane treści nie są tracone po przywróceniu 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 preinstalowanych treści.

Aby preinstalowane treści zostały skopiowane podczas pierwszego uruchomienia, system wywołuje skrypt w /system/bin/preloads_copy.sh. Skrypt jest wywoływany z 1 argumentem (ścieżką do punktu podłączania tylko do odczytu dla partycji system_b):

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

  1. Dodaj skrypt, który wykonuje kopiowanie, do device-common.mk pliku (w tym przypadku device/google/marlin/device-common.mk):
    # 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
    
    Źródło przykładowego skryptu znajdziesz w: device/google/marlin/preloads_copy.sh
  2. Edytuj plik init.common.rc, aby utworzyć niezbędny /data/preloads katalog i podkatalogi:
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    Źródło przykładowego pliku init znajdziesz w: 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: /device/google/marlin/+/android17-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ładowy plik kontekstów SELinux znajdziesz w: 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 jest przykład zmiany w pliku Makefile, która umożliwia kopiowanie zasobów pamięci podręcznej plików APK z repozytorium Git dostawcy (w naszym przypadku vendor/google_devices/marlin/preloads) do lokalizacji na partycji system_other, która zostanie później skopiowana do /data/preloads podczas pierwszego uruchomienia urządzenia. Ten skrypt jest uruchamiany podczas kompilacji w celu przygotowania obrazu system_other. Oczekuje, że preinstalowane treści będą dostępne w vendor/google_devices/marlin/preloads. Producent OEM może wybrać dowolną nazwę lub ścieżkę repozytorium.
  6. Pamięć podręczna plików 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ć dowolny sposób implementacji, o ile ostateczna struktura plików będzie zgodna z opisaną powyżej.

Sposób nr 2. Treści na obrazie danych użytkownika wgranym w fabryce

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

Zaleta: działa od razu – nie trzeba wprowadzać żadnych dostosowań urządzenia , aby kopiować pliki podczas pierwszego uruchomienia. Treści są już na partycji /data.

Wada: preinstalowane treści są tracone po przywróceniu ustawień fabrycznych. Choć dla niektórych może to być akceptowalne, nie zawsze będzie to odpowiednie dla producentów OEM, którzy przywracają ustawienia fabryczne po przeprowadzeniu kontroli jakości.

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

Dodano nową metodę IPackageManager.deletePreloadsFileCache, która umożliwia usunięcie katalogu preinstalowanych treści, aby odzyskać całą przestrzeń. Metodę tę mogą wywoływać tylko aplikacje z SYSTEM_UID, czyli serwer systemowy lub Ustawienia.

Przygotowanie aplikacji

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

Weryfikacja

  • Po pierwszym uruchomieniu urządzenie powinno mieć treści w katalogu /data/preloads/file_cache.
  • Jeśli na urządzeniu zabraknie miejsca, treści w katalogu file_cache/ muszą zostać usunięte.

Do testowania pamięci podręcznej plików 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ę z uprawnieniami. (Pamiętaj, że tylko aplikacje z uprawnieniami mogą mieć dostęp do pamięci podręcznej plików 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 zasymuluj 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 testowego katalogu file_cache otwórz aplikację ApkCacheTest. Powinien się w niej wyświetlić plik test.txt i jego zawartość. Na tym zrzucie ekranu możesz zobaczyć, jak te wyniki wyglądają w interfejsie.

    Rysunek 1. Wyniki ApkCacheTest.