Android system properties (SystemProperties)

1. System properties

System properties are key-value pairs with special meanings in the system. We sometimes need to use system properties during the development process, such as getting the system software version, getting the device name, etc. Sometimes we also need to set custom properties. System properties are global and easy to access.

2. Get and set

2.1 Architecture

2.2 Using the terminal

//Get system property values
getprop my.prop.test

//Set system property values
setprop my.prop.test

//Listen for system property changes
watchprops my.prop.test

2.3 Java code

There is a hidden class under android.os in the Android source code: SystemProperties. System properties can be set and obtained through SystemProperties.set and SystemProperties.get.

Part of the source code of Systemproperties is as follows: frameworks\base\core\java\android\os\SystemProperties.java

...
public class SystemProperties {
...
    //Get the value of the attribute key, if there is no such attribute, return the default value def
    @SystemApi
    public static String get(@NonNull String key, @Nullable String def) {
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        return native_get(key, def);
    }
...
    //Set the value of attribute key to val
    @SystemApi
    public static void set(@NonNull String key, @Nullable String val) {
        if (val != null & amp; & amp; !val.startsWith("ro.") & amp; & amp; val.length() > PROP_VALUE_MAX) {
            throw new IllegalArgumentException("value of system property '" + key
                     + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
        }
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        native_set(key, val);
    }
....
}

However, the interface of this class is not open to the outside world and needs to be used through reflection. Here is one of my SystemProperties utility classes:

public class SystemProperties {

    private final static String TAG = "SystemProperties";

