Agrega propiedades del sistema

En esta página, se proporciona un método canónico para agregar o definir propiedades del sistema en Android, con instrucciones para refactorizar las propiedades del sistema existentes. Asegúrate de usar los lineamientos cuando refactorices, a menos que tengas un problema de compatibilidad grave que indique lo contrario.

Paso 1: Define la propiedad del sistema

Cuando agregas una propiedad del sistema, debes decidir un nombre para la propiedad y asociarlo con un contexto de propiedad de SELinux. Si no hay un contexto existente adecuado, crea uno nuevo. El nombre se usa cuando se accede a la propiedad; el contexto de la propiedad se usa para controlar la accesibilidad en términos de SELinux. Los nombres pueden ser cualquier cadena, pero el AOSP recomienda que sigas un formato estructurado para que sean claros.

Nombre de la propiedad

Usa este formato con la convención de escritura snake_case:

[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]

Usa "" (omitido), ro (para las propiedades establecidas solo una vez) o persist (para las propiedades que persisten después de los reinicios) para el elemento prefix.

Advertencias

Usa ro solo cuando tengas la certeza de que no necesitarás que prefix sea grabable en el futuro. ** No especifiques el prefijo ro.** En cambio, confía en sepolicy para que prefix sea de solo lectura (en otras palabras, solo init puede escribir en él).

Usa persist solo cuando tengas la certeza de que el valor debe persistir en los reinicios y de que usar las propiedades del sistema es tu única opción.

Google revisa estrictamente las propiedades del sistema que tienen propiedades ro o persist.

El término group se usa para agregar propiedades relacionadas. Se diseñó para ser un nombre de subsistema similar en uso a audio o telephony. No uses términos ambiguos o sobrecargados, como sys, system, dev, default o config.

Es una práctica común usar el nombre del tipo de dominio de un proceso que tiene acceso exclusivo de lectura o escritura a las propiedades del sistema. Por ejemplo, para las propiedades del sistema a las que el proceso vold tiene acceso de escritura, es común usar vold (el nombre del tipo de dominio para el proceso) como el nombre del grupo.

Si es necesario, agrega subgroup para categorizar aún más las propiedades, pero evita los términos ambiguos o sobrecargados para describir este elemento. (También puedes tener más de un subgroup).

Ya se definieron muchos nombres de grupos. Revisa el archivo system/sepolicy/private/property_contexts y usa los nombres de grupos existentes siempre que sea posible, en lugar de crear grupos nuevos. En la siguiente tabla, se proporcionan ejemplos de nombres de grupos que se usan con frecuencia.

Dominio Grupo (y subgrupo)
Relacionado con Bluetooth bluetooth
sysprops de la línea de comandos del kernel boot
sysprops que identifican una compilación build
Relacionado con la telefonía telephony
Relacionado con el audio audio
Gráficos relacionados graphics
Relacionado con el volumen vold

A continuación, se define el uso de name y type en el ejemplo de regex anterior.

[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]

  • name identifica una propiedad del sistema dentro de un grupo.

  • type es un elemento opcional que aclara el tipo o la intención de la propiedad del sistema. Por ejemplo, en lugar de nombrar una sysprop como audio.awesome_feature_enabled o simplemente audio.awesome_feature, cámbiale el nombre a audio.awesome_feature.enabled para reflejar el tipo y la intención de la propiedad del sistema.

No hay una regla específica sobre qué tipo debe ser; estas son recomendaciones de uso:

  • enabled: Se usa si el tipo es una propiedad del sistema booleana que se usa para activar o desactivar una función.
  • config: Se usa si la intención es aclarar que la propiedad del sistema no representa un estado dinámico del sistema, sino un valor preconfigurado (por ejemplo, un elemento de solo lectura).
  • List: Se usa si es una propiedad del sistema cuyo valor es una lista.
  • Timeoutmillis: Se usa si es una propiedad del sistema para un valor de tiempo de espera en unidades de ms.

Ejemplos:

  • persist.radio.multisim.config
  • drm.service.enabled

