На этой странице представлен канонический метод добавления или определения системных свойств в Android, а также рекомендации по рефакторингу существующих системных свойств. Обязательно используйте эти рекомендации при рефакторинге, если только у вас нет серьезных проблем с совместимостью, требующих иного подхода.
Шаг 1: Определите свойство системы.
При добавлении системного свойства выберите для него имя и свяжите его с контекстом свойства SELinux. Если подходящего контекста нет, создайте новый. Имя используется при доступе к свойству; контекст свойства используется для управления доступом с точки зрения SELinux. Имена могут быть любыми строками, но AOSP рекомендует использовать структурированный формат для обеспечения их понятности.
Название объекта недвижимости
Используйте этот формат с регистром snake_case:
[{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 |
Ниже приведено описание использования 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.config -
drm.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 для true, false или 0 для false |
| Целое число | знаковое 64-битное целое число |
| Беззнаковое целое число | беззнаковое 64-битное целое число |
| Двойной | число с плавающей запятой двойной точности |
| Нить | любая допустимая строка UTF-8 |
| перечисление | В качестве значений может использоваться любая допустимая строка 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: Добавить в system/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 привязано к конкретному домену, размещайте правила neverallow в файле system/sepolicy/private/{domain}.te Для более общих правил neverallow используйте общие домены, например, такие, где это уместно:
-
system/sepolicy/private/property.te -
system/sepolicy/private/coredomain.te -
system/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 ".
Наконец, свяжите системное свойство с контекстом свойства. Это гарантирует, что предоставленный доступ и правила «никогда не разрешать», применяемые к контекстам свойств, будут применяться к фактическим свойствам. Для этого добавьте запись в файл property_contexts , описывающий сопоставление между системными свойствами и контекстами свойств. В этом файле можно указать либо одно свойство, либо префикс для свойств, которые должны быть сопоставлены с контекстом.
Вот синтаксис для сопоставления одного свойства:
[property_name] u:object_r:[context_name]:s0 exact [type]
Вот синтаксис для сопоставления префикса:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
При желании вы можете указать тип объекта недвижимости, который может быть одним из следующих:
-
bool -
int -
uint -
double -
enum [list of possible values...] -
string(Используйтеstringдля свойств списка.)
По возможности убедитесь, что каждая запись имеет свой заданный тип, поскольку type определяется при установке property . В следующем примере показано, как написать сопоставление:
# 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).
Если свойство системы используется в обновляемых программных компонентах, например, в разных системных и заводских разделах, оно должно быть стабильным. Однако, если оно используется только, например, в конкретном модуле основной ветки, вы можете изменить его имя, тип или контекст свойств и даже удалить его.
Чтобы определить стабильность свойства системы, задайте следующие вопросы:
- Предназначена ли данная системная настройка для партнеров (или она настраивается по-разному для каждого устройства)? Если да, то она должна быть стабильной.
- Предназначено ли это определенное в AOSP системное свойство для записи или чтения из кода (а не процесса), находящегося в несистемных разделах, таких как
vendor.imgилиproduct.img? Если да, то оно должно быть стабильным. - Доступ к этому системному свойству осуществляется между основными модулями или между основным модулем и не подлежащей обновлению частью платформы? Если да, то оно должно быть стабильным.
Для стабильных свойств системы формально определите каждое из них как 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/Russ
Используя 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 являются обертками. По возможности используйте функции sysprop libbase ; они наиболее удобны, и исполняемые файлы хоста могут использовать функции 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 .
Начиная с проекта 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_red -
persist.vendor.faceauth.use_disk_cache -
ro.odm.hardware.platform
Все контексты свойств поставщика должны начинаться с vendor_ . Это также необходимо для обеспечения совместимости. Ниже приведены примеры:
-
vendor_radio_prop. -
vendor_faceauth_prop. -
vendor_usb_prop.
Ответственность за присвоение имен и поддержку свойств лежит на поставщике, поэтому, помимо требований к пространствам имен поставщика, следуйте формату, предложенному в Шаге 2 .
Правила SEPolicy, специфичные для поставщика, и контексты свойств.
Свойства поставщика можно определить с помощью макроса vendor_internal_prop . Разместите определенные вами правила для конкретного поставщика в каталоге BOARD_VENDOR_SEPOLICY_DIRS . Например, предположим, что вы определяете свойство 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 , system-ext или раздела product .
Приложение: Переименование существующих объектов
Когда необходимо объявить свойство устаревшим и перейти к новому, используйте Sysprop в качестве 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);
Обратите внимание на следующие оговорки:
Во-первых, вы не можете изменить тип свойства `sysprop`. Например, вы не можете превратить свойство `
intв свойство `string. Вы можете изменить только имя.Во-вторых, только API чтения использует устаревшее имя. API записи не использует это имя. Если sysprop доступен для записи, его нельзя переименовать.