Кэширование APK

В этом документе описывается разработка решения по кэшированию APK для быстрой установки предустановленных приложений на устройстве, поддерживающем разделы A/B.

OEM-производители могут размещать предзагрузки и популярные приложения в кэше APK, хранящемся в почти пустом разделе B на новых устройствах с разделами A/B, не влияя на пространство данных, доступное пользователю. Имея кэш APK на устройстве, новые или недавно сброшенные до заводских настроек устройства готовы к использованию практически сразу, без необходимости загрузки файлов APK из Google Play.

Варианты использования

  • Сохраняйте предустановленные приложения в разделе B для более быстрой настройки.
  • Сохраняйте популярные приложения в разделе B для более быстрого восстановления.

Предпосылки

Для использования этой функции устройству необходимо:

  • Установлена ​​версия Android 8.1 (O MR1)
  • Реализовано разделение A/B

Предварительно загруженный контент можно скопировать только во время первой загрузки. Это связано с тем, что на устройствах, поддерживающих обновления системы A/B, раздел B на самом деле не хранит файлы образа системы, а вместо этого предварительно загруженный контент, такой как ресурсы розничной демонстрации, файлы OAT и кэш APK. После копирования ресурсов в раздел /data (это происходит при первой загрузке) раздел B будет использоваться обновлениями по воздуху (OTA) для загрузки обновленных версий образа системы.

Поэтому кэш APK не может быть обновлен через OTA; он может быть предварительно загружен только на заводе. Сброс к заводским настройкам влияет только на раздел /data. Раздел системы B по-прежнему имеет предварительно загруженный контент до тех пор, пока не будет загружен образ OTA. После сброса к заводским настройкам система снова пройдет первую загрузку. Это означает, что кэширование APK недоступно, если образ OTA загружен в раздел B, а затем устройство будет сброшено к заводским настройкам.

Выполнение

Подход 1. Содержимое раздела system_other

Плюс : Предварительно загруженный контент не теряется после сброса настроек к заводским — он будет скопирован из раздела B после перезагрузки.

Минусы : Требуется место на разделе B. Загрузка после сброса настроек к заводским требует дополнительного времени для копирования предустановленного контента.

Для копирования предзагрузок во время первой загрузки система вызывает скрипт в /system/bin/preloads_copy.sh . Скрипт вызывается с одним аргументом (путь к точке монтирования только для чтения для раздела system_b ):

Чтобы реализовать эту функцию, внесите эти изменения, специфичные для устройства. Вот пример из Marlin:

  1. Добавьте скрипт, который выполняет копирование, в файл device-common.mk (в данном случае 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
    
    Пример исходного кода скрипта можно найти по адресу: device/google/marlin/preloads_copy.sh
  2. Отредактируйте файл init.common.rc , чтобы он создал необходимый каталог /data/preloads и подкаталоги:
    mkdir /data/preloads 0775 system system
    mkdir /data/preloads/media 0775 system system
    mkdir /data/preloads/demo 0775 system system
    
    Пример исходного файла init можно найти по адресу: device/google/marlin/init.common.rc
  3. Определите новый домен SELinux в файле 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;
    
    Пример файла домена SELinux можно найти по адресу: /device/google/marlin/+/android16-release/sepolicy/preloads_copy.te
  4. Зарегистрируйте домен в новом /sepolicy/file_contexts файл:
    /system/bin/preloads_copy\.sh     u:object_r:preloads_copy_exec:s0
    
    Пример файла контекстов SELinux можно найти по адресу: device/google/marlin/sepolicy/preloads_copy.te
  5. Во время сборки каталог с предварительно загруженным содержимым необходимо скопировать в раздел 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)
    
    Это пример изменения в Makefile, которое позволяет копировать ресурсы кэша APK из репозитория Git поставщика (в нашем случае это был vendor/google_devices/marlin/preloads) в местоположение на разделе system_other, которое позже будет скопировано в /data/preloads при первой загрузке устройства. Этот скрипт запускается во время сборки для подготовки образа system_other. Он ожидает, что предварительно загруженный контент будет доступен в vendor/google_devices/marlin/preloads. OEM может свободно выбирать фактическое имя/путь к репозиторию.
  6. Кэш APK находится в /data/preloads/file_cache и имеет следующую структуру:
    /data/preloads/file_cache/
        app.package.name.1/
              file1
              fileN
        app.package.name.N/
    
    Это окончательная структура каталогов на устройствах. OEM-производители могут свободно выбирать любой подход к реализации, пока окончательная структура файлов повторяет описанную выше.

Подход 2. Содержимое изображения пользовательских данных, прошитое на заводе

Этот альтернативный подход предполагает, что предварительно загруженный контент уже включен в каталог /data/preloads на разделе /data .

Pro : Работает из коробки — не нужно настраивать устройство для копирования файлов при первой загрузке. Содержимое уже находится в разделе /data .

Con : Предварительно загруженный контент теряется после сброса настроек. Хотя это может быть приемлемо для некоторых, это не всегда работает для OEM-производителей, которые сбрасывают настройки устройств после проведения проверок контроля качества.

В android.content.Context добавлен новый метод @SystemApi, getPreloadsFileCache() . Он возвращает абсолютный путь к каталогу приложения в предварительно загруженном кэше.

Добавлен новый метод IPackageManager.deletePreloadsFileCache , позволяющий удалить каталог preloads для освобождения всего пространства. Метод может быть вызван только приложениями с SYSTEM_UID, т. е. системным сервером или настройками.

Подготовка приложения

Только привилегированные приложения могут получить доступ к каталогу кэша preloads. Для этого доступа приложения должны быть установлены в каталоге /system/priv-app .

Проверка

  • После первой загрузки устройство должно иметь содержимое в каталоге /data/preloads/file_cache .
  • Содержимое каталога file_cache/ необходимо удалить, если на устройстве заканчивается свободное место.

Используйте пример приложения ApkCacheTest для тестирования кэша APK.

  1. Создайте приложение, выполнив эту команду из корневого каталога:
    make ApkCacheTest
    
  2. Установите приложение как привилегированное. (Помните, что только привилегированные приложения могут получить доступ к кэшу APK.) Для этого требуется устройство с правами root:
    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. При необходимости смоделируйте каталог кэша файлов и его содержимое (также требуются права root):
    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. Протестируйте приложение. После установки приложения и создания каталога test file_cache откройте приложение ApkCacheTest. Оно должно показать один файл test.txt и его содержимое. Посмотрите на этот снимок экрана, чтобы увидеть, как эти результаты отображаются в пользовательском интерфейсе.

    Рисунок 1. Результаты ApkCacheTest.