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

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