    public static String get(String key) {
        Class<?> SysProp = null;
        Method method = null;
        String value = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("get", String.class);
            value = (String) method.invoke(null, key);
            Log.i(TAG, "value:" + value);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static String get(String key, String defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        String value = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("get", String.class, String.class);
            value = (String) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static int getInt(String key, int defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        int value = 0;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getInt", String.class, int.class);
            value = (Integer) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static long getLong(String key, long defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        long value = 0;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getLong", String.class, long.class);
            value = (Long) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static boolean getBoolean(String key, boolean defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        boolean value = false;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getBoolean", String.class, boolean.class);
            value = (Boolean) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static void set(String key, String value) {
        Class<?> SysProp = null;
        Method method = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("set", String.class, String.class);
            method.invoke(null, key, value);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
    }

    public static void addChangeCallback(Runnable runnable) {
        Class<?> SysProp = null;
        Method method = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("addChangeCallback", Runnable.class);
            method.invoke(null, runnable);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
    }
}

Take ActivityManagerService as an example below, frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
List examples of using prop in source code:

final void finishBooting() {
  ...
  //Set the boot completion flag attribute sys.boot_completed
  SystemProperties.set("sys.boot_completed", "1");
}
...
private static void maybePruneOldTraces(File tracesDir) {
  ...
  //Get tombstoned.max_anr_count attribute value
  final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
}

2.4 C++ code

Take MPEG4Writer.cpp as an example below.

frameworks\av\media\libstagefright\MPEG4Writer.cpp
List examples of using prop in source code:

#include <cutils/properties.h>
    ...
void MPEG4Writer::addDeviceMeta() {
    ...
    if (property_get("ro.build.version.release", val, NULL)
    ...
        if (property_get("ro.product.model", val, NULL)
    ...
}

Using prop in C++ code requires:

  1. include
  2. Android.mk or Android.bp or Makefile needs to link the libcutils library frameworks\av\media\libstagefright\Android.bp
shared_libs: [
        ...
        "libcutils",
        ...

The source code of properties.h is in: system/core/libcutils/include/cutils/properties.h

int property_get(const char* key, char* value, const char* default_value);
int property_set(const char *key, const char *value);
int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);

3. Special attributes

3.1 ro read-only attribute

Attributes such as ro (read only) are usually system default attributes and are set during system compilation or initialization.

$ getprop ro.vendor.build.version.release
10
$ setprop ro.vendor.build.version.release 9
setprop: failed to set property 'ro.vendor.build.version.release' to '9'

3.2 persist persistent attributes

Set the properties starting with persist, which can still be saved after power failure, and the value is written to data/property/persistent_properties.

$ getprop persist.hello.test //The property is empty
$ setprop persist.hello.test abc //Set the property persist.hello.test value to abc
$ getprop persist.hello.test abc //Attribute get is normal
abc
$reboot //Restart the device
$ getprop persist.hello.test //The attribute is abc
abc

3.3 ctl control attribute

setprop ctl.start xxx //Start a service
setprop ctl.stop xxx //Close a service
setprop ctl.restart xxx //Restart a service

3.4 sys.powerctl attribute

The sys.powerctl attribute can control device restart and shutdown.

setprop sys.powerctl shutdown //Device shutdown
setprop sys.powerctl reboot //Device reboot

3.5 Common attributes

Setting attributes starting with other formats cannot be saved after power off.

$ getprop hello.test //The attribute is empty
$ setprop hello.test 123//Set the property persist.hello.test value to abc
$ getprop hello.test 123//Attribute get is normal
123
$reboot //Restart the device
$ getprop hello.test //The property is empty

3.7 Add system default attributes

When the system is powered on, the properties in the *.prop property configuration file will be loaded, so there will be default properties after booting. Analysis of the property_load_boot_defaults(load_debug_prop) function in the init process.

Location: system\core\init\property_service.cpp

void property_load_boot_defaults(bool load_debug_prop) {
    std::map<std::string, std::string> properties;
    //Load properties build.prop, default.prop to properties
    if (!load_properties_from_file("/system/etc/prop.default", nullptr, & amp;properties)) {
        // Try recovery path
        if (!load_properties_from_file("/prop.default", nullptr, & amp;properties)) {
            // Try legacy path
            load_properties_from_file("/default.prop", nullptr, & amp;properties);
        }
    }
    load_properties_from_file("/system/build.prop", nullptr, & amp;properties);
    load_properties_from_file("/vendor/default.prop", nullptr, & amp;properties);
    load_properties_from_file("/vendor/build.prop", nullptr, & amp;properties);
    if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
        load_properties_from_file("/odm/etc/build.prop", nullptr, & amp;properties);
    } else {
        load_properties_from_file("/odm/default.prop", nullptr, & amp;properties);
        load_properties_from_file("/odm/build.prop", nullptr, & amp;properties);
    }
    load_properties_from_file("/product/build.prop", nullptr, & amp;properties);
    load_properties_from_file("/product_services/build.prop", nullptr, & amp;properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", & amp;properties);
    ...
    //Save the properties in properties into property properties through PropertySet
    for (const auto & amp; [name, value] : properties) {
        std::string error;
        if (PropertySet(name, value, & amp;error) != PROP_SUCCESS) {
            LOG(ERROR) << "Could not set '" << name << "' to '" << value
                       << "' while loading .prop files" << error;
        }
    }
    property_initialize_ro_product_props();//Initialize the read-only properties of the ro_product prefix
    property_derive_build_props();//Initialize compilation related properties
    update_sys_usb_config();//Set the persist.sys.usb.config property to control USB debugging and file transfer functions
}

As can be seen from the above code, system properties are loaded into the properties variable from multiple property files.prop, and then properties are added to the system through PropertySet(), and read-only, compiled, and USB-related property values are initialized. In PropertySet(), it communicates with the property service through Socket and stores props in shared memory.

Here is an example of adding system properties to device/google/marlin/system.prop:

# Add your own system default properties
persist.hello.world=hello

Note: The attribute prefix added here must be defined in system/sepolicy/private/property_contexts, otherwise it will be invalid. After making android, you can find the added attribute persist.hello.world in out/target/product/xxx/system/build.prop or out/target/product/xxx/vendor/build.prop, which means the addition is basically successful.

4. Add customized *.prop

The code paths involved are summarized as follows:

device/qcom/qssi/hello.prop
device/qcom/qssi/qssi.mk
device/qcom/sepolicy/generic/private/property_contexts
system/core/rootdir/init.rc
system/core/init/property_service.cpp

In order to facilitate unified management of customized attributes, sometimes customized attributes are written in customized .prop files. The following takes adding hello.prop as an example to illustrate the adding process.

Add hello.prop under 4.1 device

device/qcom/qssi/hello.prop

#
# system.prop for qssi
#
ro.hello.year=2022 //Add ro attribute
persist.hello.month=07 //Add persist attribute
hello.day=25 //Add hello attribute

ro.product.model=HelloWorld //The customized system already has the ro.product.model attribute

4.2 Configure preset path

Modify device.mk under device to specify the preset path of hello.prop device/qcom/qssi/qssi.mk

#Preset hello.prop to system/hello.prop
PRODUCT_COPY_FILES + = \
    device/qcom/qssi/hello.prop:system/hello.prop

4.3 SELinux permission configuration

The properties starting with hello. are newly added configurations. You need to configure the corresponding SELinux rules, otherwise they will be invalid. The configuration method is as follows: device/qcom/sepolicy/generic/private/property_contexts

hello. u:object_r:system_prop:s0

4.4 Configure hello.prop permissions

This step can be omitted. If read and write permissions are not configured, the default system/prop is 644. Here, configure the same 600 permissions as system/build.prop system/core/rootdir/init.rc

on fs
   chmod 0600 /system/hello.prop

4.5 Load hello.prop

It is not enough to preset hello.prop to system/hello.prop. You need to load hello.prop when the system starts to make it effective system/core/init/property_service.cpp

load_properties_from_file("/system/build.prop", nullptr, & amp;properties);
   load_properties_from_file("/vendor/default.prop", nullptr, & amp;properties);
   load_properties_from_file("/vendor/build.prop", nullptr, & amp;properties);
   if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
       load_properties_from_file("/odm/etc/build.prop", nullptr, & amp;properties);
   } else {
       load_properties_from_file("/odm/default.prop", nullptr, & amp;properties);
       load_properties_from_file("/odm/build.prop", nullptr, & amp;properties);
   }
   load_properties_from_file("/product/build.prop", nullptr, & amp;properties);
   load_properties_from_file("/product_services/build.prop", nullptr, & amp;properties);
   load_properties_from_file("/factory/factory.prop", "ro.*", & amp;properties);
   //load the preset hello.prop. The last load ensures that its configuration properties have a higher priority.
   load_properties_from_file("/system/hello.prop", nullptr, & amp;properties);

4.6 Verification

After Android is fully compiled, the generated out/target/product/qssi/system/hello.prop can be found normally.

Check that its content should be consistent with the content of device/qcom/qssi/hello.prop.