Cette page fournit une méthode canonique pour ajouter ou définir des propriétés système dans Android, ainsi que des consignes pour refactoriser les propriétés système existantes. Assurez-vous de suivre les consignes lorsque vous refactorisez, sauf si vous rencontrez un problème de compatibilité majeur qui vous oblige à faire autrement.
Étape 1 : Définissez la propriété système
Lorsque vous ajoutez une propriété système, choisissez un nom pour la propriété et associez-le à un contexte de propriété SELinux. S'il n'existe aucun contexte approprié, créez-en un. Le nom est utilisé pour accéder à la propriété. Le contexte de la propriété est utilisé pour contrôler l'accessibilité en termes de SELinux. Les noms peuvent être n'importe quelle chaîne, mais l'AOSP 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 un redémarrage) pour l'élément prefix
.
Mises en garde
N'utilisez ro
que lorsque vous êtes certain que vous n'aurez plus besoin que prefix
soit accessible en écriture. ** Ne spécifiez pas le préfixe ro
.** Au lieu de cela, appuyez-vous sur sepolicy pour rendre prefix
en lecture seule (en d'autres termes, accessible en écriture uniquement par init
).
N'utilisez persist
que si vous êtes certain que la valeur doit être conservée lors des redémarrages et que l'utilisation des propriétés système est votre seule option.
Google examine rigoureusement les propriétés système qui comportent les propriétés ro
ou persist
.
Le terme group
est utilisé pour agréger les propriétés associées. Il s'agit d'un nom de sous-système dont l'utilisation est 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 d'un processus 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 accès en écriture, il est courant d'utiliser vold
(le nom du type de domaine pour le processus) comme nom de groupe.
Si nécessaire, ajoutez subgroup
pour catégoriser davantage les propriétés, mais évitez les termes ambigus ou surchargés pour décrire cet élément. (Vous pouvez également avoir plusieurs subgroup
.)
De nombreux noms de groupes ont déjà été définis. Consultez le fichier system/sepolicy/private/property_contexts
et utilisez les noms de groupes existants dans la mesure du possible, au lieu d'en créer de nouveaux. Le tableau suivant fournit des exemples de noms de groupes fréquemment utilisés.
Domaine | Groupe (et sous-groupe) |
---|---|
Problèmes liés au Bluetooth | bluetooth |
sysprops de la ligne de commande du noyau | boot |
sysprops qui identifient une compilation | build
|
lié à la téléphonie | telephony |
lié à l'audio | audio |
graphiques | graphics |
vold related | vold |
La section suivante 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 précise 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'intention de la propriété système.
Il n'existe aucune règle spécifique concernant le type. Voici des recommandations d'utilisation :
enabled
: à utiliser si le type est une propriété système booléenne qui sert à activer ou désactiver une fonctionnalité.config
: à utiliser si l'intention 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, une chose 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 pour une valeur de délai avant expiration en millisecondes.
Exemples :
persist.radio.multisim.config
drm.service.enabled
Contexte de la propriété
Le nouveau schéma de contexte de propriété SELinux permet une granularité plus fine et des noms plus descriptifs. Comme pour les noms de propriétés, l'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 l'exemple d'expression régulière précédent. Par exemple, vold_config_prop
désigne les propriétés qui sont des configurations d'un fournisseur et qui doivent être définies par vendor_init
, tandis que vold_status_prop
ou simplement vold_prop
désigne les propriétés qui doivent exposer l'état actuel de vold
.
Lorsque vous nommez un contexte de propriété, choisissez des noms qui reflètent l'utilisation générale des propriétés. Évitez en particulier les types de termes suivants :
- Les termes qui semblent trop généraux et ambigus, tels que
sys
,system
etdefault
. - Termes qui encodent directement l'accessibilité, tels que
exported
,apponly
,ro
,public
etprivate
.
Privilégiez les noms tels que vold_config_prop
plutôt que exported_vold_prop
ou vold_vendor_writable_prop
.
Saisie
Un type de propriété peut être l'un des suivants, comme indiqué dans le tableau.
Saisie | Définition |
---|---|
Booléen | true ou 1 pour "vrai", false ou 0 pour "faux" |
Nombre entier | Entier signé de 64 bits |
Entier non signé | Entier non signé de 64 bits |
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 ci-dessus | Une virgule (, ) est utilisée comme délimiteurLa 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 forme de chaînes. Vous pouvez appliquer le type en le spécifiant en tant que fichier property_contexts
. Pour en savoir plus, consultez property_contexts
dans 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 autant que possible. Par le passé, un accès étendu a entraîné des dysfonctionnements d'applications et des failles de sécurité. Posez-vous les questions suivantes lorsque vous définissez le champ d'application :
- Cette propriété système doit-elle être conservée ? (si oui, pourquoi ?)
- Quel processus doit avoir 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 pour déterminer un champ d'application approprié pour l'accès.
Figure 1 : Arbre de décision permettant de déterminer le champ d'accès aux propriétés du système
Étape 3 : Ajoutez à system/sepolicy
Lors de l'accès à sysprop, SELinux contrôle l'accessibilité des processus. Après avoir déterminé le niveau d'accessibilité requis, définissez les contextes de propriété sous system/sepolicy
, ainsi que les règles allow et neverallow supplémentaires concernant 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 le fichier system/sepolicy/private/property.te
. Utilisez l'une des macros system_[accessibility]_prop([context])
qui fournit l'accessibilité requise pour la propriété système. Voici un exemple de 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é. Utilisez les macros set_prop
et get_prop
pour accorder l'accès, dans le fichier system/sepolicy/public/{domain}.te
ou system/sepolicy/private/{domain}.te
. Utilisez private
chaque fois que possible. public
ne convient que si la macro set_prop
ou get_prop
affecte des domaines en dehors du 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 "neverallow" pour réduire davantage l'accessibilité définie par la macro. Par exemple, supposons que vous avez 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, mais uniquement par un certain ensemble de processus (comme vendor_init
), interdisez les processus du fournisseur qui n'ont pas besoin de l'accès en lecture.
Utilisez la syntaxe suivante pour limiter l'accès en écriture et en lecture :
Pour limiter l'accès en écriture :
neverallow [domain] [context]:property_service set;
Pour limiter l'accès en lecture :
neverallow [domain] [context]:file no_rw_file_perms;
Placez les règles neverallow dans le fichier system/sepolicy/private/{domain}.te
si la règle neverallow est liée à un domaine spécifique. Pour les règles "neverallow" plus générales, utilisez des domaines généraux tels que ceux-ci, 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 les éléments suivants :
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. Ainsi, {domain -coredomain -vendor_init}
signifie "tous les processus du fournisseur sauf
vendor_init
".
Enfin, associez une propriété système au contexte de la propriété. Cela permet de s'assurer que l'accès accordé et les règles "neverallow" appliquées aux contextes de propriété le sont aux propriétés réelles. Pour ce faire, ajoutez une entrée au fichier property_contexts
, qui décrit le mappage entre les propriétés système et les contextes de propriété. Dans ce fichier, vous pouvez spécifier une seule propriété 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'un des suivants :
bool
int
uint
double
enum [list of possible values...]
string
(Utilisezstring
pour les propriétés de liste.)
Assurez-vous que chaque entrée possède son type désigné dans la mesure du possible, car type
est appliqué lorsque property
est défini. L'exemple suivant montre comment écrire un 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 prévaut. Pour obtenir d'autres 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 du système, qui diffère de l'accessibilité. La stabilité concerne la possibilité de modifier (par exemple, renommer ou même supprimer) une propriété système à l'avenir. Cela est particulièrement important à mesure que l'OS Android devient modulaire. Avec Treble, les partitions système, fournisseur et produit peuvent être mises à jour indépendamment les unes des autres. Avec Mainline, certaines parties de l'OS sont modularisées sous forme de modules pouvant être mis à jour (dans les APEX ou les 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 fournisseur), elle doit être stable. Toutefois, s'il n'est utilisé que dans un module Mainline spécifique, par exemple, vous pouvez modifier son nom, son type ou ses contextes de propriété, et même le supprimer.
Posez-vous les questions suivantes pour déterminer la stabilité d'une propriété système :
- Cette propriété système est-elle censée être configurée par les partenaires (ou configurée différemment par appareil) ? Si oui, il doit être stable.
- Cette propriété système définie par AOSP est-elle destinée à être écrite ou lue à partir du code (et non du processus) qui existe dans des partitions non système telles que
vendor.img
ouproduct.img
? Si oui, il doit être stable. - Cette propriété système est-elle accessible dans les modules Mainline ou dans un module Mainline et la partie non modifiable de la plate-forme ? Si oui, il doit être stable.
Pour les propriétés système stables, définissez formellement chacune d'elles comme une API et utilisez l'API pour accéder à la propriété système, comme expliqué à l'étape 6.
Étape 5 : Définissez les propriétés au moment de la compilation
Définissez les propriétés au moment de la compilation à l'aide de variables makefile. Techniquement, les valeurs sont intégrées à {partition}/build.prop
. Ensuite, init
lit {partition}/build.prop
pour définir les propriétés. Il existe deux ensembles de variables de ce type : 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 attribution normale qui garantit que {prop}
est défini sur {value}
. Il n'est possible de n'avoir qu'une seule attribution de ce type par propriété.
{prop}?={value}
est un devoir facultatif. {prop}
est défini sur {value}
uniquement s'il n'y a pas de devoirs {prop}={value}
. S'il existe plusieurs affectations facultatives, la première est retenue.
# 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 directement émise 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éder aux propriétés lors 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}
, définir une action qui s'exécute chaque fois qu'une propriété prend une valeur spécifique et é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 en tant qu'API pour C++/Java/Rust
Avec sysprop en tant qu'API, vous pouvez définir des propriétés système et utiliser des API concrètes et typées générées automatiquement. Définir scope
sur Public
permet également de rendre les API générées disponibles pour les modules au-delà des limites et d'assurer la stabilité des API. Voici un exemple de fichier .sysprop
, de module Android.bp
et de code C++, Java et Rust les utilisant.
# 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 les propriétés système en tant qu'API.
Fonctions et méthodes de propriété de bas niveau en C/C++, Java et Rust
Dans la mesure du possible, utilisez Sysprop comme API, même si des fonctions C/C++ ou Rust de bas niveau, ou des méthodes Java de bas niveau sont disponibles.
libc
, libbase
et libcutils
proposent des fonctions de propriété système C++. libc
contient 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 en savoir plus, consultez 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 sur le développement Pixel) souhaitent définir des propriétés système spécifiques au matériel (ou à l'appareil).
Les propriétés spécifiques au fournisseur sont des propriétés appartenant à un partenaire et propres à son matériel ou appareil, et non à la plate-forme. Comme elles dépendent du matériel ou de l'appareil, elles sont destinées à être utilisées dans les partitions /vendor
ou /odm
.
Depuis Project Treble, les propriétés de la plate-forme et celles du fournisseur ont été complètement séparées pour éviter les conflits. La section suivante explique comment définir les propriétés du fournisseur et indique celles qui doivent toujours être utilisées.
Espace de noms sur les noms de propriétés et de contextes
Toutes les propriétés du fournisseur doivent commencer par l'un des préfixes suivants pour éviter toute 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é comme préfixe, mais uniquement pour la compatibilité.
Ne l'utilisez pas pour les propriétés normales.
Les exemples suivants utilisent tous l'un des préfixes listé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_
. Cela permet également d'assurer la compatibilité. 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 des fournisseurs.
Règles SEPolicy et property_contexts spécifiques aux fournisseurs
Les propriétés du fournisseur peuvent être définies par la macro vendor_internal_prop
. Placez 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 coral.
Dans le fichier BoardConfig.mk
(ou dans n'importe quel fichier BoardConfig.mk
inclus), ajoutez ce qui suit :
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
Dans le fichier device/google/coral-sepolicy/private/property.te
, ajoutez les éléments suivants :
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 du fournisseur
Étant donné que les partitions système et produit ne peuvent pas dépendre du fournisseur, n'autorisez jamais l'accès aux propriétés du fournisseur à partir des partitions system
, system-ext
ou product
.
Annexe : Renommer des propriétés existantes
Lorsque vous devez abandonner une propriété et passer à une autre, utilisez Sysprop as APIs 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 propriété. Plus précisément, vous pouvez définir l'ancien nom à l'aide du champ legacy_prop_name
dans le 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 transformer une propriété
int
en propriétéstring
. Vous ne pouvez que changer le nom.Deuxièmement, seule l'API de lecture revient à l'ancien nom. L'API Write ne revient pas en arrière. Si la propriété système est accessible en écriture, vous ne pouvez pas la renommer.