Contexto de la propiedad

El nuevo esquema de contexto de propiedades de SELinux permite una mayor granularidad y nombres más descriptivos. Al igual que con los nombres de propiedades, el AOSP recomienda el siguiente formato:

{group}[_{subgroup}]*_prop

Los términos se definen de la siguiente manera:

group y subgroup tienen el mismo significado que se definió para la regex de ejemplo anterior. Por ejemplo, vold_config_prop significa propiedades que son configuraciones de un proveedor y que vendor_init debe establecer, mientras que vold_status_prop o solo vold_prop significa propiedades que deben exponer el estado actual de vold.

Cuando asignes un nombre a un contexto de propiedad, elige nombres que reflejen el uso general de las propiedades. En particular, evita los siguientes tipos de términos:

  • Términos que parecen demasiado generales y ambiguos, como sys, system y default
  • Términos que codifican directamente la accesibilidad, como exported, apponly, ro, public y private.

Prefiere el uso de nombres como vold_config_prop en lugar de exported_vold_prop o vold_vendor_writable_prop.

Tipo

Un tipo de propiedad puede ser uno de los siguientes, como se indica en la tabla.

Tipo Definición
Booleano true o 1 para verdadero, false o 0 para falso
Entero Número entero de 64 bits con signo
Número entero sin signo Número entero de 64 bits sin signo
Double punto flotante de doble precisión
String Cualquier cadena UTF-8 válida
enum Los valores pueden ser cualquier cadena UTF-8 válida sin espacios en blanco.
Lista de lo anterior Se usa una coma (,) como delimitador
La lista de números enteros [1, 2, 3] se almacena como 1,2,3

Internamente, todas las propiedades se almacenan como cadenas. Puedes aplicar el tipo especificándolo como un archivo property_contexts. Para obtener más información, consulta property_contexts en el paso 3.

Paso 2: Determina los niveles de accesibilidad requeridos

Existen cuatro macros auxiliares que definen una propiedad.

Tipo de accesibilidad Significado
system_internal_prop Propiedades que solo se usan en /system
system_restricted_prop Propiedades que se leen fuera de /system, pero no se escriben
system_vendor_config_prop Propiedades que se leen fuera de /system y que solo escribe vendor_init
system_public_prop Propiedades que se leen y escriben fuera de /system

Limita el acceso a las propiedades del sistema lo más posible. En el pasado, el acceso amplio generó fallas en las apps y vulnerabilidades de seguridad. Ten en cuenta las siguientes preguntas cuando definas el alcance:

  • ¿Es necesario conservar esta propiedad del sistema? (si es así, ¿por qué?)
  • ¿Qué proceso debería tener acceso de lectura a esta propiedad?
  • ¿Qué proceso debería tener acceso de escritura a esta propiedad?

Usa las preguntas anteriores y el siguiente árbol de decisión como herramientas para determinar un alcance adecuado para el acceso.

Árbol de decisión para determinar el alcance del acceso

Figura 1: Árbol de decisión para determinar el alcance del acceso a las propiedades del sistema

Paso 3: Agrega a system/sepolicy

Cuando se accede a sysprop, SELinux controla la accesibilidad de los procesos. Después de determinar qué nivel de accesibilidad se requiere, define los contextos de propiedad en system/sepolicy, junto con reglas adicionales de allow y neverallow sobre lo que los procesos pueden (y no pueden) leer o escribir.

Primero, define el contexto de la propiedad en el archivo system/sepolicy/public/property.te. Si la propiedad es interna del sistema, defínela en el archivo system/sepolicy/private/property.te. Usa una de las macros de system_[accessibility]_prop([context]) que proporciona la accesibilidad requerida de la propiedad del sistema. Este es un ejemplo del archivo system/sepolicy/public/property.te:

system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)

Ejemplo para agregar en el archivo system/sepolicy/private/property.te:

system_internal_prop(audio_baz_prop)

