Questa pagina fornisce un metodo canonico per aggiungere o definire le proprietà di sistema in Android, con linee guida per il refactoring delle proprietà di sistema esistenti. Assicurati di utilizzare le linee guida quando esegui il refactoring, a meno che tu non abbia un forte problema di compatibilità che comporti diversamente.
Passaggio 1: definisci la proprietà di sistema
Quando aggiungi una proprietà di sistema, scegli un nome per la proprietà e associala a un contesto di proprietà SELinux. Se non esiste un contesto appropriato, creane uno nuovo. Il nome viene utilizzato per accedere alla proprietà; il contesto della proprietà viene utilizzato per controllare l'accessibilità in termini di SELinux. I nomi possono essere qualsiasi stringa, ma AOSP consiglia di seguire un formato strutturato per chiarirli.
Nome proprietà
Utilizza questo formato con maiuscole/minuscole snake_case:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Utilizza "" (omesso), ro
(per le proprietà impostate una sola volta) o persist
(per le proprietà persistenti tra i riavvii) per l'elemento prefix
.
Precisazioni
Utilizza ro
solo quando hai la certezza che non sia necessario che prefix
sia scrivibile
in futuro. ** Non specificare il prefisso ro
.** Utilizza invece sepolicy per rendere prefix
di sola lettura (in altre parole, accessibile in scrittura solo da init
).
Utilizza persist
solo se hai la certezza che il valore debba essere mantenuto durante i riavvii e che l'utilizzo delle proprietà di sistema è l'unica opzione disponibile.
Google esamina rigorosamente le proprietà di sistema che hanno proprietà ro
o persist
.
Il termine group
viene utilizzato per aggregare le proprietà correlate. È destinato a
essere un nome di sottosistema simile in uso a audio
o telephony
. Non utilizzare
termini ambigui o sovraccarichi, ad esempio sys
, system
, dev
, default
o
config
.
È pratica comune utilizzare il nome del tipo di dominio di un processo che ha accesso esclusivo in lettura o scrittura alle proprietà di sistema. Ad esempio, per le proprietà di sistema a cui il processo vold
ha accesso in scrittura, è comune utilizzare vold
(il nome del tipo di dominio per il processo) come nome del gruppo.
Se necessario, aggiungi subgroup
per classificare ulteriormente le proprietà, ma evita di usare termini ambigui o sovraccarichi per descrivere questo elemento. Puoi anche avere più di un subgroup
.
Molti nomi di gruppi sono già stati definiti. Controlla il file system/sepolicy/private/property_contexts
e, se possibile, utilizza i nomi dei gruppi esistenti, anziché crearne di nuovi. La tabella seguente fornisce
esempi di nomi di gruppi utilizzati di frequente.
Dominio | Gruppo (e sottogruppo) |
---|---|
relativo al bluetooth | bluetooth |
sysprops da kernel cmdline | boot |
sysprop che identificano una build | build
|
relativo alla telefonia | telephony |
relativi all'audio | audio |
risorse grafiche | graphics |
relativi a vold | vold |
Quanto segue definisce l'utilizzo di name
e type
nell'esempio di regex precedente.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
identifica una proprietà di sistema all'interno di un gruppo.type
è un elemento facoltativo che chiarisce il tipo o l'intento della proprietà di sistema. Ad esempio, invece di denominare un sysprop comeaudio.awesome_feature_enabled
o soloaudio.awesome_feature
, rinominaloaudio.awesome_feature.enabled
per riflettere il tipo e l'intent della proprietà di sistema.
Non esiste una regola specifica su quale deve essere il tipo; questi sono suggerimenti sull'utilizzo:
enabled
: da utilizzare se il tipo è una proprietà di sistema booleana utilizzata per attivare o disattivare una funzionalità.config
: da utilizzare se lo scopo è chiarire che la proprietà di sistema non rappresenta uno stato dinamico del sistema, ma un valore preconfigurato (ad esempio, un elemento di sola lettura).List
: da utilizzare se si tratta di una proprietà di sistema il cui valore è un elenco.Timeoutmillis
: da utilizzare se si tratta di una proprietà di sistema per un valore di timeout in unità di ms.
Esempi:
persist.radio.multisim.config
drm.service.enabled
Contesto della proprietà
Il nuovo schema di contesto delle proprietà SELinux consente una granularità maggiore e nomi più descrittivi. Analogamente a quanto utilizzato per i nomi delle proprietà, AOSP consiglia il seguente formato:
{group}[_{subgroup}]*_prop
Tali termini sono definiti come segue:
group
e subgroup
hanno lo stesso significato definito per la precedente
espressione regolare di esempio. Ad esempio, vold_config_prop
indica le proprietà che sono configurazioni di un fornitore e che devono essere impostate da vendor_init
, mentre vold_status_prop
o solo vold_prop
indicano proprietà che devono esporre lo stato attuale di vold
.
Quando assegni un nome al contesto di una proprietà, scegli nomi che riflettano l'utilizzo generale delle proprietà. In particolare, evita i seguenti tipi di termini:
- Termini troppo generici e ambigui, ad esempio
sys
,system
edefault
. - Termini che codificano direttamente l'accessibilità: ad esempio
exported
,apponly
,ro
,public
,private
.
Preferenza per l'utilizzo di nomi come da vold_config_prop
a exported_vold_prop
o vold_vendor_writable_prop
.
Tipo
Un tipo di proprietà può essere uno dei seguenti, come indicato nella tabella.
Tipo | Definizione |
---|---|
Booleano | true o 1 per vero, false o 0 per falso |
Numero intero | numero intero a 64 bit firmato |
Numero intero senza segno | numero intero a 64 bit senza segno |
Doppio | rappresentazione in virgola mobile a precisione doppia |
Stringa | qualsiasi stringa UTF-8 valida |
enum | i valori possono essere qualsiasi stringa UTF-8 valida senza spazi vuoti |
Elenco delle risposte precedenti | Una virgola (, ) viene utilizzata come delimitatoreL'elenco interi [1, 2, 3] viene memorizzato come 1,2,3 |
Internamente, tutte le proprietà sono memorizzate come stringhe. Puoi applicare in modo forzato il tipo
specificandolo come file property_contexts
. Per maggiori informazioni, consulta
property_contexts
nel Passaggio 3.
Passaggio 2: determina i livelli di accessibilità richiesti
Esistono quattro macro helper che definiscono una proprietà.
Tipo di accessibilità | Significato |
---|---|
system_internal_prop |
Proprietà utilizzate solo in /system |
system_restricted_prop |
Proprietà che vengono lette all'esterno di /system , ma non scritte |
system_vendor_config_prop |
Proprietà lette all'esterno di /system e scritte solo da vendor_init |
system_public_prop |
Proprietà lette e scritte all'esterno di /system |
Limitare l'ambito dell'accesso alle proprietà di sistema. In passato, l'accesso ampio ha causato malfunzionamenti delle app e vulnerabilità di sicurezza. Prendi in considerazione le seguenti domande durante la definizione dell'ambito:
- Questa proprietà di sistema deve essere persistente? (Se sì, perché?)
- Quale processo deve avere accesso in lettura a questa proprietà?
- Quale processo deve avere accesso in scrittura a questa proprietà?
Usa le domande precedenti e il seguente albero decisionale come strumenti per determinare un ambito appropriato per l'accesso.
Figura 1. Albero decisionale per determinare l'ambito di accesso alle proprietà di sistema
Passaggio 3: aggiungi a system/sepolicy
Quando accede a sysprop, SELinux controlla l'accessibilità dei processi. Dopo
aver stabilito il livello di accessibilità richiesto, definisci i contesti delle proprietà
in system/sepolicy
, insieme ad altre regole allow e neverallow
per indicare quali processi sono (e non sono) autorizzati a leggere o scrivere.
Innanzitutto, definisci il contesto della proprietà nel file system/sepolicy/public/property.te
. Se la proprietà è interna al sistema, definiscila nel file system/sepolicy/private/property.te
. Utilizza una delle
macro system_[accessibility]_prop([context])
che fornisce l'accessibilità
richiesta della tua proprietà di sistema. Ecco un esempio per il file system/sepolicy/public/property.te
:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Esempio da aggiungere nel file system/sepolicy/private/property.te
:
system_internal_prop(audio_baz_prop)
In secondo luogo, concedi l'accesso in lettura e (o) scrittura al contesto della proprietà. Utilizza le macro set_prop
e get_prop
per concedere l'accesso, nel file
system/sepolicy/public/{domain}.te
o system/sepolicy/private/{domain}.te
. Utilizza private
quando possibile. public
è adatto solo se la macro set_prop
o get_prop
interessa domini esterni al dominio principale.
Esempio, nel file system/sepolicy/private/audio.te
:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Esempio, nel file system/sepolicy/public/domain.te
:
get_prop(domain, audio_bar_prop)
In terzo luogo, aggiungi alcune regole che non consentono l'accesso per ridurre ulteriormente l'accessibilità
nell'ambito della macro. Ad esempio, supponiamo che tu abbia utilizzato system_restricted_prop
perché le proprietà di sistema devono essere lette dai processi del fornitore. Se l'accesso in lettura non è richiesto da tutti i processi del fornitore ed è richiesto solo da un determinato insieme di processi (ad esempio, vendor_init
), proibisci i processi del fornitore che non richiedono l'accesso in lettura.
Utilizza la seguente sintassi per limitare l'accesso in scrittura e in lettura:
Per limitare l'accesso in scrittura:
neverallow [domain] [context]:property_service set;
Per limitare l'accesso in lettura:
neverallow [domain] [context]:file no_rw_file_perms;
Inserisci regole non consentite nel file system/sepolicy/private/{domain}.te
se
la regola sempre consentita è associata a un dominio specifico. Per regole non consentite più generali, usa
domini generici come i seguenti laddove opportuno:
system/sepolicy/private/property.te
system/sepolicy/private/coredomain.te
system/sepolicy/private/domain.te
Nel file system/sepolicy/private/audio.te
, inserisci quanto segue:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
Nel file system/sepolicy/private/property.te
, inserisci quanto segue:
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
Tieni presente che {domain -coredomain}
acquisisce tutti i processi del fornitore. Pertanto,
{domain -coredomain -vendor_init}
significa "tutti i processi del fornitore tranne
vendor_init
".
Infine, associa una proprietà di sistema al contesto della proprietà. In questo modo ti assicuri che l'accesso concesso e le regole che non consentono l'accesso applicate ai contesti delle proprietà vengano applicate alle proprietà effettive. Per farlo, aggiungi una voce al file property_contexts
, un file che descrive la mappatura tra proprietà di sistema e contesti delle proprietà. In questo file puoi specificare una singola proprietà o un prefisso per le proprietà da mappare in un contesto.
Questa è la sintassi per mappare una singola proprietà:
[property_name] u:object_r:[context_name]:s0 exact [type]
Questa è la sintassi per mappare un prefisso:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
Facoltativamente, puoi specificare il tipo di proprietà, che può essere uno dei seguenti:
bool
int
uint
double
enum [list of possible values...]
string
(utilizzastring
per le proprietà dell'elenco).
Assicurati che per ogni voce sia designato il tipo quando possibile, poiché type
viene applicato quando si imposta property
. L'esempio seguente mostra come scrivere una
mappatura:
# 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
In caso di conflitto tra una voce esatta e una voce prefisso, la voce esatta ha
la precedenza. Per altri esempi, vedi system/sepolicy/private/property_contexts
.
Passaggio 4: definisci i requisiti di stabilità
La stabilità è un altro aspetto delle proprietà del sistema e differisce dall'accessibilità. La stabilità riguarda se una proprietà di sistema può essere modificata (ad esempio rinominata o addirittura rimossa) in futuro. Ciò è particolarmente importante quando il sistema operativo Android diventa modulare. Con Treble, le partizioni di sistema, fornitore e prodotto possono essere aggiornate in modo indipendente. Con Mainline, alcune parti del sistema operativo sono modularizzate in moduli aggiornabili (in APEX o APK).
Se una proprietà di sistema deve essere utilizzata in componenti software aggiornabili, ad esempio in partizioni di sistema e dei fornitori, deve essere stabile. Tuttavia, se viene utilizzato, ad esempio, solo all'interno di un modulo Mainline specifico, puoi modificarne il nome, il tipo o i contesti delle proprietà e persino rimuoverlo.
Per determinare la stabilità di una proprietà di sistema, poni le seguenti domande:
- Questa proprietà di sistema deve essere configurata dai partner (o configurata in modo diverso in base al dispositivo)? Se sì, deve essere stabile.
- Questa proprietà di sistema definita da AOSP è destinata a essere scritta o letta dal codice (non da un processo) esistente in partizioni non di sistema come
vendor.img
oproduct.img
? Se sì, deve essere stabile. - È possibile accedere a questa proprietà di sistema attraverso i moduli Mainline o in un modulo Mainline e nella parte non aggiornabile della piattaforma? Se sì, deve essere stabile.
Per le proprietà di sistema stabili, definisci formalmente ciascuna come API e utilizza l'API per accedere alla proprietà di sistema, come spiegato nel Passaggio 6.
Passaggio 5: imposta le proprietà al momento della creazione
Imposta le proprietà al momento della creazione con le variabili makefile. Tecnicamente, i valori sono integrati in {partition}/build.prop
. init
legge
{partition}/build.prop
per impostare le proprietà. Esistono due insiemi di
variabili: PRODUCT_{PARTITION}_PROPERTIES
e TARGET_{PARTITION}_PROP
.
PRODUCT_{PARTITION}_PROPERTIES
contiene un elenco di valori delle proprietà. La sintassi
è {prop}={value}
o {prop}?={value}
.
{prop}={value}
è un'assegnazione normale che garantisce che {prop}
sia impostato su
{value}
. È possibile una sola assegnazione di questo tipo per ogni singola proprietà.
{prop}?={value}
è un compito facoltativo; {prop}
viene impostato su {value}
solo se
non sono presenti assegnazioni {prop}={value}
. Se esistono più incarichi facoltativi, ha vinto il primo.
# 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 un elenco di file, che viene emesso direttamente in
{partition}/build.prop
. Ogni file contiene un elenco di {prop}={value}
coppie.
# 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
Per maggiori dettagli, vedi
build/make/core/sysprop.mk
.
Passaggio 6: accedi alle proprietà in fase di runtime
Le proprietà possono essere lette e scritte in fase di runtime.
Script di inizializzazione
I file di script di inizializzazione (di solito file *.rc) possono leggere una proprietà in base a ${prop}
o
${prop:-default}
, possono impostare un'azione che viene eseguita ogni volta che una proprietà diventa un
valore specifico e possono scrivere le proprietà utilizzando il 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}
i comandi della shell getprop e setprop
Puoi utilizzare, rispettivamente, i comandi della shell getprop
o setprop
per leggere o scrivere le proprietà. Per maggiori dettagli, richiama getprop --help
o
setprop --help
.
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop come API per C++/Java/Rust
Con sysprop come API, puoi definire le proprietà di sistema e utilizzare l'API generata automaticamente, che sono concrete e digitate. Inoltre, se imposti scope
con Public
, le API generate vengono rese disponibili per i moduli oltre i confini e ne garantisce la stabilità. Ecco un esempio di un file .sysprop
, un modulo Android.bp
e codice C++, Java e Rust che li utilizza.
# 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);
Per saperne di più, consulta Implementare le proprietà di sistema come API.
Funzioni e metodi delle proprietà di basso livello C/C++, Java e Rust
Se possibile, utilizza Sysprop come API anche se sono disponibili funzioni C/C++ o Rust di basso livello o metodi Java di basso livello.
libc
, libbase
e libcutils
offrono funzioni per le proprietà di sistema C++. libc
ha l'API sottostante, mentre le funzioni libbase
e libcutils
sono
wrapper. Se è possibile, utilizza le funzioni sysprop libbase
, che sono le più pratiche e i file binari degli host possono utilizzare le funzioni libbase
. Per ulteriori
dettagli, consulta le pagine sys/system_properties.h
(libc
), android-base/properties.h
(libbase
) e cutils/properties.h
(libcutils
).
La classe android.os.SystemProperties
offre metodi per le proprietà del sistema Java.
Il modulo rustutils::system_properties
offre funzioni e tipi delle proprietà del sistema Rust.
Appendice: aggiunta di proprietà specifiche del fornitore
I partner (inclusi i Googler che lavorano nel contesto dello sviluppo di Pixel) vogliono definire proprietà di sistema specifiche per hardware (o dispositivo).
Le proprietà specifiche del fornitore sono proprietà dei partner uniche per il loro
hardware o dispositivo, non per la piattaforma. Poiché dipendono dall'hardware o dal dispositivo, devono essere utilizzati all'interno delle partizioni /vendor
o /odm
.
A partire dal progetto Treble, le proprietà della piattaforma e quelle del fornitore sono state completamente suddivise per evitare conflitti. Di seguito viene descritto come definire le proprietà del fornitore e indicare quali proprietà del fornitore devono essere sempre utilizzate.
Spazio dei nomi sui nomi di proprietà e contesto
Tutte le proprietà del fornitore devono iniziare con uno dei prefissi seguenti per evitare conflitti tra loro e le proprietà di altre partizioni.
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.
Tieni presente che ro.hardware.
è consentito come prefisso, ma solo per compatibilità.
Non utilizzarlo per le normali proprietà.
I seguenti esempi utilizzano tutti uno dei prefissi elencati sopra:
vendor.display.primary_red
persist.vendor.faceauth.use_disk_cache
ro.odm.hardware.platform
Tutti i contesti delle proprietà del fornitore devono iniziare con vendor_
. Questo vale anche per
la compatibilità. Ecco alcuni esempi:
vendor_radio_prop
.vendor_faceauth_prop
.vendor_usb_prop
.
È responsabilità del fornitore assegnare un nome e gestire le proprietà, quindi segui il formato suggerito nel passaggio 2, oltre ai requisiti degli spazi dei nomi del fornitore.
Regole SEPolicy e proprietà_contesti specifici del fornitore
Le proprietà del fornitore possono essere definite dalla macro vendor_internal_prop
. Inserisci le
regole specifiche del fornitore che definisci nella directory BOARD_VENDOR_SEPOLICY_DIRS
.
Ad esempio, supponiamo che tu stia definendo una proprietà faceauth del fornitore in corallo.
Nel file BoardConfig.mk
(o in qualsiasi elemento incluso in BoardConfig.mk
), inserisci quanto segue:
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
Nel file device/google/coral-sepolicy/private/property.te
, inserisci quanto segue:
vendor_internal_prop(vendor_faceauth_prop)
Nel file device/google/coral-sepolicy/private/property_contexts
, inserisci quanto segue:
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
Limitazioni delle proprietà del fornitore
Poiché le partizioni di sistema e di prodotto non possono dipendere dal fornitore, non
consentire mai l'accesso alle proprietà del fornitore dalle partizioni system
, system-ext
o
product
.
Appendice: Rinominare le proprietà esistenti
Quando devi ritirare una proprietà e spostarla a una nuova, utilizza Sysprop come API per rinominare le proprietà esistenti. Questo mantiene la compatibilità con le versioni precedenti specificando sia il nome precedente sia quello della nuova proprietà. In particolare, puoi impostare il nome precedente tramite il campo legacy_prop_name
nel file .sysprop
. L'API generata tenta di leggere prop_name
e utilizza legacy_prop_name
se prop_name
non esiste.
Ad esempio, i passaggi seguenti rinominano awesome_feature_foo_enabled
in foo.awesome_feature.enabled
.
Nel file 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"
}
Nel codice 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);
Tieni presenti le seguenti avvertenze:
Innanzitutto, non puoi modificare il tipo di sysprop. Ad esempio, non puoi trasformare una proprietà
int
in una propostastring
. Puoi solo cambiare il nome.In secondo luogo, solo l'API di lettura utilizza il nome precedente. L'API write non utilizza le versioni precedenti. Se il valore sysprop è scrivibile, non puoi rinominarlo.