En esta página, se proporciona un método canónico para agregar o definir propiedades del sistema en Android, con lineamientos para refactorizar las propiedades del sistema existentes. Asegúrate de usar los lineamientos cuando reestructures, a menos que tengas un problema de compatibilidad fuerte que dicte lo contrario.
Paso 1: Define la propiedad del sistema
Cuando agregues una propiedad del sistema, decide un nombre para la propiedad y asóciala con un contexto de propiedad SELinux. Si no existe un contexto 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 AOSP recomienda que sigas un formato estructurado para que sean claros.
Nombre de la propiedad
Usa este formato con mayúsculas en snake_case:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Usa "" (se omite), ro
(para propiedades configuradas solo una vez) o persist
(para propiedades que persisten en los reinicios) para el elemento prefix
.
Advertencias
Usa ro
solo cuando estés seguro de que no necesitas que prefix
permita la escritura en el futuro. ** No especifiques el prefijo ro
.** En su lugar, usa sepolicy para hacer que prefix
sea de solo lectura (en otras palabras, que solo init
pueda 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. Su objetivo es ser un nombre de subsistema similar en el uso a audio
o telephony
. No uses términos ambiguos ni 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 de lectura o escritura exclusivo 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 mejor las propiedades, pero evita usar 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 los grupos existentes siempre que sea posible, en lugar de crear otros nuevos. En la siguiente tabla, se proporcionan ejemplos de nombres de grupos de uso frecuente.
Dominio | Grupo (y subgrupo) |
---|---|
Relacionado con Bluetooth | bluetooth |
sysprops de cmdline del kernel | boot |
sysprops que identifican una compilación | build
|
relacionado con la telefonía | telephony |
relacionado con el audio | audio |
relacionado con los gráficos | graphics |
Relacionado con vold | 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 a una sysprop comoaudio.awesome_feature_enabled
o soloaudio.awesome_feature
, cámbiale el nombre aaudio.awesome_feature.enabled
para reflejar el tipo de propiedad del sistema y el intent.
No hay una regla específica sobre cuál debe ser el tipo. Estas son recomendaciones de uso:
enabled
: Úsalo si el tipo es una propiedad del sistema booleana que se usa para activar o desactivar una función.config
: Úsalo si el objetivo 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
: Úsalo si es una propiedad del sistema cuyo valor es una lista.Timeoutmillis
: Úsalo 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 descritivos. Al igual que lo que se usa para los nombres de propiedad, 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 muestra anterior. Por ejemplo, vold_config_prop
indica propiedades que son configuraciones de un proveedor y que vendor_init
debe establecer, mientras que vold_status_prop
o solo vold_prop
indican propiedades que deben exponer el estado actual de vold
.
Cuando nombres 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
ydefault
- Términos que codifican directamente la accesibilidad, como
exported
,apponly
,ro
,public
yprivate
Se 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 firma |
Número entero sin signo | Número entero de 64 bits sin firma |
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 . |
De forma interna, todas las propiedades se almacenan como cadenas. Para aplicar el tipo, puedes especificarlo 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
Hay cuatro macros de ayuda 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 que 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 provocó fallas en las apps y vulnerabilidades de seguridad. Ten en cuenta las siguientes preguntas cuando definas el alcance:
- ¿Es necesario que esta propiedad del sistema se conserve? (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 permiso de acceso adecuado.
Figura 1: Árbol de decisión para determinar el alcance del acceso a las propiedades del sistema
Paso 3: Agregar al sistema o a la política
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 system_[accessibility]_prop([context])
que proporciona la accesibilidad requerida para tu 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 al 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
es adecuado solo si la macro set_prop
o get_prop
afecta a cualquier dominio fuera del dominio principal.
Por ejemplo, en el archivo system/sepolicy/private/audio.te
:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Por ejemplo, en el archivo system/sepolicy/public/domain.te
:
get_prop(domain, audio_bar_prop)
Tercero, 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
, ya que los procesos del proveedor deben leer las propiedades de tu sistema. Si el acceso de lectura no es necesario para todos los procesos del proveedor y solo lo requiere un conjunto determinado de procesos (como vendor_init
), prohíbe los procesos del proveedor que no lo necesiten.
Usa la siguiente sintaxis para restringir el acceso de escritura y lectura:
Para restringir el acceso de escritura, sigue estos pasos:
neverallow [domain] [context]:property_service set;
Para restringir el acceso de lectura, sigue estos pasos:
neverallow [domain] [context]:file no_rw_file_perms;
Coloca reglas de neverallow en el archivo system/sepolicy/private/{domain}.te
si la regla de neverallow está vinculada a un dominio específico. Para reglas de neverallow más amplias, usa dominios generales como los siguientes cuando corresponda:
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 de los proveedores. 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
, un archivo 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 una de las siguientes:
bool
int
uint
double
enum [list of possible values...]
string
(usastring
para las propiedades de lista).
Asegúrate de que cada entrada tenga su tipo designado siempre que sea posible, ya que se aplica type
cuando se configura 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 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 se puede cambiar una propiedad del sistema (por ejemplo, cambiar su nombre o incluso quitarla) en el futuro. Esto es muy importante a medida que el SO Android se vuelve modular. Con Treble, las particiones del sistema, el proveedor y el 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 software actualizable, por ejemplo, en particiones del sistema y del proveedor, debe ser estable. Sin embargo, si solo se usa, por ejemplo, dentro de un módulo de línea principal específico, puedes cambiar su nombre, tipo o contextos de propiedad, o 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 en cada dispositivo)? Si es así, debe ser estable.
- ¿Esta propiedad del sistema definida por AOSP está destinada a escribir o leer código (no proceso) que existe en particiones que no son del sistema, como
vendor.img
oproduct.img
? Si es así, debe ser estable. - ¿Se accede a esta propiedad del sistema en los módulos de Mainline o en un módulo de Mainline y la parte no actualizable de la plataforma? Si es así, debe ser estable.
Para las propiedades del sistema estables, define cada una de forma formal como una API y úsala para acceder a la propiedad del sistema, como se explica en el Paso 6.
Paso 5: Establece propiedades durante el tiempo de compilación
Configura propiedades durante el tiempo de compilación con variables makefile. Técnicamente, los valores están integrados en {partition}/build.prop
. Luego, init
lee {partition}/build.prop
para configurar las propiedades. Hay dos conjuntos de esas variables: PRODUCT_{PARTITION}_PROPERTIES
y TARGET_{PARTITION}_PROP
.
PRODUCT_{PARTITION}_PROPERTIES
contiene una lista de valores de propiedades. La sintaxis es {prop}={value}
o {prop}?={value}
.
{prop}={value}
es una asignación normal que garantiza que {prop}
se establezca en {value}
. Solo se puede realizar una asignación de este tipo por propiedad.
{prop}?={value}
es una tarea opcional. {prop}
se establece en {value}
solo si no hay tareas {prop}={value}
. Si existen varias tareas opcionales, se aplica la primera.
# 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 información, consulta build/make/core/sysprop.mk
.
Paso 6: Accede a las propiedades durante el tiempo de ejecución
Las propiedades se pueden leer y escribir durante el tiempo de ejecución.
Secuencias de comandos Init
Los archivos de secuencia de comandos de inicio (por lo general, archivos *.rc) pueden leer una propiedad por ${prop}
o ${prop:-default}
, pueden establecer una acción que se ejecuta cada vez que una propiedad se convierte en 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 getprop y setprop de shell
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/Rust
Con sysprop como API, puedes definir propiedades del sistema y usar APIs generadas de forma automática, concretas y sobre tipos. Configurar scope
con Public
también hace que las APIs generadas estén disponibles para módulos más allá de los límites y garantiza la estabilidad de las APIs. A continuación, se muestra un ejemplo de un archivo .sysprop
, un módulo Android.bp
y código de C++, Java y Rust con ellos.
# 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 Cómo implementar propiedades del sistema como APIs.
Funciones y métodos de propiedades de bajo nivel de C/C++, Java y Rust
Cuando sea posible, usa Sysprop como API, aunque tengas a tu disposición funciones de C/C++ o Rust, o métodos de Java de bajo nivel.
libc
, libbase
y libcutils
ofrecen funciones de propiedades del sistema 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 objetos binarios del host pueden usar las funciones 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 propiedades del sistema de 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) quieren definir propiedades del sistema específicas del hardware (o del dispositivo).
Las propiedades específicas del proveedor son propiedades de socios que son únicas para su hardware o dispositivo, no para la plataforma. Como dependen del hardware o del dispositivo, están diseñados para usarse dentro de las particiones /vendor
o /odm
.
Desde Project Treble, las propiedades de la plataforma y de los proveedores se dividieron por completo para evitar conflictos. A continuación, se describe cómo definir las propiedades del proveedor y se indica cuáles deben usarse siempre.
Espacio de nombres en los nombres de propiedades y contextos
Todas las propiedades del proveedor deben comenzar con uno de los siguientes prefijos para evitar colisiones 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 motivos de compatibilidad.
No la uses para propiedades normales.
En los siguientes ejemplos, se usa uno de los prefijos mencionados 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 se hace para la compatibilidad. Estos son algunos ejemplos:
vendor_radio_prop
.vendor_faceauth_prop
.vendor_usb_prop
.
Es responsabilidad del proveedor nombrar y mantener 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íficas 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 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
Debido a 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: Cómo cambiar el nombre de las propiedades existentes
Cuando debas dar de baja 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 especifica el nombre heredado y el nombre nuevo de la propiedad. Específicamente, puedes establecer el nombre heredado en el campo legacy_prop_name
del 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:
En primer lugar, no puedes cambiar el tipo de sysprop. Por ejemplo, no puedes convertir un elemento
int
en un elementostring
. Solo puedes cambiar el nombre.En segundo lugar, solo la API de lectura recurre al nombre heredado. La API de escritura no recurre a ella. Si el sysprop es de escritura, no puedes cambiarle el nombre.