This page provides a canonical method for adding or defining system properties in Android, with guidelines for refactoring existing system properties. Ensure that you use the guidelines when you refactor, unless you have a strong compatibility issue that dictates otherwise.
Step 1: Define the system property
When you add a system property, decide on a name for the property, and associate it with an SELinux property context. If there's no appropriate existing context, create a new one. The name is used when accessing the property; the property context is used to control accessibility in terms of SELinux. Names can be any string, but AOSP recommends that you follow a structured format to make them clear.
Property name
Use this format with snake_case casing:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Use either "" (omitted), ro
(for properties set only once), or persist
(for properties that persist across reboots) for the element prefix
.
Caveats
Use ro
only when you're certain that you don't need prefix
to be writable
in the future. ** Don't specify the ro
prefix.** Instead, rely on sepolicy to
make prefix
read-only (in other words, writable only by init
).
Use persist
only when you're certain that the value must be persisted across
reboots, and that using the system properties is your only option.
Google strictly reviews the system properties that have either ro
or persist
properties.
The term group
is used to aggregate related properties. It's intended to
be a subsystem name similar in use to audio
or telephony
. Don't use
ambiguous or overloaded terms such as sys
, system
, dev
, default
, or
config
.
It's common practice to use the name of the domain type of a
process that has exclusive read or write access to the system properties. For
example, for the system properties to which the vold
process has write access,
it's common to use vold
(the name of the domain type for the process) as the
group name.
If needed, add subgroup
to further categorize properties, but avoid
ambiguous or overloaded terms to describe this element. (You can also have more
than one subgroup
.)
Many group names have already been defined. Check the
system/sepolicy/private/property_contexts
file and use existing group names
where possible, instead of making new ones. The following table provides
examples of frequently used group names.
Domain | Group (and subgroup) |
---|---|
bluetooth related | bluetooth |
sysprops from kernel cmdline | boot |
sysprops that identify a build | build
|
telephony related | telephony |
audio related | audio |
graphics related | graphics |
vold related | vold |
The following defines the use of name
and type
in the previous regex
example.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
identifies a system property within a group.type
is an optional element that clarifies the type or intent of the system property. For example, instead of naming a sysprop asaudio.awesome_feature_enabled
or justaudio.awesome_feature
, rename it asaudio.awesome_feature.enabled
to reflect the system property type and intent.
There's no specific rule about what the type must be; these are usage recommendations:
enabled
: Use if the type is a boolean system property that's used to turn a feature on or off.config
: Use if the intent is to clarify that the system property doesn't represent a dynamic state of the system; it represents a preconfigured value (for example, a read-only thing).List
: Use if it's a system property whose value is a list.Timeoutmillis
: Use if it's a system property for a timeout value in units of ms.
Examples:
persist.radio.multisim.config
drm.service.enabled
Property context
The new SELinux property context scheme allows for finer granularity and more descriptive names. Similar to what's used for property names, AOSP recommends the following format:
{group}[_{subgroup}]*_prop
The terms are defined as follows:
group
and subgroup
have the same meaning as defined for the previous
sample regex. For example, vold_config_prop
signifies
properties which are configurations from a vendor and meant to be set by
vendor_init
, while vold_status_prop
or just vold_prop
signifies properties
which are to expose the current status of vold
.
When naming a property context, choose names that reflect the general usage of the properties. In particular, avoid the following types of terms:
- Terms which look too general and ambiguous, such as
sys
,system
,default
. - Terms that directly encode accessibility: such as
exported
,apponly
,ro
,public
,private
.
Prefer name usages like vold_config_prop
to exported_vold_prop
,
or vold_vendor_writable_prop
.
Type
A property type can be one of the following as listed in the table.
Type | Definition |
---|---|
Boolean | true or 1 for true, false or 0 for false |
Integer | signed 64-bit integer |
Unsigned integer | unsigned 64-bit integer |
Double | double-precision floating point |
String | any valid UTF-8 string |
enum | values can be any valid UTF-8 string without whitespaces |
List of above | A comma (, ) is used as the delimiterThe integer list [1, 2, 3] is stored as 1,2,3 |
Internally, all properties are stored as strings. You can enforce the type by
specifying it as a property_contexts
file. For more information, see
property_contexts
in Step 3.
Step 2: Determine required accessibility levels
There are four helper macros that define a property.
Accessibility type | Meaning |
---|---|
system_internal_prop |
Properties which are used only in /system |
system_restricted_prop |
Properties which are read outside /system , but not written |
system_vendor_config_prop |
Properties which are read outside /system , and written only by vendor_init |
system_public_prop |
Properties which are read and written outside /system |
Scope the access to system properties as narrowly as possible. In the past, broad access has resulted in app breakage and security vulnerabilities. Consider the following questions when scoping:
- Does this system property need to be persisted? (if so, why?)
- Which process should have read access to this property?
- Which process should have write access to this property?
Use the preceding questions and the following decision tree as tools for determining an appropriate scope for access.
Figure 1. Decision tree for determining scope of access to system properties
Step 3: Add to system/sepolicy
When accessing sysprop, SELinux controls the accessibility of processes. After
you determine what level of accessibility is required, define property contexts
under system/sepolicy
, along with additional allow and neverallow rules
about what the processes are (and aren't) allowed to read or write.
First, define the property context in the system/sepolicy/public/property.te
file. If the property is system-internal, define it in the
system/sepolicy/private/property.te
file. Use one of
thesystem_[accessibility]_prop([context])
macros that provides the
accessibility required of your system property. This is an example for the
system/sepolicy/public/property.te
file:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Example to add in the system/sepolicy/private/property.te
file:
system_internal_prop(audio_baz_prop)
Second, grant read and (or) write access to the property context. Use set_prop
and get_prop
macros to grant access, in either the
system/sepolicy/public/{domain}.te
or system/sepolicy/private/{domain}.te
file. Use private
whenever possible; public
is suitable only if the
set_prop
or get_prop
macro affects any domains outside the core domain.
Example, in the system/sepolicy/private/audio.te
file:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Example, in the system/sepolicy/public/domain.te
file:
get_prop(domain, audio_bar_prop)
Third, add some neverallow rules to further reduce the accessibility that's
scoped by the macro. For example, assume that you've used
system_restricted_prop
because your system properties must be read by vendor
processes. If the read access isn't required by all vendor processes, and it's
only required by a certain set of processes (such as vendor_init
), prohibit
the vendor processes that don't need the read access.
Use the following syntax for restricting write and read access:
To restrict write access:
neverallow [domain] [context]:property_service set;
To restrict read access:
neverallow [domain] [context]:file no_rw_file_perms;
Place neverallow rules in the system/sepolicy/private/{domain}.te
file if the
neverallow rule is bound to a specific domain. For broader neverallow rules, use
general domains such as these wherever appropriate:
system/sepolicy/private/property.te
system/sepolicy/private/coredomain.te
system/sepolicy/private/domain.te
In the system/sepolicy/private/audio.te
file, place the following:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
In the system/sepolicy/private/property.te
file, place the following:
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
Note that {domain -coredomain}
captures all vendor processes. So
{domain -coredomain -vendor_init}
means "all vendor processes except
vendor_init
."
Finally, associate a system property with the property context. This ensures
that the access that's granted and the neverallow rules that are applied to
property contexts are applied to actual properties. To do this, add an entry to
the property_contexts
file, a file that describes mapping between system
properties and property contexts. In this file, you can specify either a single
property, or a prefix for properties to be mapped into a context.
This is the syntax for mapping a single property:
[property_name] u:object_r:[context_name]:s0 exact [type]
This is the syntax for mapping a prefix:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
You can optionally specify the type of the property, which can be one of the following:
bool
int
uint
double
enum [list of possible values...]
string
(Usestring
for list properties.)
Ensure that every entry has its designated type whenever possible, as type
is
enforced when setting property
. The following example shows how to write a
mapping:
# 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
When an exact entry and a prefix entry conflict, the exact entry takes
precedence. For more examples, see system/sepolicy/private/property_contexts
.
Step 4: Determine stability requirements
Stability is another aspect of system properties, and it differs from accessibility. Stability is about whether or not a system property can be changed (for example renamed, or even removed) in the future. This is particularly important as Android OS becomes modular. With Treble, the system, vendor, and product partitions can be updated independently of one another. With Mainline, some parts of the OS are modularized as updatable modules (in APEXes or APKs).
If a system property is for use across updatable pieces of software, for example across system and vendor partitions, it must be stable. However, if it's used only within, for example, a specific Mainline module, you can change its name, type, or property contexts, and even remove it.
Ask the following questions to determine the stability of a system property:
- Is this system property intended to be configured by partners (or configured differently per device)? If yes, it must be stable.
- Is this AOSP-defined system property intended to be written to or read from
code (not process) that exists in non-system partitions like
vendor.img
orproduct.img
? If yes, it must be stable. - Is this system property accessed across Mainline modules or across a Mainline module and the non-updatable part of the platform? If yes, it must be stable.
For the stable system properties, formally define each as an API and use the API to access the system property, as explained in Step 6.
Step 5: Set properties at build time
Set properties at build time with makefile variables. Technically, the values
are built into {partition}/build.prop
. Then init
reads
{partition}/build.prop
to set the properties. There are two sets of such
variables: PRODUCT_{PARTITION}_PROPERTIES
and TARGET_{PARTITION}_PROP
.
PRODUCT_{PARTITION}_PROPERTIES
contains a list of property values. The syntax
is {prop}={value}
or {prop}?={value}
.
{prop}={value}
is a normal assignment which ensures that {prop}
is set to
{value}
; only one such assignment is possible per a single property.
{prop}?={value}
is an optional assignment; {prop}
sets to {value}
only if
there aren't any {prop}={value}
assignments. If multiple optional assignments
exist, the first one wins.
# 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
contains a list of files, which is directly emitted to
{partition}/build.prop
. Each file contains a list of {prop}={value}
pairs.
# 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
For more detail, see
build/make/core/sysprop.mk
.
Step 6: Access properties at runtime
Properties can be read and written at runtime.
Init scripts
Init script files (usually *.rc files) can read a property by ${prop}
or
${prop:-default}
, can set an action which runs whenever a property becomes a
specific value, and can write the properties using the setprop
command.
# 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 and setprop shell commands
You can use the getprop
or setprop
shell commands, respectively, to read or
write the properties. For more details, invoke getprop --help
or
setprop --help
.
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop as API for C++/Java/Rust
With sysprop as API, you can define system properties and use auto-generated API
which are concrete and typed. Setting scope
with Public
also makes generated
APIs available to modules across boundaries, and ensures API stability. Here's a
sample of a .sysprop
file, an Android.bp
module, and C++, Java and Rust code
using them.
# 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);
For more information, see Implement system properties as APIs.
C/C++, Java, and Rust low-level property functions and methods
When possible, use Sysprop as API even though low-level C/C++ or Rust functions or low-level Java methods are available to you.
libc
, libbase
, and libcutils
offer C++ system property functions. libc
has the underlying API, while the libbase
and libcutils
functions are
wrappers. If it's possible, use the libbase
sysprop functions; they're the
most convenient, and host binaries can use the libbase
functions. For more
details, see sys/system_properties.h
(libc
), android-base/properties.h
(libbase
), and cutils/properties.h
(libcutils
).
The android.os.SystemProperties
class offers Java system property methods.
The rustutils::system_properties
module offers Rust system property functions
and types.
Appendix: Add vendor-specific properties
Partners (including Googlers working in the context of Pixel development) want
to define hardware-specific (or device-specific) system properties.
Vendor-specific properties are partner-owned properties that are unique to their
own hardware or device, not to the platform. As these are hardware or device
dependent, they're meant to be used within the /vendor
or /odm
partitions.
Since Project Treble, the platform properties and vendor properties have been completely split to keep them from conflicting. The following describes how to define vendor properties, and tells which vendor properties must always be used.
Namespace on property and context names
All vendor properties must start with one of the following prefixes to prevent collision between them and the properties of other partitions.
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.
Note that ro.hardware.
is allowed as a prefix, but for only compatibility.
Don't use it for normal properties.
The following examples all use one of the preceding listed prefixes:
vendor.display.primary_red
persist.vendor.faceauth.use_disk_cache
ro.odm.hardware.platform
All vendor property contexts must start with vendor_
. This is also for
compatibility. The following are examples:
vendor_radio_prop
.vendor_faceauth_prop
.vendor_usb_prop
.
It's the vendor's responsibility to name and maintain properties, so follow the format suggested in Step 2, in addition to the vendor namespaces requirements.
Vendor-specific SEPolicy rules and property_contexts
Vendor properties can be defined by vendor_internal_prop
macro. Put the
vendor-specific rules you define in the BOARD_VENDOR_SEPOLICY_DIRS
directory.
For example, suppose that you're defining a vendor faceauth property in coral.
In the BoardConfig.mk
file (or in any BoardConfig.mk
includes), put the
following:
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
In the device/google/coral-sepolicy/private/property.te
file, put the
following:
vendor_internal_prop(vendor_faceauth_prop)
In the device/google/coral-sepolicy/private/property_contexts
file, put the
following:
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
Limitations of vendor properties
Because the system and product partitions can't depend on the vendor, never
allow the vendor properties to be accessed from the system
, system-ext
, or
product
partitions.
Appendix: Rename existing properties
When you must deprecate a property and move to a new one, use Sysprop as APIs
to rename your existing properties. This maintains backward compatibility by
specifying both the legacy name and the new property name. Specifically, you can
set the legacy name by the legacy_prop_name
field in the .sysprop
file. The
generated API tries to read prop_name
, and uses legacy_prop_name
if
prop_name
doesn't exist.
For example, the following steps rename awesome_feature_foo_enabled
to foo.awesome_feature.enabled
.
In the foo.sysprop
file
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"
}
In C++ code
// 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);
Note the following caveats:
First, you can't change the type of the sysprop. For example, you can't make an
int
prop into astring
prop. You can only change the name.Second, only the read API falls back to the legacy name. The write API doesn't fall back. If the sysprop is a writable one, you can't rename it.