На этой странице описан канонический метод добавления или определения системных свойств в Android, а также приведены рекомендации по рефакторингу существующих системных свойств. При рефакторинге следуйте рекомендациям, если только у вас нет серьезных проблем с совместимостью, которые требуют других действий.
Шаг 1. Определите системное свойство
При добавлении системного свойства выберите для него название и свяжите его с контекстом свойства SELinux. Если подходящего контекста нет, создайте новый. Имя используется при доступе к свойству, а контекст свойства – для управления доступом в SELinux. Название может быть любой строкой, но AOSP рекомендует использовать структурированный формат, чтобы оно было понятным.
Название ресурса
Используйте следующий формат с нижним подчеркиванием:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Для элемента prefix используйте "" (пропущено), ro (для свойств, задаваемых только один раз) или persist (для свойств, сохраняющихся после перезагрузки).
Важные примечания
Используйте ro, только если вы уверены, что вам не потребуется записывать данные в prefix. ** Не указывайте префикс ro.** Вместо этого используйте sepolicy, чтобы сделать prefix доступным только для чтения (то есть доступным для записи только для init).
Используйте persist, только если вы уверены, что значение должно сохраняться после перезагрузки и что системные свойства – единственный доступный вам вариант.
Google строго проверяет системные свойства, у которых есть свойство ro или persist.
Термин group используется для агрегирования связанных ресурсов. Оно предназначено для обозначения подсистемы и используется так же, как audio или telephony. Не используйте
неоднозначные или перегруженные термины, такие как sys, system, dev, default или
config.
Обычно используется название типа домена процесса, у которого есть эксклюзивный доступ для чтения или записи к системным свойствам. Например, для системных свойств, к которым у процесса vold есть доступ на запись, в качестве названия группы обычно используется vold (название типа домена для процесса).
При необходимости добавьте subgroup, чтобы более точно классифицировать свойства, но избегайте неоднозначных или перегруженных терминов для описания этого элемента. (Вы также можете указать несколько значений subgroup.)
Многие названия групп уже определены. Проверьте файл system/sepolicy/private/property_contexts и по возможности используйте существующие названия групп, а не создавайте новые. В следующей таблице приведены
примеры часто используемых названий групп.
| Домен | Группа (и подгруппа) |
|---|---|
| Bluetooth | bluetooth |
| sysprops из командной строки ядра | boot |
| sysprops, которые идентифицируют сборку | build
|
| связанные с телефонией; | telephony |
| аудио; | audio |
| графика; | graphics |
| vold related | vold |
Ниже описано, как используются name и type в примере регулярного выражения выше.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
nameопределяет системное свойство в группе.type– необязательный элемент, который уточняет тип или назначение системного свойства. Например, вместо того чтобы назвать системное свойствоaudio.awesome_feature_enabledили простоaudio.awesome_feature, переименуйте его вaudio.awesome_feature.enabled, чтобы отразить тип и назначение системного свойства.
Конкретных правил относительно типа нет, но мы рекомендуем использовать следующие варианты:
enabled: используйте, если тип представляет собой системное логическое свойство, которое используется для включения или отключения функции.config– используется, если нужно пояснить, что системное свойство не представляет динамическое состояние системы, а является заранее заданным значением (например, объектом, доступным только для чтения).List: используется, если это системное свойство, значением которого является список.Timeoutmillis– используйте, если это системное свойство для значения времени ожидания в миллисекундах.
Примеры:
persist.radio.multisim.configdrm.service.enabled
Контекст ресурса
Новая схема контекста свойств SELinux позволяет использовать более точные и описательные названия. Как и в случае с названиями свойств, AOSP рекомендует использовать следующий формат:
{group}[_{subgroup}]*_prop
Ниже приведены определения этих терминов.
group и subgroup имеют то же значение, что и в предыдущем примере регулярного выражения. Например, vold_config_prop обозначает свойства, которые являются конфигурациями от поставщика и должны быть заданы vendor_init, а vold_status_prop или просто vold_prop обозначает свойства, которые должны показывать текущий статус vold.
Присваивайте контексту свойства имена, отражающие их общее назначение. В частности, не используйте следующие типы терминов:
- Слишком общие и неоднозначные термины, например
sys,system,default. - Термины, напрямую связанные с доступностью, например
exported,apponly,ro,public,private.
Используйте названия, например vold_config_prop, а не exported_vold_prop или vold_vendor_writable_prop.
Тип
Тип свойства может быть одним из следующих (см. таблицу).
| Тип | Определение |
|---|---|
| Логическое значение | true или 1 – для значения "истина"; false или 0 – для значения "ложь". |
| Целое число | 64-битное целое число со знаком |
| Целое число без знака | беззнаковое 64-битное целое число |
| Двойной трекер | число с плавающей запятой двойной точности |
| Строка | Любая допустимая строка UTF-8. |
| enum : перечисление | Значения могут быть любой допустимой строкой UTF-8 без пробелов. |
| Список выше | В качестве разделителя используется запятая (,).Список целых чисел [1, 2, 3] хранится как 1,2,3. |
Внутренне все свойства хранятся как строки. Вы можете задать тип, указав его в файле property_contexts. Подробную информацию вы найдете в разделе property_contexts в шаге 3.
Шаг 2. Определите необходимые уровни доступности
Существует четыре вспомогательных макроса, которые определяют свойство.
| Тип специальных возможностей | Значение |
|---|---|
system_internal_prop |
Свойства, которые используются только в /system |
system_restricted_prop |
Свойства, которые считываются за пределами /system, но не записываются |
system_vendor_config_prop |
Свойства, которые считываются вне /system и записываются только vendor_init |
system_public_prop |
Свойства, которые считываются и записываются вне /system |
Ограничьте доступ к системным свойствам. В прошлом широкий доступ приводил к сбоям в работе приложений и уязвимостям в системе безопасности. При определении области действия учитывайте следующие вопросы:
- Нужно ли сохранять это системное свойство? (если да, то почему?)
- Какому процессу следует предоставить доступ на чтение к этому ресурсу?
- Какому процессу нужно предоставить доступ с правом записи к этому ресурсу?
Используйте приведенные выше вопросы и следующую схему принятия решений, чтобы определить подходящую область доступа.
Рисунок 1. Дерево принятия решений для определения области доступа к свойствам системы
Шаг 3. Добавьте в систему/sepolicy
При доступе к sysprop SELinux управляет доступностью процессов. Определив необходимый уровень доступности, задайте контексты свойств в разделе system/sepolicy, а также дополнительные правила allow и neverallow о том, что процессам разрешено и запрещено читать или записывать.
Сначала определите контекст свойства в файле system/sepolicy/public/property.te. Если свойство является внутренним для системы, определите его в файле system/sepolicy/private/property.te. Используйте один из макросов system_[accessibility]_prop([context]), который обеспечивает доступность, необходимую для свойства вашей системы. Вот пример файла system/sepolicy/public/property.te:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Пример кода, который нужно добавить в файл system/sepolicy/private/property.te:
system_internal_prop(audio_baz_prop)
Во-вторых, предоставьте доступ для чтения и (или) записи к контексту ресурса. Чтобы предоставить доступ, используйте макросы set_prop и get_prop в файле system/sepolicy/public/{domain}.te или system/sepolicy/private/{domain}.te. По возможности используйте макрос private. Макрос public подходит только в том случае, если макрос set_prop или get_prop влияет на домены за пределами основного домена.
Пример в файле system/sepolicy/private/audio.te:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Пример в файле system/sepolicy/public/domain.te:
get_prop(domain, audio_bar_prop)
Затем добавьте несколько правил neverallow, чтобы ещё больше ограничить доступ, заданный макросом. Например, вы использовали system_restricted_prop, поскольку свойства вашей системы должны быть доступны для процессов поставщика. Если доступ на чтение не требуется всем процессам поставщика, а нужен только определенным процессам (например, vendor_init), запретите процессы поставщика, которым не нужен доступ на чтение.
Чтобы ограничить доступ на чтение и запись, используйте следующий синтаксис:
Чтобы ограничить доступ для записи:
neverallow [domain] [context]:property_service set;
Чтобы ограничить доступ на чтение:
neverallow [domain] [context]:file no_rw_file_perms;
Если правило neverallow связано с определенным доменом, поместите его в файл system/sepolicy/private/{domain}.te. Для более широких правил neverallow используйте общие домены, например:
system/sepolicy/private/property.tesystem/sepolicy/private/coredomain.tesystem/sepolicy/private/domain.te
В файл system/sepolicy/private/audio.te добавьте следующий код:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
В файле system/sepolicy/private/property.te разместите следующий код:
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
Обратите внимание, что {domain -coredomain} регистрирует все процессы поставщика. Таким образом,
{domain -coredomain -vendor_init} означает "все процессы поставщика, кроме
vendor_init".
Наконец, свяжите системное свойство с контекстом свойства. Это гарантирует, что предоставленный доступ и правила neverallow, примененные к контекстам свойств, будут применены к фактическим свойствам. Для этого добавьте запись в файл property_contexts, в котором описывается сопоставление между системными свойствами и контекстами свойств. В этом файле можно указать одно свойство или префикс для свойств, которые будут сопоставлены с контекстом.
Вот синтаксис для сопоставления одного свойства:
[property_name] u:object_r:[context_name]:s0 exact [type]
Синтаксис для сопоставления префикса:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
Вы также можете указать тип ресурса. Доступны следующие варианты:
boolintuintdoubleenum [list of possible values...]string(для свойств списка используйтеstring).
По возможности указывайте тип для каждой записи, поскольку при задании значения property применяется правило type. Ниже показано, как составить сопоставление.
# binds a boolean property "ro.audio.status.enabled"
# to the context "audio_foo_prop"
ro.audio.status.enabled u:object_r:audio_foo_prop:s0 exact bool
# binds a boolean property "vold.decrypt.status"
# to the context "vold_foo_prop"
# The property can only be set to one of these: on, off, unknown
vold.decrypt.status u:object_r:vold_foo_prop:s0 exact enum on off unknown
# binds any properties starting with "ro.audio.status."
# to the context "audio_bar_prop", such as
# "ro.audio.status.foo", or "ro.audio.status.bar.baz", and so on.
ro.audio.status. u:object_r:audio_bar_prop:s0 prefix
Если точная запись и запись с префиксом конфликтуют, приоритет имеет точная запись. Другие примеры можно найти в статье system/sepolicy/private/property_contexts.
Шаг 4. Определите требования к стабильности
Стабильность – ещё один аспект системных свойств, который отличается от доступности. Стабильность означает, можно ли будет изменить системное свойство (например, переименовать или даже удалить) в будущем. Это особенно важно, поскольку ОС Android становится модульной. Благодаря Treble разделы системы, поставщика и продукта можно обновлять независимо друг от друга. В Mainline некоторые части ОС представлены в виде модулей, которые можно обновлять (в APEX-файлах или APK).
Если системное свойство используется в нескольких обновляемых компонентах ПО, например в системном и стороннем разделах, оно должно быть стабильным. Однако если он используется только в определенном модуле Mainline, вы можете изменить его название, тип или контекст свойств и даже удалить его.
Чтобы определить стабильность системного свойства, задайте следующие вопросы:
- Это системное свойство предназначено для настройки партнерами (или для настройки по-разному на разных устройствах)? Если да, то оно должно быть стабильным.
- Предназначено ли это системное свойство, определенное в AOSP, для записи в код или чтения из кода (не процесса), который существует в несистемных разделах, таких как
vendor.imgилиproduct.img? Если да, то он должен быть стабильным. - Доступно ли это системное свойство в модулях Mainline или в модуле Mainline и необновляемой части платформы? Если да, то оно должно быть стабильным.
Для стабильных системных свойств формально определите каждое из них как API и используйте API для доступа к системному свойству, как описано в шаге 6.
Шаг 5. Задайте свойства во время сборки
Задайте свойства во время сборки с помощью переменных makefile. Технически значения встроены в {partition}/build.prop. Затем init считывает {partition}/build.prop, чтобы задать свойства. Существует два набора таких переменных: PRODUCT_{PARTITION}_PROPERTIES и TARGET_{PARTITION}_PROP.
PRODUCT_{PARTITION}_PROPERTIES содержит список значений свойств. Синтаксис: {prop}={value} или {prop}?={value}.
{prop}={value} – это обычное присваивание, которое гарантирует, что для {prop} будет задано значение {value}. Для одного свойства возможно только одно такое присваивание.
{prop}?={value} – необязательное присвоение; {prop} устанавливает значение {value}, только если нет присвоений {prop}={value}. Если есть несколько необязательных назначений, применяется первое из них.
# sets persist.traced.enable to 1 with system/build.prop
PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1
# sets ro.zygote to zygote32 with system/build.prop
# but only when there are no other assignments to ro.zygote
# optional are useful when giving a default value to a property
PRODUCT_SYSTEM_PROPERTIES += ro.zygote?=zygote32
# sets ro.config.low_ram to true with vendor/build.prop
PRODUCT_VENDOR_PROPERTIES += ro.config.low_ram=true
TARGET_{PARTITION}_PROP содержит список файлов, который напрямую передается в {partition}/build.prop. Каждый файл содержит список из {prop}={value} пар.
# example.prop
ro.cp_system_other_odex=0
ro.adb.secure=0
ro.control_privapp_permissions=disable
# emits example.prop to system/build.prop
TARGET_SYSTEM_PROP += example.prop
Подробную информацию можно найти в статье build/make/core/sysprop.mk.
Шаг 6. Получите доступ к свойствам во время выполнения
Свойства можно читать и записывать во время выполнения.
Скрипты инициализации
Файлы скриптов инициализации (обычно файлы *.rc) могут считывать свойство с помощью ${prop} или ${prop:-default}, могут задавать действие, которое выполняется всякий раз, когда свойство принимает определенное значение, и могут записывать свойства с помощью команды setprop.
# when persist.device_config.global_settings.sys_traced becomes 1,
# set persist.traced.enable to 1
on property:persist.device_config.global_settings.sys_traced=1
setprop persist.traced.enable 1
# when security.perf_harden becomes 0,
# write /proc/sys/kernel/sample_rate to the value of
# debug.sample_rate. If it's empty, write -100000 instead
on property:security.perf_harden=0
write /proc/sys/kernel/sample_rate ${debug.sample_rate:-100000}
команды оболочки getprop и setprop;
Чтобы прочитать или записать свойства, используйте команды оболочки getprop или setprop соответственно. Подробнее: getprop --help, setprop --help.
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop как API для C++, Java и Rust
С помощью sysprop как API можно задавать системные свойства и использовать созданные автоматически API, которые являются конкретными и типизированными. Если задать для scope значение Public, сгенерированные API также станут доступны модулям за пределами границ, а стабильность API будет обеспечена. Ниже приведены примеры файла .sysprop, модуля Android.bp и кода на C++, Java и Rust, в котором они используются.
# AudioProps.sysprop
# module becomes static class (Java) / namespace (C++) for serving API
module: "android.sysprop.AudioProps"
# owner can be Platform or Vendor or Odm
owner: Platform
# one prop defines one property
prop {
prop_name: "ro.audio.volume.level"
type: Integer
scope: Public
access: ReadWrite
api_name: "volume_level"
}
…
// Android.bp
sysprop_library {
name: "AudioProps",
srcs: ["android/sysprop/AudioProps.sysprop"],
property_owner: "Platform",
}
// Rust, Java and C++ modules can link against the sysprop_library
rust_binary {
rustlibs: ["libaudioprops_rust"],
…
}
java_library {
static_libs: ["AudioProps"],
…
}
cc_binary {
static_libs: ["libAudioProps"],
…
}
// Rust code accessing generated API.
// Get volume. Use 50 as the default value.
let vol = audioprops::volume_level()?.unwrap_or_else(50);
// Java codes accessing generated API
// get volume. use 50 as the default value.
int vol = android.sysprop.AudioProps.volume_level().orElse(50);
// add 10 to the volume level.
android.sysprop.AudioProps.volume_level(vol + 10);
// C++ codes accessing generated API
// get volume. use 50 as the default value.
int vol = android::sysprop::AudioProps::volume_level().value_or(50);
// add 10 to the volume level.
android::sysprop::AudioProps::volume_level(vol + 10);
Подробнее о том, как реализовать системные свойства в виде API…
Низкоуровневые функции и методы свойств C/C++, Java и Rust
По возможности используйте Sysprop как API, даже если вам доступны низкоуровневые функции C/C++ или Rust или низкоуровневые методы Java.
libc, libbase и libcutils предлагают функции системных свойств C++. libc – это базовый API, а функции libbase и libcutils – это оболочки. Если это возможно, используйте функции libbase sysprop. Они наиболее удобны, и хост-двоичные файлы могут использовать функции libbase. Подробнее: sys/system_properties.h (libc), android-base/properties.h (libbase) и cutils/properties.h (libcutils).
Класс android.os.SystemProperties предлагает методы системных свойств Java.
Модуль rustutils::system_properties содержит функции и типы системных свойств Rust.
Приложение. Как добавить свойства, относящиеся к определенному поставщику
Партнеры (в том числе сотрудники Google, работающие над Pixel) хотят определять системные свойства, относящиеся к оборудованию или устройству.
Свойства поставщика принадлежат партнеру и уникальны для его оборудования или устройства, а не для платформы. Поскольку они зависят от аппаратного обеспечения или устройства, их следует использовать в разделах /vendor или /odm.
С момента запуска Project Treble свойства платформы и поставщика полностью разделены, чтобы избежать конфликтов. Ниже описано, как определять свойства поставщика и какие из них необходимо использовать всегда.
Пространство имен для названий свойств и контекстов
Чтобы избежать конфликтов между свойствами поставщика и свойствами других разделов, все свойства поставщика должны начинаться с одного из следующих префиксов:
ctl.odm.ctl.vendor.ctl.start$odm.ctl.start$vendor.ctl.stop$odm.ctl.stop$vendor.init.svc.odm.init.svc.vendor.ro.odm.ro.vendor.odm.persist.odm.persist.vendor.vendor.
Обратите внимание, что префикс ro.hardware. разрешен только для совместимости.
Не используйте его для обычных ресурсов.
В примерах ниже используется один из перечисленных выше префиксов.
vendor.display.primary_redpersist.vendor.faceauth.use_disk_cachero.odm.hardware.platform
Все контексты свойств поставщика должны начинаться с vendor_. Это также необходимо для совместимости. Примеры:
vendor_radio_prop.vendor_faceauth_prop.vendor_usb_prop.
Поставщик несет ответственность за именование и поддержку свойств, поэтому следуйте формату, предложенному на шаге 2, в дополнение к требованиям к пространствам имен поставщиков.
Правила SEPolicy и property_contexts, относящиеся к поставщику
Свойства поставщика можно задать с помощью макроса vendor_internal_prop. Правила, относящиеся к поставщику, нужно поместить в каталог BOARD_VENDOR_SEPOLICY_DIRS.
Предположим, что вы определяете свойство vendor.faceauth в coral.
В файле BoardConfig.mk (или в любом файле, включенном в BoardConfig.mk) добавьте следующий код:
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
В файле device/google/coral-sepolicy/private/property.te укажите следующее:
vendor_internal_prop(vendor_faceauth_prop)
В файле device/google/coral-sepolicy/private/property_contexts укажите следующее:
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
Ограничения для свойств поставщиков
Поскольку разделы system и product не могут зависеть от поставщика, никогда не разрешайте доступ к свойствам поставщика из разделов system, system-ext или product.
Приложение. Как переименовать существующие свойства
Если вам нужно прекратить поддержку ресурса и перейти на новый, используйте системные свойства как API, чтобы переименовать существующие ресурсы. Это обеспечивает обратную совместимость, поскольку указываются и старое, и новое название свойства. В частности, вы можете задать устаревшее название в поле legacy_prop_name файла .sysprop. Сгенерированный API пытается прочитать prop_name и использует legacy_prop_name, если prop_name не существует.
Например, чтобы переименовать awesome_feature_foo_enabled в foo.awesome_feature.enabled, выполните следующие действия:
В файле foo.sysprop
module: "android.sysprop.foo"
owner: Platform
prop {
api_name: "is_awesome_feature_enabled"
type: Boolean
scope: Public
access: Readonly
prop_name: "foo.awesome_feature.enabled"
legacy_prop_name: "awesome_feature_foo_enabled"
}
В коде C++
// is_awesome_feature_enabled() reads "foo.awesome_feature.enabled".
// If it doesn't exist, reads "awesome_feature_foo_enabled" instead
using android::sysprop::foo;
bool enabled = foo::is_awesome_feature_enabled().value_or(false);
Обратите внимание на следующие ограничения:
Во-первых, нельзя изменить тип системного свойства. Например, нельзя превратить объект
intв объектstring. Можно только изменить название.Во-вторых, только API для чтения возвращается к устаревшему названию. API записи не поддерживает резервные копии. Если системное свойство доступно для записи, его нельзя переименовать.