Add system properties

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 as audio.awesome_feature_enabled or just audio.awesome_feature, rename it as audio.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 delimiter
The 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.

Decision tree for determining the scope of 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 (Use string 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 or product.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 a string 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.