En segundo lugar, otorga acceso de lectura o escritura al contexto de la propiedad. Usa las macros set_prop y get_prop para otorgar acceso en el archivo system/sepolicy/public/{domain}.te o system/sepolicy/private/{domain}.te. Usa private siempre que sea posible. public solo es adecuado si la macro de set_prop o get_prop afecta a dominios fuera del dominio principal.

Ejemplo en el archivo system/sepolicy/private/audio.te:

set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)

Ejemplo en el archivo system/sepolicy/public/domain.te:

get_prop(domain, audio_bar_prop)

En tercer lugar, agrega algunas reglas de neverallow para reducir aún más la accesibilidad que abarca la macro. Por ejemplo, supongamos que usaste system_restricted_prop porque los procesos del proveedor deben leer las propiedades del sistema. Si todos los procesos del proveedor no requieren acceso de lectura, y solo un conjunto determinado de procesos (como vendor_init) lo requiere, prohíbe los procesos del proveedor que no necesiten el acceso de lectura.

Usa la siguiente sintaxis para restringir el acceso de lectura y escritura:

Para restringir el acceso de escritura, sigue estos pasos:

neverallow [domain] [context]:property_service set;

Para restringir el acceso de lectura, haz lo siguiente:

neverallow [domain] [context]:file no_rw_file_perms;

Coloca las reglas neverallow en el archivo system/sepolicy/private/{domain}.te si la regla neverallow está vinculada a un dominio específico. Para reglas neverallow más amplias, usa dominios generales como los siguientes cuando sea apropiado:

  • system/sepolicy/private/property.te
  • system/sepolicy/private/coredomain.te
  • system/sepolicy/private/domain.te

En el archivo system/sepolicy/private/audio.te, coloca lo siguiente:

