В этом документе описывается разработка решения по кэшированию 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:
- Добавьте скрипт, который выполняет копирование, в файл
device-common.mk
(в данном случаеdevice/google/marlin/device-common.mk
), например так: Исходный код примера скрипта можно найти по адресу: device/google/marlin/preloads_copy.sh# 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
- Отредактируйте файл
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 - Определите новый домен SELinux в файле
preloads_copy.te
: Пример файла домена SELinux можно найти по адресу: /device/google/marlin/+/android16-release/sepolicy/preloads_copy.tetype 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;
- Зарегистрируйте домен в новом
Файл/sepolicy/file_contexts
:/sepolicy/file_contexts Пример файла контекста SELinux можно найти по адресу: device/google/marlin/sepolicy/preloads_copy.te./system/bin/preloads_copy\.sh u:object_r:preloads_copy_exec:s0
- Во время сборки каталог с предварительно загруженным содержимым необходимо скопировать в раздел
system_other
: Это пример изменения в Makefile, которое позволяет копировать ресурсы кэша APK из Git-репозитория поставщика (в нашем случае это был vendor/google_devices/marlin/preloads) в папку на разделе system_other, которая впоследствии будет скопирована в /data/preloads при первой загрузке устройства. Этот скрипт запускается во время сборки для подготовки образа system_other. Он ожидает, что предзагруженный контент будет доступен в vendor/google_devices/marlin/preloads. OEM-производитель может свободно выбрать имя и путь к репозиторию.# 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)
- Кэш APK находится в
/data/preloads/file_cache
и имеет следующую структуру: Это окончательная структура каталогов на устройствах. OEM-производители могут выбрать любой подход к реализации, при условии, что окончательная структура файлов будет соответствовать описанной выше./data/preloads/file_cache/ app.package.name.1/ file1 fileN app.package.name.N/
Подход 2. Содержимое изображения пользовательских данных, прошитое на заводе
Этот альтернативный подход предполагает, что предварительно загруженный контент уже включен в каталог /data/preloads
на разделе /data
.
Плюс : Работает сразу «из коробки» — не нужно настраивать устройство для копирования файлов при первой загрузке. Содержимое уже находится в разделе /data
.
Минусы : предустановленный контент теряется после сброса настроек к заводским. Хотя для некоторых это приемлемо, для производителей оригинального оборудования (OEM), которые выполняют сброс настроек к заводским после проверки качества, это не всегда работает.
В android.content.Context
добавлен новый метод @SystemApi — getPreloadsFileCache()
. Он возвращает абсолютный путь к каталогу приложения в предварительно загруженном кэше.
Добавлен новый метод IPackageManager.deletePreloadsFileCache
, позволяющий удалить каталог preloads для освобождения всего пространства. Метод может быть вызван только приложениями с SYSTEM_UID, то есть системным сервером или настройками.
Подготовка приложения
Доступ к каталогу кэша предзагрузок имеют только привилегированные приложения. Для этого приложения должны быть установлены в каталоге /system/priv-app
.
Проверка
- После первой загрузки устройство должно иметь содержимое в каталоге
/data/preloads/file_cache
. - Содержимое каталога
file_cache/
необходимо удалить, если на устройстве недостаточно свободного места.
Используйте пример приложения ApkCacheTest для тестирования кэша APK.
- Создайте приложение, выполнив эту команду из корневого каталога:
make ApkCacheTest
- Установите приложение как привилегированное. (Помните, что только привилегированные приложения могут получить доступ к кэшу 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
- При необходимости смоделируйте каталог кеша файлов и его содержимое (также требуя привилегий 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"
- Протестируйте приложение. После установки приложения и создания каталога test
file_cache
откройте приложение ApkCacheTest. Оно должно отобразить файлtest.txt
и его содержимое. На этом снимке экрана показано, как эти результаты отображаются в пользовательском интерфейсе.
Рисунок 1. Результаты ApkCacheTest.