В этом документе описывается решение для кеширования APK, которое позволяет быстро устанавливать предустановленные приложения на устройствах с поддержкой A/B-разделов.
Производители устройств могут размещать предварительно установленные и популярные приложения в кеше APK, который хранится в почти пустом разделе B на новых устройствах с разделами A/B, не затрагивая при этом пользовательские данные. Благодаря кешу APK на устройстве новые или недавно сброшенные до заводских настроек устройства готовы к использованию почти сразу, без необходимости скачивать APK-файлы из Google Play.
Примеры использования
- Храните предустановленные приложения в разделе B, чтобы ускорить настройку
- Храните популярные приложения в разделе B, чтобы быстрее восстанавливать их.
Требования
Чтобы использовать эту функцию, устройство должно соответствовать следующим требованиям:
- Установлена версия Android 8.1 (O MR1)
- Реализован раздел A/B
Предварительно загруженный контент можно скопировать только при первом запуске. Это связано с тем, что на устройствах, поддерживающих системные обновления A/B, в разделе B хранятся не файлы системных образов, а предварительно загруженный контент, например ресурсы для демонстрации в розничных магазинах, файлы OAT и кеш APK. После того как ресурсы будут скопированы в раздел /data (это происходит при первой загрузке), раздел B будет использоваться для беспроводных обновлений, чтобы скачивать обновленные версии образа системы.
Поэтому кеш 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 systemmkdir /data/preloads/media 0775 system systemmkdir /data/preloads/demo 0775 system systeminit: device/google/marlin/init.common.rc - Определите новый домен SELinux в файле
preloads_copy.te: Пример файла домена SELinux можно найти по адресу /device/google/marlin/+/android17-release/sepolicy/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;
- Зарегистрируйте домен в новом файле
:/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и имеет следующую структуру: Это окончательная структура каталогов на устройствах. Производители устройств могут выбрать любой способ реализации, если конечная структура файла будет соответствовать описанной выше./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, который позволяет удалить каталог предзагрузок и освободить все занятое им пространство. Этот метод могут вызывать только приложения с идентификатором SYSTEM_UID, например системный сервер или приложение "Настройки".
Подготовка приложения
Доступ к каталогу кеша предзагрузок имеют только привилегированные приложения. Чтобы получить доступ к этим данным, приложения должны быть установлены в каталог /system/priv-app.
Проверка
- После первой загрузки устройства в каталоге
/data/preloads/file_cacheдолжен быть контент. - Если на устройстве заканчивается место, контент из каталога
file_cache/должен быть удален.
Для тестирования кеша APK используйте пример приложения ApkCacheTest.
- Чтобы создать приложение, выполните следующую команду в корневом каталоге:
make ApkCacheTest - Установите приложение как привилегированное. Помните, что доступ к кешу APK есть только у привилегированных приложений.
Для этого требуется устройство с root-доступом:
adb root && adb remountadb shell mkdir /system/priv-app/ApkCacheTestadb 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.apkcachetestadb shell restorecon -r /data/preloadsadb shell "echo "Test File" > /data/preloads/file_cache/com.android.apkcachetest/test.txt" - Проверьте приложение. После установки приложения и создания тестового каталога
file_cacheоткройте приложение ApkCacheTest. В нем должен быть показан один файлtest.txtи его содержимое. На скриншоте ниже показано, как эти результаты выглядят в пользовательском интерфейсе.
Рисунок 1. Результаты ApkCacheTest.