neverallow {
    domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;

En el archivo system/sepolicy/private/property.te, coloca lo siguiente:

neverallow {
    domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;

Ten en cuenta que {domain -coredomain} captura todos los procesos del proveedor. Por lo tanto, {domain -coredomain -vendor_init} significa "todos los procesos del proveedor excepto vendor_init".

Por último, asocia una propiedad del sistema con el contexto de la propiedad. Esto garantiza que el acceso que se otorga y las reglas de neverallow que se aplican a los contextos de propiedad se apliquen a las propiedades reales. Para ello, agrega una entrada al archivo property_contexts, que describe la asignación entre las propiedades del sistema y los contextos de propiedad. En este archivo, puedes especificar una sola propiedad o un prefijo para las propiedades que se asignarán a un contexto.

Esta es la sintaxis para asignar una sola propiedad:

[property_name] u:object_r:[context_name]:s0 exact [type]

Esta es la sintaxis para asignar un prefijo:

[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]

De manera opcional, puedes especificar el tipo de propiedad, que puede ser uno de los siguientes:

  • bool
  • int
  • uint
  • double
  • enum [list of possible values...]
  • string (usa string para las propiedades de lista).

Asegúrate de que cada entrada tenga su tipo designado siempre que sea posible, ya que type se aplica cuando se establece property. En el siguiente ejemplo, se muestra cómo escribir una asignación:

# 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

Cuando una entrada exacta y una entrada de prefijo entran en conflicto, la entrada exacta tiene prioridad. Para obtener más ejemplos, consulta system/sepolicy/private/property_contexts.

Paso 4: Determina los requisitos de estabilidad

La estabilidad es otro aspecto de las propiedades del sistema y difiere de la accesibilidad. La estabilidad se refiere a si una propiedad del sistema se puede cambiar (por ejemplo, cambiarle el nombre o incluso quitarla) en el futuro. Esto es especialmente importante a medida que el SO Android se vuelve modular. Con Treble, las particiones del sistema, del proveedor y del producto se pueden actualizar de forma independiente. Con Mainline, algunas partes del SO se modularizan como módulos actualizables (en APEX o APK).

Si una propiedad del sistema se usa en varias partes actualizables del software, por ejemplo, en las particiones del sistema y del proveedor, debe ser estable. Sin embargo, si se usa solo dentro de un módulo de Mainline específico, por ejemplo, puedes cambiar su nombre, tipo o contextos de propiedad, e incluso quitarlo.

Haz las siguientes preguntas para determinar la estabilidad de una propiedad del sistema:

  • ¿Los socios deben configurar esta propiedad del sistema (o configurarla de manera diferente según el dispositivo)? Si la respuesta es sí, debe ser estable.
  • ¿Se pretende escribir o leer esta propiedad del sistema definida por el AOSP desde el código (no el proceso) que existe en particiones que no son del sistema, como vendor.img o product.img? Si la respuesta es sí, debe ser estable.
  • ¿Se accede a esta propiedad del sistema en todos los módulos de Mainline o en un módulo de Mainline y la parte no actualizable de la plataforma? Si la respuesta es sí, debe ser estable.

Para las propiedades del sistema estables, define formalmente cada una como una API y usa la API para acceder a la propiedad del sistema, como se explica en el paso 6.

Paso 5: Establece propiedades en el tiempo de compilación

Establece propiedades en el momento de la compilación con variables de makefile. Técnicamente, los valores están integrados en {partition}/build.prop. Luego, init lee {partition}/build.prop para establecer las propiedades. Existen dos conjuntos de variables de este tipo: PRODUCT_{PARTITION}_PROPERTIES y TARGET_{PARTITION}_PROP.

PRODUCT_{PARTITION}_PROPERTIES contiene una lista de valores de propiedad. La sintaxis es {prop}={value} o {prop}?={value}.

{prop}={value} es una asignación normal que garantiza que {prop} se establezca en {value}. Solo es posible una asignación de este tipo por propiedad.

{prop}?={value} es una asignación opcional; {prop} se establece en {value} solo si no hay asignaciones de {prop}={value}. Si existen varias asignaciones opcionales, la primera es la que se aplica.

# 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 contiene una lista de archivos que se emite directamente a {partition}/build.prop. Cada archivo contiene una lista de pares {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

Para obtener más detalles, consulta build/make/core/sysprop.mk.

Paso 6: Accede a las propiedades en el tiempo de ejecución

Las propiedades se pueden leer y escribir durante el tiempo de ejecución.

Secuencias de comandos de init

Los archivos de secuencia de comandos de inicialización (por lo general, archivos *.rc) pueden leer una propiedad con ${prop} o ${prop:-default}, pueden establecer una acción que se ejecute cada vez que una propiedad tome un valor específico y pueden escribir las propiedades con el comando 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}

Comandos de shell getprop y setprop

Puedes usar los comandos de shell getprop o setprop, respectivamente, para leer o escribir las propiedades. Para obtener más detalles, invoca getprop --help o setprop --help.

$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0

Sysprop como API para C++, Java y Rust

Con sysprop como API, puedes definir propiedades del sistema y usar la API generada automáticamente, que es concreta y con tipos. Si configuras scope con Public, las APIs generadas también estarán disponibles para los módulos en todos los límites y se garantizará la estabilidad de la API. Aquí tienes un ejemplo de un archivo .sysprop, un módulo Android.bp y código en C++, Java y Rust que los usa.

# 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);

Para obtener más información, consulta Implementa propiedades del sistema como APIs.

Funciones y métodos de propiedades de bajo nivel en C/C++, Java y Rust

Cuando sea posible, usa Sysprop como API, aunque tengas a tu disposición funciones de C/C++ o Rust de bajo nivel, o bien métodos de Java de bajo nivel.

libc, libbase y libcutils ofrecen funciones de propiedades del sistema en C++. libc tiene la API subyacente, mientras que las funciones libbase y libcutils son wrappers. Si es posible, usa las funciones de sysprop libbase, ya que son las más convenientes y los archivos binarios del host pueden usar las funciones de libbase. Para obtener más detalles, consulta sys/system_properties.h (libc), android-base/properties.h (libbase) y cutils/properties.h (libcutils).

La clase android.os.SystemProperties ofrece métodos de propiedad del sistema Java.

El módulo rustutils::system_properties ofrece funciones y tipos de propiedades del sistema de Rust.

Apéndice: Agrega propiedades específicas del proveedor

Los socios (incluidos los Googlers que trabajan en el contexto del desarrollo de Pixel) desean definir propiedades del sistema específicas del hardware (o del dispositivo). Las propiedades específicas del proveedor son propiedades que pertenecen al socio y son exclusivas de su propio hardware o dispositivo, no de la plataforma. Como dependen del hardware o del dispositivo, están diseñadas para usarse dentro de las particiones /vendor o /odm.

Desde Project Treble, las propiedades de la plataforma y del proveedor se dividieron por completo para evitar conflictos. A continuación, se describe cómo definir las propiedades del proveedor y se indica qué propiedades del proveedor siempre se deben usar.

Espacio de nombres en los nombres de la propiedad y el contexto

Todas las propiedades del proveedor deben comenzar con uno de los siguientes prefijos para evitar conflictos entre ellas y las propiedades de otras particiones.

  • 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.

Ten en cuenta que ro.hardware. se permite como prefijo, pero solo por compatibilidad. No la uses para propiedades normales.

En los siguientes ejemplos, se usa uno de los prefijos que se mencionaron anteriormente:

  • vendor.display.primary_red
  • persist.vendor.faceauth.use_disk_cache
  • ro.odm.hardware.platform

Todos los contextos de propiedades del proveedor deben comenzar con vendor_. Esto también es por compatibilidad. A continuación, se incluyen algunos ejemplos:

  • vendor_radio_prop.
  • vendor_faceauth_prop.
  • vendor_usb_prop.

Es responsabilidad del proveedor nombrar y mantener las propiedades, por lo que debes seguir el formato sugerido en el paso 2, además de los requisitos de los espacios de nombres del proveedor.

Reglas de SEPolicy y property_contexts específicos del proveedor

Las propiedades del proveedor se pueden definir con la macro vendor_internal_prop. Coloca las reglas específicas del proveedor que definas en el directorio BOARD_VENDOR_SEPOLICY_DIRS. Por ejemplo, supongamos que estás definiendo una propiedad de faceauth del proveedor en Coral.

En el archivo BoardConfig.mk (o en cualquier inclusión de BoardConfig.mk), coloca lo siguiente:

BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy

En el archivo device/google/coral-sepolicy/private/property.te, ingresa lo siguiente:

vendor_internal_prop(vendor_faceauth_prop)

En el archivo device/google/coral-sepolicy/private/property_contexts, ingresa lo siguiente:

vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool

Limitaciones de las propiedades del proveedor

Dado que las particiones del sistema y del producto no pueden depender del proveedor, nunca permitas que se acceda a las propiedades del proveedor desde las particiones system, system-ext o product.

Apéndice: Cambia el nombre de las propiedades existentes

Cuando debas desaprobar una propiedad y cambiar a una nueva, usa Sysprop como APIs para cambiar el nombre de tus propiedades existentes. Esto mantiene la retrocompatibilidad, ya que se especifican tanto el nombre heredado como el nombre de la propiedad nueva. Específicamente, puedes establecer el nombre heredado con el campo legacy_prop_name en el archivo .sysprop. La API generada intenta leer prop_name y usa legacy_prop_name si prop_name no existe.

Por ejemplo, en los siguientes pasos, se cambia el nombre de awesome_feature_foo_enabled a foo.awesome_feature.enabled.

En el archivo 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"
}

En código 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);

Ten en cuenta las siguientes advertencias:

  • Primero, no puedes cambiar el tipo de la propiedad del sistema. Por ejemplo, no puedes convertir una propiedad int en una propiedad string. Solo puedes cambiar el nombre.

  • En segundo lugar, solo la API de lectura recurre al nombre heredado. La API de escritura no recurre a una alternativa. Si la propiedad del sistema es de escritura, no puedes cambiarle el nombre.