Добавить свойства системы

На этой странице представлен канонический метод добавления или определения системных свойств в 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

Ограничьте доступ к системным свойствам как можно более узко. В прошлом широкий доступ приводил к сбоям в работе приложений и уязвимостям безопасности. При определении области действия учтите следующие вопросы:

  • Необходимо ли сохранять это системное свойство? (если да, то почему?)
  • Какой процесс должен иметь доступ на чтение этого свойства?
  • Какой процесс должен иметь доступ на запись к этому свойству?

Используйте предыдущие вопросы и следующее дерево решений в качестве инструментов для определения соответствующей области доступа.

Decision tree for determining the scope of access

Рисунок 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 привязано к конкретному домену, разместите его в файле 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).

Если системное свойство предназначено для использования в обновляемых частях программного обеспечения, например, в системных и вендорных разделах, оно должно быть стабильным. Однако, если оно используется только, например, в конкретном модуле Mainline, вы можете изменить его имя, тип или контексты свойств, а также удалить его.

Задайте следующие вопросы, чтобы определить устойчивость свойства системы:

  • Предполагается ли, что это системное свойство будет настраиваться партнёрами (или настраиваться по-разному для каждого устройства)? Если да, оно должно быть стабильным.
  • Предназначено ли это системное свойство, определяемое 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/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 .

Начиная с проекта 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 поставщика в 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);

Обратите внимание на следующие предостережения:

  • Во-первых, вы не можете изменить тип системного свойства. Например, вы не можете преобразовать свойство int в свойство типа string . Можно изменить только имя.

  • Во-вторых, только API чтения возвращается к старому имени. API записи не возвращается. Если системное свойство доступно для записи, его нельзя переименовать.