Динамическое разделение реализовано с помощью модуля dm-linear device-mapper в ядре Linux. Раздел super
содержит метаданные, в которых перечислены имена и диапазоны блоков каждого динамического раздела в super
. Во время init
первого этапа эти метаданные анализируются и проверяются, и создаются виртуальные блочные устройства для представления каждого динамического раздела.
При применении OTA динамические разделы автоматически создаются, изменяются или удаляются по мере необходимости. Для устройств A/B существует две копии метаданных, и изменения применяются только к копии, представляющей целевой слот.
Поскольку динамические разделы реализованы в пользовательском пространстве, разделы, необходимые загрузчику, нельзя сделать динамическими. Например, boot
, dtbo
и vbmeta
считываются загрузчиком и поэтому должны оставаться физическими разделами.
Каждый динамический раздел может принадлежать группе обновления . Эти группы ограничивают максимальное пространство, которое могут занимать разделы в этой группе. Например, system
и vendor
могут принадлежать к группе, которая ограничивает общий размер system
и vendor
.
Реализация динамических разделов на новых устройствах
В этом разделе подробно описано, как реализовать динамические разделы на новых устройствах с Android 10 и выше. Чтобы обновить существующие устройства, см. Обновление устройств Android .
Изменения разделов
Для устройств, запускаемых с Android 10, создайте раздел с именем super
. Раздел super
обрабатывает слоты A/B внутри, поэтому устройствам A/B не нужны отдельные super_a
и super_b
. Все разделы AOSP только для чтения, которые не используются загрузчиком, должны быть динамическими и должны быть удалены из таблицы разделов GUID (GPT). Разделы конкретного поставщика не обязательно должны быть динамическими и могут быть размещены в GPT.
Чтобы оценить размер super
, добавьте размеры удаляемых разделов из GPT. Для устройств A/B это должно включать размер обоих слотов. На рис. 1 показан пример таблицы разделов до и после преобразования в динамические разделы.
Поддерживаемые динамические разделы:
- Система
- Продавец
- Товар
- Расширение системы
- ОДМ
Для устройств, запускаемых с Android 10, параметр командной строки ядра androidboot.super_partition
должен быть пустым, чтобы команда sysprop ro.boot.super_partition
была пустой.
Выравнивание разделов
Модуль сопоставления устройств может работать менее эффективно, если super
не выровнен должным образом. super
ДОЛЖЕН быть выровнен по минимальному размеру запроса ввода-вывода , определенному блочным уровнем. По умолчанию система сборки (с помощью lpmake
, которая создает образ super
) предполагает, что для каждого динамического раздела достаточно выравнивания в 1 МБ. Однако поставщики должны обеспечить правильное выравнивание super
.
Вы можете определить минимальный размер запроса блочного устройства, sysfs
. Например:
# ls -l /dev/block/by-name/super lrwxrwxrwx 1 root root 16 1970-04-05 01:41 /dev/block/by-name/super -> /dev/block/sda17 # cat /sys/block/sda/queue/minimum_io_size 786432
Вы можете проверить выравнивание super
аналогичным образом:
# cat /sys/block/sda/sda17/alignment_offset
Смещение выравнивания ДОЛЖНО быть равно 0.
Изменения конфигурации устройства
Чтобы включить динамическое разбиение, добавьте следующий флаг в device.mk
:
PRODUCT_USE_DYNAMIC_PARTITIONS := true
Изменения конфигурации платы
Вам необходимо установить размер super
:
BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>
На устройствах A/B система сборки выдает ошибку, если общий размер образов динамических разделов превышает половину размера super
.
Вы можете настроить список динамических разделов следующим образом. Для устройств, использующих группы обновления, перечислите группы в переменной BOARD_SUPER_PARTITION_GROUPS
. Каждое имя группы имеет BOARD_ group _SIZE
и BOARD_ group _PARTITION_LIST
. Для устройств A/B максимальный размер группы должен охватывать только один слот, так как имена групп имеют внутренний суффикс слота.
Вот пример устройства, которое помещает все разделы в группу с именем example_dynamic_partitions
:
BOARD_SUPER_PARTITION_GROUPS := example_dynamic_partitions BOARD_EXAMPLE_DYNAMIC_PARTITIONS_SIZE := 6442450944 BOARD_EXAMPLE_DYNAMIC_PARTITIONS_PARTITION_LIST := system vendor product
Вот пример устройства, которое помещает сервисы системы и продукта в group_foo
, а vendor
, product
и odm
— в group_bar
:
BOARD_SUPER_PARTITION_GROUPS := group_foo group_bar BOARD_GROUP_FOO_SIZE := 4831838208 BOARD_GROUP_FOO_PARTITION_LIST := system product_services BOARD_GROUP_BAR_SIZE := 1610612736 BOARD_GROUP_BAR_PARTITION_LIST := vendor product odm
- Для устройств запуска Virtual A/B сумма максимальных размеров всех групп не должна превышать:
BOARD_SUPER_PARTITION_SIZE
— накладные расходы
См. Реализация виртуального A/B . - Для устройств запуска A/B сумма максимальных размеров всех групп должна быть:
BOARD_SUPER_PARTITION_SIZE
/ 2 — накладные расходы - Для устройств без A/B и модифицированных устройств A/B сумма максимальных размеров всех групп должна быть:
BOARD_SUPER_PARTITION_SIZE
— накладные расходы - Во время сборки сумма размеров образов каждого раздела в группе обновлений не должна превышать максимальный размер группы.
- Накладные расходы требуются при вычислении для учета метаданных, выравниваний и т. д. Разумные накладные расходы составляют 4 МБ, но вы можете выбрать более высокие накладные расходы по мере необходимости для устройства.
Размер динамических разделов
До появления динамических разделов размеры разделов были перераспределены, чтобы у них было достаточно места для будущих обновлений. Фактический размер был взят как есть, и большинство разделов только для чтения имели некоторое количество свободного места в своей файловой системе. В динамических разделах это свободное пространство не используется и может быть использовано для увеличения разделов во время OTA. Крайне важно убедиться, что разделы не занимают место впустую и занимают минимально возможный размер.
Для образов ext4, доступных только для чтения, система сборки автоматически выделяет минимальный размер, если жестко заданный размер раздела не указан. Система сборки подгоняет образ так, чтобы в файловой системе оставалось как можно меньше неиспользуемого пространства. Это гарантирует, что устройство не будет тратить место, которое можно использовать для OTA.
Кроме того, образы ext4 можно дополнительно сжать, включив дедупликацию на уровне блоков. Чтобы включить это, используйте следующую конфигурацию:
BOARD_EXT4_SHARE_DUP_BLOCKS := true
Если автоматическое выделение минимального размера раздела нежелательно, есть два способа управления размером раздела. Вы можете указать минимальный объем свободного места с BOARD_ partition IMAGE_PARTITION_RESERVED_SIZE
или указать BOARD_ partition IMAGE_PARTITION_SIZE
, чтобы динамические разделы имели определенный размер. Ни один из них не рекомендуется без необходимости.
Например:
BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE := 52428800
Это заставляет файловую систему в product.img
иметь 50 МБ неиспользуемого пространства.
Изменения системы от имени root
Устройства, запускаемые с Android 10, не должны использовать систему от имени root.
Устройства с динамическими разделами (независимо от того, запускаются ли они с динамическими разделами или модернизируются) не должны использовать систему от имени пользователя root. Ядро Linux не может интерпретировать super
и поэтому не может монтировать саму system
. system
теперь монтируется первой стадией init
, которая находится на виртуальном диске.
Не устанавливайте BOARD_BUILD_SYSTEM_ROOT_IMAGE
. В Android 10 флаг BOARD_BUILD_SYSTEM_ROOT_IMAGE
используется только для того, чтобы различать, монтируется ли система ядром или на первом этапе init
в виртуальном диске.
Установка для BOARD_BUILD_SYSTEM_ROOT_IMAGE
true
приводит к ошибке сборки, когда PRODUCT_USE_DYNAMIC_PARTITIONS
также имеет значение true
.
Когда BOARD_USES_RECOVERY_AS_BOOT
установлено значение true, образ восстановления создается как boot.img, содержащий виртуальный диск восстановления. Раньше загрузчик использовал параметр командной строки ядра skip_initramfs
, чтобы решить, в каком режиме загружаться. Для устройств Android 10 загрузчик НЕ ДОЛЖЕН передавать skip_initramfs
в командную строку ядра. Вместо этого загрузчик должен передать androidboot.force_normal_boot=1
, чтобы пропустить восстановление и загрузить обычный Android. Устройства, запускаемые с Android 12 или более поздней версии, должны использовать bootconfig для передачи androidboot.force_normal_boot=1
.
Изменения конфигурации AVB
При использовании Android Verified Boot 2.0 , если устройство не использует дескрипторы разделов в цепочке, никаких изменений не требуется. Однако если используются связанные разделы и один из проверенных разделов является динамическим, тогда необходимы изменения.
Вот пример конфигурации для устройства, которое vbmeta
для system
и vendor
разделов.
BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048 BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 BOARD_AVB_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_VENDOR_ALGORITHM := SHA256_RSA2048 BOARD_AVB_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION := 1
В этой конфигурации загрузчик ожидает найти нижний колонтитул vbmeta в конце system
и vendor
разделов. Поскольку эти разделы больше не видны загрузчику (они находятся в super
), необходимы два изменения.
- Добавьте
vbmeta_system
иvbmeta_vendor
в таблицу разделов устройства. Для устройств A/B добавьтеvbmeta_system_a
,vbmeta_system_b
,vbmeta_vendor_a
иvbmeta_vendor_b
. При добавлении одного или нескольких таких разделов они должны быть того же размера, что и разделvbmeta
. - Переименуйте флаги конфигурации, добавив
VBMETA_
и укажите, на какие разделы распространяется цепочка:BOARD_AVB_VBMETA_SYSTEM := system BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048 BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1 BOARD_AVB_VBMETA_VENDOR := vendor BOARD_AVB_VBMETA_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem BOARD_AVB_VBMETA_VENDOR_ALGORITHM := SHA256_RSA2048 BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP) BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION := 1
Устройство может использовать один, оба или ни один из этих разделов. Изменения нужны только при привязке к логическому разделу.
Изменения в загрузчике AVB
Если загрузчик имеет встроенную libavb , включите следующие исправления:
- 818cf56740775446285466eda984acedd4baeac0 — «libavb: запрашивать GUID раздела только тогда, когда они нужны командной строке».
- 5abd6bc2578968d24406d834471adfd995a0c2e9 — «Разрешить отсутствие системного раздела»
- 9ba3b6613b4e5130fa01a11d984c6b5f0eb3af05 — «Исправить AvbSlotVerifyData-> cmdline может быть NULL»
При использовании связанных разделов включите дополнительный патч:
- 49936b4c0109411fdd38bd4ba3a32a01c40439a9 — «libavb: поддержка больших двоичных объектов vbmeta в начале раздела».
Изменения в командной строке ядра
В командную строку ядра необходимо добавить новый параметр androidboot.boot_devices
. Это используется init
для включения символических ссылок /dev/block/by-name
. Это должен быть компонент пути устройства к базовой символической ссылке по имени, созданной ueventd
, то есть /dev/block/platform/ device-path /by-name/ partition-name
. Устройства, запускаемые с Android 12 или более поздней версии, должны использовать bootconfig для передачи androidboot.boot_devices
в init
.
Например, если символическая ссылка суперраздела по имени — это /dev/block/platform/ soc/100000.ufshc /by-name/super
, вы можете добавить параметр командной строки в файл BoardConfig.mk следующим образом:
BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshcВы можете добавить параметр bootconfig в файл BoardConfig.mk следующим образом:
BOARD_BOOTCONFIG += androidboot.boot_devices=soc/100000.ufshc
fstab изменения
Дерево устройств и наложения дерева устройств не должны содержать записи fstab. Используйте файл fstab, который будет частью виртуального диска.
Необходимо внести изменения в файл fstab для логических разделов:
- Поле флагов fs_mgr должно включать
logical
флаг и флагfirst_stage_mount
, представленный в Android 10, который указывает, что раздел должен быть смонтирован на первом этапе. - Раздел может указать
avb= vbmeta partition name
в качестве флагаfs_mgr
, а затем указанный разделvbmeta
инициализируется на первом этапеinit
перед попыткой монтировать какие-либо устройства. - Поле
dev
должно быть именем раздела.
Следующие записи fstab задают систему, поставщика и продукт как логические разделы в соответствии с приведенными выше правилами.
#<dev> <mnt_point> <type> <mnt_flags options> <fs_mgr_flags> system /system ext4 ro,barrier=1 wait,slotselect,avb=vbmeta,logical,first_stage_mount vendor /vendor ext4 ro,barrier=1 wait,slotselect,avb,logical,first_stage_mount product /product ext4 ro,barrier=1 wait,slotselect,avb,logical,first_stage_mount
Скопируйте файл fstab на виртуальный диск первого этапа.
Изменения SELinux
Блочное устройство суперраздела должно быть помечено меткой super_block_device
. Например, если символическая ссылка /dev/block/platform/ soc/100000.ufshc /by-name/super
, добавьте следующую строку в file_contexts
:
/dev/block/platform/soc/10000\.ufshc/by-name/super u:object_r:super_block_device:s0
быстрая загрузка
Загрузчик (или любой инструмент для прошивки, не относящийся к пользовательскому пространству) не понимает динамические разделы, поэтому не может их прошить. Чтобы решить эту проблему, устройства должны использовать реализацию протокола fastboot в пользовательском пространстве, которая называется fastbootd.
Дополнительные сведения о реализации fastbootd см. в разделе Перемещение Fastboot в пространство пользователя .
adb перемонтировать
Для разработчиков, использующих сборки eng или userdebug, adb remount
чрезвычайно полезен для быстрой итерации. Динамические разделы создают проблему для adb remount
, поскольку в каждой файловой системе больше нет свободного места. Чтобы решить эту проблему, устройства могут включать оверлеи. Пока в суперразделе есть свободное место, adb remount
автоматически создает временный динамический раздел и использует overlayfs для записи. Временный раздел scratch
, поэтому не используйте это имя для других разделов.
Дополнительные сведения о том, как включить overlayfs, см. в файле README overlayfs в AOSP.
Обновление Android-устройств
Если вы обновляете устройство до Android 10 и хотите включить поддержку динамических разделов в OTA, вам не нужно изменять встроенную таблицу разделов. Требуется дополнительная настройка.
Изменения конфигурации устройства
Чтобы модифицировать динамическое разбиение, добавьте следующие флаги в device.mk
:
PRODUCT_USE_DYNAMIC_PARTITIONS := true PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true
Изменения конфигурации платы
Вам необходимо установить следующие переменные платы:
- Установите
BOARD_SUPER_PARTITION_BLOCK_DEVICES
в список блочных устройств, используемых для хранения экстентов динамических разделов. Это список имен существующих физических разделов на устройстве. - Установите
BOARD_SUPER_PARTITION_ partition _DEVICE_SIZE
размеры каждого блочного устройства вBOARD_SUPER_PARTITION_BLOCK_DEVICES
соответственно. Это список размеров существующих физических разделов на устройстве. ОбычноBOARD_ partition IMAGE_PARTITION_SIZE
в существующих конфигурациях платы. - Отмените существующий
BOARD_ partition IMAGE_PARTITION_SIZE
для всех разделов вBOARD_SUPER_PARTITION_BLOCK_DEVICES
. - Задайте для
BOARD_SUPER_PARTITION_SIZE
суммуBOARD_SUPER_PARTITION_ partition _DEVICE_SIZE
. - Установите
BOARD_SUPER_PARTITION_METADATA_DEVICE
на блочное устройство, на котором хранятся метаданные динамического раздела. Это должен быть один изBOARD_SUPER_PARTITION_BLOCK_DEVICES
. Обычно это значениеsystem
. - Установите
BOARD_SUPER_PARTITION_GROUPS
,BOARD_ group _SIZE
иBOARD_ group _PARTITION_LIST
соответственно. Дополнительные сведения см. в разделе Изменения конфигурации платы на новых устройствах .
Например, если на устройстве уже есть разделы системы и производителя, и вы хотите преобразовать их в динамические разделы и добавить новый раздел продукта во время обновления, задайте следующую конфигурацию платы:
BOARD_SUPER_PARTITION_BLOCK_DEVICES := system vendor BOARD_SUPER_PARTITION_METADATA_DEVICE := system # Rename BOARD_SYSTEMIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE. BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE := <size-in-bytes> # Rename BOARD_VENDORIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE := <size-in-bytes> # This is BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE + BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE BOARD_SUPER_PARTITION_SIZE := <size-in-bytes> # Configuration for dynamic partitions. For example: BOARD_SUPER_PARTITION_GROUPS := group_foo BOARD_GROUP_FOO_SIZE := <size-in-bytes> BOARD_GROUP_FOO_PARTITION_LIST := system vendor product
Изменения SELinux
Блочные устройства суперраздела должны быть отмечены атрибутом super_block_device_type
. Например, если на устройстве уже есть system
и vendor
разделы, вы хотите использовать их как блочные устройства для хранения экстентов динамических разделов, а их символические ссылки по имени помечены как system_block_device
:
/dev/block/platform/soc/10000\.ufshc/by-name/system u:object_r:system_block_device:s0 /dev/block/platform/soc/10000\.ufshc/by-name/vendor u:object_r:system_block_device:s0
Затем добавьте следующую строку в device.te
:
typeattribute system_block_device super_block_device_type;
Другие конфигурации см. в разделе Реализация динамических разделов на новых устройствах .
Дополнительные сведения об обновлении обновлений см. в разделе OTA для устройств A/B без динамических разделов .
Заводские изображения
Для устройства, запускаемого с поддержкой динамических разделов, избегайте использования быстрой загрузки пользовательского пространства для прошивки заводских образов, так как загрузка в пользовательское пространство медленнее, чем другие методы прошивки.
Чтобы решить эту проблему, make dist
теперь создает дополнительный образ super.img
, который можно прошить непосредственно в раздел super. Он автоматически объединяет содержимое логических разделов, то есть содержит system.img
, vendor.img
и т. д. в дополнение к метаданным super
. Этот образ можно прошить непосредственно в раздел super
без каких-либо дополнительных инструментов или использования fastbootd. После сборки super.img
помещается в ${ANDROID_PRODUCT_OUT}
.
Для устройств A/B, которые запускаются с динамическими разделами, super.img
содержит изображения в слоте A. После прямой прошивки суперобраза пометьте слот A как загрузочный перед перезагрузкой устройства.
Для модифицированных устройств make dist
создает набор образов super_*.img
, которые можно прошивать непосредственно в соответствующие физические разделы. Например, make dist
создает super_system.img
и super_vendor.img
когда BOARD_SUPER_PARTITION_BLOCK_DEVICES
является поставщиком системы. Эти образы помещаются в папку OTA в target_files.zip
.
Device mapper — настройка устройства хранения
Динамическое разделение поддерживает ряд недетерминированных объектов-сопоставителей устройств. Не все они могут создаваться должным образом, поэтому вы должны отслеживать все монтирования и обновлять свойства Android всех связанных разделов с их базовыми устройствами хранения.
Механизм внутри init
отслеживает монтирование и асинхронно обновляет свойства Android. Время, затрачиваемое на это, не обязательно находится в пределах определенного периода, поэтому вы должны предоставить достаточно времени, чтобы все триггеры on property
отреагировали. Свойства: dev.mnt.blk. <partition>
, где <partition>
— это, например, root
, system
, data
или vendor
. Каждое свойство связано с именем базового устройства хранения, как показано в следующих примерах:
taimen:/ % getprop | grep dev.mnt.blk [dev.mnt.blk.data]: [sda] [dev.mnt.blk.firmware]: [sde] [dev.mnt.blk.metadata]: [sde] [dev.mnt.blk.persist]: [sda] [dev.mnt.blk.root]: [dm-0] [dev.mnt.blk.vendor]: [dm-1] blueline:/ $ getprop | grep dev.mnt.blk [dev.mnt.blk.data]: [dm-4] [dev.mnt.blk.metadata]: [sda] [dev.mnt.blk.mnt.scratch]: [sda] [dev.mnt.blk.mnt.vendor.persist]: [sdf] [dev.mnt.blk.product]: [dm-2] [dev.mnt.blk.root]: [dm-0] [dev.mnt.blk.system_ext]: [dm-3] [dev.mnt.blk.vendor]: [dm-1] [dev.mnt.blk.vendor.firmware_mnt]: [sda]
Язык init.rc
позволяет расширять свойства Android как часть правил, а устройства хранения могут быть настроены платформой по мере необходимости с помощью таких команд:
write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb 128 write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb 128
Как только обработка команды начинается на втором этапе init
, epoll loop
становится активным, и значения начинают обновляться. Однако, поскольку триггеры свойств не активны до поздней init
, их нельзя использовать на начальных этапах загрузки для обработки root
, system
или vendor
. Вы можете ожидать, что read_ahead_kb
ядра по умолчанию будет достаточным, пока сценарии init.rc
не смогут переопределить в Early early-fs
FS (когда запускаются различные демоны и средства). Поэтому Google рекомендует использовать функцию on property
в сочетании со свойством, контролируемым init.rc
, например sys.read_ahead_kb
, для определения времени операций и предотвращения условий гонки, как в этих примерах:
on property:dev.mnt.blk.root=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on property:dev.mnt.blk.system=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.system}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on property:dev.mnt.blk.vendor=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.vendor}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on property:dev.mnt.blk.product=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.system_ext}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on property:dev.mnt.blk.oem=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.oem}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on property:dev.mnt.blk.data=* && property:sys.read_ahead_kb=* write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048} on early-fs: setprop sys.read_ahead_kb ${ro.read_ahead_kb.boot:-2048} on property:sys.boot_completed=1 setprop sys.read_ahead_kb ${ro.read_ahead_kb.bootcomplete:-128}