Cette page fournit une méthode canonique pour ajouter ou définir des propriétés système dans Android, avec des consignes pour refactoriser les propriétés système existantes. Veillez à suivre les consignes lorsque vous effectuez une refactorisation, sauf si vous rencontrez un problème de compatibilité important qui l'exige.
Étape 1 : Définir la propriété système
Lorsque vous ajoutez une propriété système, choisissez son nom et associez-la avec un contexte de propriété SELinux. S'il n'y a pas de contexte existant approprié, créez-en une. Le nom est utilisé lors de l'accès à la propriété. la propriété le contexte est utilisé pour contrôler l'accessibilité en termes de SELinux. Les noms peuvent être n'importe quelle chaîne, mais AOSP vous recommande de suivre un format structuré pour les rendre clairs.
Nom de la propriété
Utilisez ce format avec la casse snake_case:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Utilisez "" (omis), ro
(pour les propriétés définies une seule fois) ou persist
(pour les propriétés qui persistent après les redémarrages) pour l'élément prefix
.
Mises en garde
N'utilisez ro
que lorsque vous êtes certain que vous n'avez pas besoin que prefix
soit accessible en écriture à l'avenir. ** Ne spécifiez pas le préfixe ro
.** Utilisez plutôt sepolicy pour
rendre prefix
en lecture seule (en d'autres termes, n'est accessible en écriture que par init
).
N'utilisez persist
que lorsque vous êtes certain que la valeur doit être conservée entre les redémarrages et que l'utilisation des propriétés système est votre seule option.
Google examine strictement les propriétés système qui comportent des propriétés ro
ou persist
.
Le terme group
permet d'agréger des propriétés associées. Il est destiné à
être un nom de sous-système semblable à audio
ou telephony
. N'utilisez pas de termes ambigus ou surchargés tels que sys
, system
, dev
, default
ou config
.
Il est courant d'utiliser le nom du type de domaine
qui dispose d’un accès exclusif en lecture
ou en écriture aux propriétés du système. Par exemple, pour les propriétés système auxquelles le processus vold
a un accès en écriture, il est courant d'utiliser vold
(nom du type de domaine pour le processus) comme nom de groupe.
Si nécessaire, ajoutez subgroup
pour classer davantage les propriétés, mais évitez
des termes ambigus ou trop lourds
pour décrire cet élément. (Vous pouvez aussi
plus d'un subgroup
.)
De nombreux noms de groupes ont déjà été définis. Vérifiez le fichier system/sepolicy/private/property_contexts
et utilisez les noms de groupe existants dans la mesure du possible, au lieu d'en créer. Le tableau suivant présente des exemples de noms de groupes fréquemment utilisés.
Domaine | Groupe (et sous-groupe) |
---|---|
lié au Bluetooth | bluetooth |
sysprops de la ligne de commande du kernel | boot |
sysprops qui identifient un build | build
|
concernant la téléphonie | telephony |
audio | audio |
liés aux éléments graphiques | graphics |
liés à vold | vold |
L'exemple suivant définit l'utilisation de name
et type
dans l'exemple d'expression régulière précédent.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
identifie une propriété système dans un groupe.type
est un élément facultatif qui clarifie le type ou l'intention de la propriété système. Par exemple, au lieu de nommer une propriété systèmeaudio.awesome_feature_enabled
ou simplementaudio.awesome_feature
, renommez-laaudio.awesome_feature.enabled
pour refléter le type et l'intent de la propriété système.
Il n'existe aucune règle spécifique concernant le type. Voici quelques recommandations d'utilisation :
enabled
: à utiliser si le type est une propriété système booléenne utilisée pour activer ou désactiver une fonctionnalité.config
: à utiliser si l'objectif est de préciser que la propriété système ne représente pas un état dynamique du système, mais une valeur préconfigurée (par exemple, un élément en lecture seule).List
: à utiliser s'il s'agit d'une propriété système dont la valeur est une liste.Timeoutmillis
: à utiliser s'il s'agit d'une propriété système d'une valeur de délai avant expiration en unités. de ms.
Exemples :
persist.radio.multisim.config
drm.service.enabled
Contexte de l'établissement
Le nouveau schéma de contexte des propriétés SELinux permet une granularité plus fine et plus des noms descriptifs. Comme pour les noms de propriétés, AOSP recommande le format suivant :
{group}[_{subgroup}]*_prop
Les termes sont définis comme suit :
group
et subgroup
ont la même signification que celle définie pour les
exemple d'expression régulière. Par exemple, vold_config_prop
signifie que les propriétés sont des configurations d'un fournisseur et doivent être définies par vendor_init
, tandis que vold_status_prop
ou simplement vold_prop
signifie que les propriétés doivent exposer l'état actuel de vold
.
Lorsque vous attribuez un nom au contexte d'une propriété, choisissez des noms qui reflètent l'utilisation générale les propriétés. En particulier, évitez les types de termes suivants :
- Termes qui semblent trop généraux et ambigus, tels que
sys
,system
etdefault
- Les termes qui encodent directement l'accessibilité, tels que
exported
,apponly
,ro
,public
etprivate
.
Préférez des noms tels que vold_config_prop
à exported_vold_prop
ou vold_vendor_writable_prop
.
Type
Un type de propriété peut être l'un des suivants, comme indiqué dans le tableau.
Type | Définition |
---|---|
Booléen | true ou 1 pour "true", false ou 0 pour "false". |
Nombre entier | entier signé de 64 bits |
Entier sans signature | Entier de 64 bits non signé |
Double | à virgule flottante à double précision |
Chaîne | toute chaîne UTF-8 valide |
enum | Les valeurs peuvent être n'importe quelle chaîne UTF-8 valide sans espaces. |
Liste des éléments ci-dessus | Une virgule (, ) est utilisée comme délimiteur.La liste d'entiers [1, 2, 3] est stockée sous la forme 1,2,3 . |
En interne, toutes les propriétés sont stockées sous la forme de chaînes. Vous pouvez appliquer le type en le spécifiant comme fichier property_contexts
. Pour en savoir plus, consultez property_contexts
à l'étape 3.
Étape 2: Déterminez les niveaux d'accessibilité requis
Quatre macros d'assistance définissent une propriété.
Type d'accessibilité | Signification |
---|---|
system_internal_prop |
Propriétés utilisées uniquement dans /system |
system_restricted_prop |
Propriétés lues en dehors de /system , mais non écrites |
system_vendor_config_prop |
Propriétés lues en dehors de /system et écrites uniquement par vendor_init |
system_public_prop |
Propriétés lues et écrites en dehors de /system |
Limitez l'accès aux propriétés système aussi précisément que possible. Auparavant, l’accès étendu a entraîné le dysfonctionnement des applications et des failles de sécurité. Envisagez d'utiliser les questions suivantes lors de la définition de la portée:
- Cette propriété système doit-elle être conservée ? (si oui, pourquoi ?)
- Quel processus devrait disposer d'un accès en lecture à cette propriété ?
- Quel processus doit disposer d'un accès en écriture à cette propriété ?
Utilisez les questions précédentes et l'arbre de décision suivant comme outils pour en déterminant la portée d'accès appropriée.
Figure 1 : Arbre de décision permettant de déterminer l'étendue de l'accès aux propriétés système
Étape 3 : Ajouter à system/sepolicy
Lors de l'accès à sysprop, SELinux contrôle l'accessibilité des processus. Après
vous déterminez le niveau d'accessibilité requis, vous définissez des contextes de propriété
sous system/sepolicy
, ainsi que des règles allow et neverallow supplémentaires
sur ce que les processus sont (et ne
sont pas) autorisés à lire ou à écrire.
Commencez par définir le contexte de la propriété dans le fichier system/sepolicy/public/property.te
. Si la propriété est interne au système, définissez-la dans
system/sepolicy/private/property.te
. Utilisez l'une des
les macros system_[accessibility]_prop([context])
qui fournissent
l'accessibilité requise pour votre propriété système. Voici un exemple pour le fichier system/sepolicy/public/property.te
:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Exemple à ajouter dans le fichier system/sepolicy/private/property.te
:
system_internal_prop(audio_baz_prop)
Ensuite, accordez un accès en lecture et (ou) en écriture au contexte de la propriété. Utiliser set_prop
et get_prop
pour accorder l'accès, dans les
system/sepolicy/public/{domain}.te
ou system/sepolicy/private/{domain}.te
. Utilisez private
autant que possible. public
ne convient que si
set_prop
ou get_prop
concerne tous les domaines extérieurs au domaine principal.
Exemple dans le fichier system/sepolicy/private/audio.te
:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Exemple dans le fichier system/sepolicy/public/domain.te
:
get_prop(domain, audio_bar_prop)
Troisièmement, ajoutez des règles de
refus pour réduire davantage l’accessibilité
limité par la macro. Par exemple, supposons que vous ayez utilisé system_restricted_prop
, car vos propriétés système doivent être lues par les processus du fournisseur. Si l'accès en lecture n'est pas requis
par tous les processus du fournisseur et qu'il est
n'est requise que par un ensemble spécifique de processus (vendor_init
, par exemple),
les processus du fournisseur qui n'ont
pas besoin de l'accès en lecture.
Pour limiter l'accès en écriture et en lecture, utilisez la syntaxe suivante :
Pour limiter l'accès en écriture :
neverallow [domain] [context]:property_service set;
Pour restreindre l'accès en lecture:
neverallow [domain] [context]:file no_rw_file_perms;
Placez des règles neverallow dans le fichier system/sepolicy/private/{domain}.te
si la règle neverallow est liée à un domaine spécifique. Pour des règles de blocage plus larges, utilisez
des domaines généraux tels que les suivants, le cas échéant:
system/sepolicy/private/property.te
system/sepolicy/private/coredomain.te
system/sepolicy/private/domain.te
Dans le fichier system/sepolicy/private/audio.te
, placez le code suivant:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
Dans le fichier system/sepolicy/private/property.te
, placez les éléments suivants :
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
Notez que {domain -coredomain}
capture tous les processus du fournisseur. {domain -coredomain -vendor_init}
signifie donc "tous les processus du fournisseur sauf vendor_init
".
Enfin, associez une propriété système au contexte de la propriété. Cela garantit
que l'accès accordé et les règles de blocage qui s'appliquent
les contextes de propriété sont appliqués aux propriétés réelles. Pour ce faire, ajoutez une entrée à
le fichier property_contexts
, qui décrit le mappage entre les ressources
et les contextes de propriété. Dans ce fichier, vous pouvez spécifier un seul
ou un préfixe pour les propriétés à mapper dans un contexte.
Voici la syntaxe pour mapper une seule propriété :
[property_name] u:object_r:[context_name]:s0 exact [type]
Voici la syntaxe pour mapper un préfixe:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
Vous pouvez éventuellement spécifier le type de propriété, qui peut être l'une des suivantes:
bool
int
uint
double
enum [list of possible values...]
string
(utilisezstring
pour les propriétés de liste)
Assurez-vous que chaque entrée a son type désigné dans la mesure du possible, car type
est appliqué lors de la définition de property
. L'exemple suivant montre comment écrire
mappage:
# 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
En cas de conflit entre une entrée exacte et une entrée de préfixe, l'entrée exacte prend
la priorité. Pour plus d'exemples, consultez system/sepolicy/private/property_contexts
.
Étape 4 : Déterminez les exigences de stabilité
La stabilité est un autre aspect des propriétés système, et elle diffère de l'accessibilité. La stabilité consiste à déterminer si une propriété système peut être modifié (par exemple, renommé ou même supprimé) par la suite. C'est ce qui est particulièrement important à mesure que l'OS Android devient modulaire. Avec Treble, le système les partitions des fournisseurs et des produits peuvent être mises à jour indépendamment les unes des autres. Avec la version principale, certaines parties de l'OS sont modélisées en tant que modules pouvant être mis à jour (dans des APEX ou des APK).
Si une propriété système est destinée à être utilisée dans des éléments logiciels pouvant être mis à jour, par exemple dans les partitions système et du fournisseur, elle doit être stable. Toutefois, s'il est utilisé dans un module Mainline spécifique, par exemple, vous pouvez modifier son nom, de type ou de propriété, et même les supprimer.
Posez les questions suivantes pour déterminer la stabilité d'une propriété système:
- Cette propriété système est-elle destinée à être configurée par les partenaires (ou configurée différemment par appareil) ? Si c'est le cas, elle doit être stable.
- Cette propriété système définie par AOSP est-elle destinée à être lue ou écrite dans
du code (non traité) existant dans des partitions non-système telles que
vendor.img
ouproduct.img
? Si c'est le cas, elle doit être stable. - L'accès à cette propriété système s'effectue-t-il dans les modules Mainline ou sur Mainline et la partie non modifiable de la plate-forme ? Si c'est le cas, elle doit être stable.
Pour les propriétés système stables, définissez-les formellement en tant qu'API et utilisez l'API pour accéder à la propriété système, comme expliqué à l'étape 6.
Étape 5: Définir les propriétés au moment de la compilation
Définissez des propriétés au moment de la compilation à l'aide de variables de fichier make. Techniquement, les valeurs
sont intégrés à {partition}/build.prop
. init
lit ensuite {partition}/build.prop
pour définir les propriétés. Il existe deux ensembles de telles variables : PRODUCT_{PARTITION}_PROPERTIES
et TARGET_{PARTITION}_PROP
.
PRODUCT_{PARTITION}_PROPERTIES
contient une liste de valeurs de propriété. La syntaxe
est {prop}={value}
ou {prop}?={value}
.
{prop}={value}
est une affectation normale qui garantit que {prop}
est défini sur {value}
. Une seule telle affectation est possible pour une seule propriété.
{prop}?={value}
est un devoir facultatif. {prop}
ne se définit sur {value}
que s'il n'y a pas de devoirs {prop}={value}
. Si plusieurs devoirs facultatifs existent, le premier l'emporte.
# 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
contient une liste de fichiers, qui est émise directement dans {partition}/build.prop
. Chaque fichier contient une liste de paires {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
Pour en savoir plus, consultez
build/make/core/sysprop.mk
Étape 6: Accédez aux propriétés au moment de l'exécution
Les propriétés peuvent être lues et écrites au moment de l'exécution.
Scripts d'initialisation
Les fichiers de script d'initialisation (généralement des fichiers *.rc) peuvent lire une propriété par ${prop}
ou
${prop:-default}
, peut définir une action qui s'exécute chaque fois qu'une propriété devient
valeur spécifique, et vous pouvez écrire les propriétés à l'aide de la commande 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}
Commandes shell getprop et setprop
Vous pouvez utiliser les commandes shell getprop
ou setprop
, respectivement, pour lire ou
écrire les propriétés. Pour en savoir plus, appelez getprop --help
ou
setprop --help
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop comme API pour C++/Java/Rust
Avec sysprop comme API, vous pouvez définir des propriétés système et utiliser une API générée automatiquement
qui sont en béton et typés. Définir scope
avec Public
permet également de rendre les API générées disponibles pour les modules au-delà des limites et de garantir leur stabilité. Voici un exemple de fichier .sysprop
, d'un module Android.bp
et de code C++, Java et Rust qui les utilisent.
# 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);
Pour en savoir plus, consultez Implémenter des propriétés système en tant qu'API.
Fonctions et méthodes de propriétés de bas niveau C/C++, Java et Rust
Si possible, utilisez Sysprop comme API même si des fonctions C/C++ ou Rust de bas niveau ou de bas niveau sont à votre disposition.
libc
, libbase
et libcutils
offrent des fonctions de propriétés système C++. libc
possède l'API sous-jacente, tandis que les fonctions libbase
et libcutils
sont des wrappers. Si possible, utilisez les fonctions sysprop libbase
. Elles sont les plus pratiques, et les binaires hôtes peuvent utiliser les fonctions libbase
. Pour plus
détails, voir sys/system_properties.h
(libc
), android-base/properties.h
(libbase
) et cutils/properties.h
(libcutils
).
La classe android.os.SystemProperties
propose des méthodes de propriété système Java.
Le module rustutils::system_properties
propose des fonctions et des types de propriétés système Rust.
Annexe : Ajouter des propriétés spécifiques au fournisseur
Les partenaires (y compris les Googleurs travaillant dans le contexte du développement des Pixel) veulent
pour définir des propriétés système spécifiques au matériel (ou à l'appareil).
Les propriétés spécifiques aux fournisseurs sont des propriétés détenues par un partenaire
propre matériel ou appareil, et non à la plateforme. Comme il s'agit de matériel
ou de l'appareil
dépendantes, elles sont destinées à être utilisées dans les partitions /vendor
ou /odm
.
Depuis le projet Treble, les propriétés de la plate-forme et les propriétés du fournisseur ont été complètement dissociées pour éviter les conflits. Vous trouverez ci-dessous la procédure à suivre pour définir les propriétés des fournisseurs et indiquer quelles propriétés de fournisseur doivent toujours être utilisées.
Espace de noms sur les noms de propriété et de contexte
Toutes les propriétés de fournisseur doivent commencer par l'un des préfixes suivants pour empêcher une collision entre elles et les propriétés des autres 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.
Notez que ro.hardware.
est autorisé en tant que préfixe, mais uniquement pour des raisons de compatibilité.
Ne l'utilisez pas pour les propriétés normales.
Les exemples suivants utilisent tous l'un des préfixes répertoriés ci-dessus:
vendor.display.primary_red
persist.vendor.faceauth.use_disk_cache
ro.odm.hardware.platform
Tous les contextes de propriété du fournisseur doivent commencer par vendor_
. C'est également pour
et la compatibilité avec d'autres appareils. Voici quelques exemples :
vendor_radio_prop
.vendor_faceauth_prop
vendor_usb_prop
.
Il incombe au fournisseur de nommer et de gérer les propriétés. Suivez donc le format suggéré à l'étape 2, en plus des exigences concernant les espaces de noms du fournisseur.
Règles SEPolicy spécifiques au fournisseur et Property_contexts
Les propriétés de fournisseur peuvent être définies par la macro vendor_internal_prop
. Placez le
les règles spécifiques au fournisseur que vous définissez dans le répertoire BOARD_VENDOR_SEPOLICY_DIRS
.
Par exemple, supposons que vous définissiez une propriété faceauth du fournisseur dans "corail".
Dans le fichier BoardConfig.mk
(ou dans toute inclusion BoardConfig.mk
), ajoutez les éléments suivants :
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
Dans le fichier device/google/coral-sepolicy/private/property.te
, placez
suivantes:
vendor_internal_prop(vendor_faceauth_prop)
Dans le fichier device/google/coral-sepolicy/private/property_contexts
, ajoutez les éléments suivants :
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
Limites des propriétés de fournisseur
Comme les partitions du système et des produits
ne peuvent pas dépendre du fournisseur,
autoriser l'accès aux propriétés du fournisseur depuis system
, system-ext
ou
product
partitions.
Annexe : Renommer des propriétés existantes
Lorsque vous devez abandonner une propriété et en déplacer une nouvelle, utilisez Sysprop en tant qu'API.
pour renommer vos propriétés existantes. Cela permet de maintenir la rétrocompatibilité en spécifiant à la fois l'ancien nom et le nouveau nom de la propriété. Plus précisément, vous pouvez
Définissez l'ancien nom à l'aide du champ legacy_prop_name
du fichier .sysprop
. L'API générée tente de lire prop_name
et utilise legacy_prop_name
si prop_name
n'existe pas.
Par exemple, les étapes suivantes renomment awesome_feature_foo_enabled
en foo.awesome_feature.enabled
.
Dans le fichier 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"
}
Dans le code 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);
Veuillez noter les points suivants :
Tout d'abord, vous ne pouvez pas modifier le type de sysprop. Par exemple, vous ne pouvez pas une prop
int
en une propstring
. Vous ne pouvez que modifier le nom.Deuxièmement, seule l'API de lecture utilise le nom ancien. L'API d'écriture n'effectue pas à l'arrêt. Si la propriété système est accessible en écriture, vous ne pouvez pas la renommer.