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

На этой странице представлен канонический метод добавления или определения системных свойств в 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 — необязательный элемент, который поясняет тип или намерение системного свойства. Например, вместо того, чтобы называть sysprop как 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 имеют то же значение, что и определенное для предыдущего примера regex . Например, 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 привязано к определенному домену. Для более широких правил 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 являются оболочками. Если возможно, используйте функции 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.

Приложение: Добавьте свойства, специфичные для поставщика

Партнеры (включая Googlers, работающих в контексте разработки 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 в 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 prop string prop. Вы можете изменить только имя.

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