На этой странице представлен канонический метод добавления или определения системных свойств в 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. Добавьте в систему/sepolicy.
При доступе к sysprop SELinux контролирует доступность процессов. После того, как вы определите, какой уровень доступности требуется, определите контексты свойств в разделе system/sepolicy
, а также дополнительные правила разрешения и никогда не разрешают, описывающие, какие процессы разрешены (и не разрешены) для чтения или записи.
Сначала определите контекст свойства в файле 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.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
» .
Наконец, свяжите системное свойство с контекстом свойства. Это гарантирует, что предоставленный доступ и правила Neverallow, применяемые к контекстам свойств, будут применены к фактическим свойствам. Для этого добавьте запись в файл 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).
Если системное свойство предназначено для использования в обновляемых частях программного обеспечения, например в разделах системы и поставщика, оно должно быть стабильным. Однако, если он используется только, например, в определенном модуле 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
являются оболочками. Если это возможно, используйте функции 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
.
Начиная с 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_red
-
persist.vendor.faceauth.use_disk_cache
-
ro.odm.hardware.platform
Все контексты свойств поставщика должны начинаться с vendor_
. Это также для совместимости. Ниже приведены примеры:
-
vendor_radio_prop
. -
vendor_faceauth_prop
. -
vendor_usb_prop
.
Ответственность за присвоение и поддержку свойств лежит на поставщике, поэтому следуйте формату, предложенному на шаге 2 , в дополнение к требованиям к пространствам имен поставщика.
Правила SEPolicy и property_contexts, зависящие от поставщика
Свойства поставщика можно определить с помощью vendor_internal_prop
. Поместите определенные вами правила для конкретного поставщика в каталог BOARD_VENDOR_SEPOLICY_DIRS
. Например, предположим, что вы определяете свойство faceauth поставщика в корале.
В файле 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
.
Приложение: переименование существующих свойств
Если вам необходимо объявить свойство устаревшим и перейти к новому, используйте 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"
}
В коде С++
// 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 записи не отступает. Если sysprop доступен для записи, вы не можете его переименовать.