Aggiungi proprietà di sistema

Questa pagina fornisce un metodo canonico per aggiungere o definire proprietà di sistema in Android, con linee guida per il refactoring delle proprietà di sistema esistenti. Assicurati di utilizzare le linee guida durante il refactoring, a meno che non ci sia un grave problema di compatibilità che imponga diversamente.

Passaggio 1: definizione della proprietà di sistema

Quando aggiungi una proprietà di sistema, decidi un nome per la proprietà e associala a un contesto di proprietà SELinux. Se non esiste un contesto esistente appropriato, creane uno nuovo. Il nome viene utilizzato quando si accede all'immobile; il contesto della proprietà viene utilizzato per controllare l'accessibilità in termini di SELinux. I nomi possono essere costituiti da qualsiasi stringa, ma AOSP consiglia di seguire un formato strutturato per renderli chiari.

Nome della proprietà

Utilizza questo formato con l'involucro Snake_case:

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

Utilizzare "" (omesso), ro (per le proprietà impostate una sola volta) o persist (per le proprietà che persistono dopo i riavvii) per il prefix dell'elemento .

Avvertenze

Usa ro solo quando sei sicuro di non aver bisogno prefix per essere scrivibile in futuro. ** Non specificare il prefisso ro .** Affidarsi invece a sepolicy per rendere prefix di sola lettura (in altre parole, scrivibile solo da init ).

Utilizzare persist solo quando si è certi che il valore debba essere mantenuto tra i riavvii e che l'utilizzo delle proprietà del sistema sia l'unica opzione. (Per i dettagli fare riferimento alla sezione Preparazione .)

Google esamina rigorosamente le proprietà del sistema che hanno proprietà ro o persist .

Il termine group viene utilizzato per aggregare proprietà correlate. È destinato a essere un nome di sottosistema simile nell'uso audio o telephony . Non utilizzare termini ambigui o sovraccarichi come 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à del 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 termini ambigui o sovraccarichi per descrivere questo elemento. (Puoi anche avere più di un subgroup .)

Molti nomi di gruppo sono già stati definiti. Controllare il file system/sepolicy/private/property_contexts e utilizzare i nomi dei gruppi esistenti ove possibile, invece di crearne di nuovi. La tabella seguente fornisce esempi di nomi di gruppi utilizzati di frequente.

Dominio Gruppo (e sottogruppo)
relativo al Bluetooth bluetooth
sysprops dalla riga cmd del kernel boot
sysprop che identificano una build build
legati alla telefonia telephony
relativo all'audio audio
relativi alla grafica graphics
correlato a vold vold

Di seguito viene definito l'uso del name e type nell'esempio 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 nominare un sysprop come audio.awesome_feature_enabled o semplicemente audio.awesome_feature , rinominalo come audio.awesome_feature.enabled per riflettere il tipo e l'intento della proprietà di sistema.

Non esiste una regola specifica su quale deve essere il tipo; questi sono i consigli d'uso:

  • enabled : utilizzare se il tipo è una proprietà di sistema booleana utilizzata per attivare o disattivare una funzionalità.
  • config : utilizzare se l'intento è chiarire che la proprietà di sistema non rappresenta uno stato dinamico del sistema; rappresenta un valore preconfigurato (ad esempio, un oggetto di sola lettura).
  • List : utilizzare se si tratta di una proprietà di sistema il cui valore è un elenco.
  • Timeoutmillis : 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 immobiliare

Il nuovo schema del contesto delle proprietà SELinux consente una granularità più fine e nomi più descrittivi. Similmente a quanto utilizzato per i nomi delle proprietà, AOSP consiglia il seguente formato:

{group}[_{subgroup}]*_prop

I termini sono definiti come segue:

group e subgroup hanno lo stesso significato definito per la regex di esempio precedente. Ad esempio, vold_config_prop indica proprietà che sono configurazioni di un fornitore e destinate ad essere impostate da vendor_init , mentre vold_status_prop o semplicemente vold_prop indica proprietà che devono esporre lo stato corrente di vold .

Quando si assegna un nome al contesto di una proprietà, scegliere nomi che riflettano l'utilizzo generale delle proprietà. In particolare, evitare i seguenti tipi di termini:

  • Termini che sembrano troppo generali e ambigui, come sys , system , default .
  • Termini che codificano direttamente l'accessibilità: come exported , apponly , ro , public , private .

Preferisci usi dei nomi come vold_config_prop a exported_vold_prop o vold_vendor_writable_prop e così via.

Tipo

Un tipo di proprietà può essere uno dei seguenti, come elencato nella tabella.

Tipo Definizione
Booleano true o 1 per vero, false o 0 per falso
Numero intero intero con segno a 64 bit
Intero senza segno numero intero a 64 bit senza segno
Doppio virgola mobile a doppia precisione
Corda qualsiasi stringa UTF-8 valida
enum i valori possono essere qualsiasi stringa UTF-8 valida senza spazi
Elenco di cui sopra Come delimitatore viene utilizzata una virgola ( , ).
L'elenco di numeri interi [1, 2, 3] è memorizzato come 1,2,3

Internamente, tutte le proprietà vengono archiviate come stringhe. È possibile applicare il tipo specificandolo come file property_contexts . Per ulteriori informazioni, vedere property_contexts nel passaggio 3 .

Fase 2: determinazione dei livelli di accessibilità richiesti

Esistono quattro macro helper che definiscono una proprietà.

Tipo di accessibilità Senso
system_internal_prop Proprietà utilizzate solo in /system
system_restricted_prop Proprietà che vengono lette all'esterno /system , ma non scritte
system_vendor_config_prop Proprietà che vengono lette all'esterno /system e scritte solo da vendor_init
system_public_prop Proprietà che vengono lette e scritte all'esterno /system

L'ambito dell'accesso alle proprietà del sistema è il più restrittivo possibile. In passato, un ampio accesso ha comportato la rottura delle app e vulnerabilità della sicurezza. Considera le seguenti domande durante la definizione dell'ambito:

  • Questa proprietà di sistema deve essere persistente? (se sì, perché?)
  • Quale processo dovrebbe avere accesso in lettura a questa proprietà?
  • Quale processo dovrebbe avere accesso in scrittura a questa proprietà?

Utilizzare le domande precedenti e il seguente albero decisionale come strumenti per determinare un ambito appropriato per l'accesso.

Decision tree for determining the scope of access

Figura 1. Albero decisionale per determinare l'ambito di accesso alle proprietà del sistema

Passaggio 3: aggiunta a system/sepolicy

Quando si accede a sysprop, SELinux controlla l'accessibilità dei processi. Dopo aver determinato quale livello di accessibilità è richiesto, definire i contesti delle proprietà in system/sepolicy , insieme a ulteriori regole di autorizzazione e non autorizzazione su ciò che i processi possono (e non possono) leggere o scrivere.

Innanzitutto, definire il contesto della proprietà nel file system/sepolicy/public/property.te . Se la proprietà è interna al sistema, definirla nel file system/sepolicy/private/property.te . Utilizza una delle macro system_[accessibility]_prop([context]) che fornisce l'accessibilità richiesta per la proprietà del sistema. Questo è 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, concedere 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 . Utilizzare private quando possibile; public è adatto solo se la macro set_prop o get_prop influisce su qualsiasi dominio esterno 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, aggiungere alcune regole Neverallow per ridurre ulteriormente l'accessibilità nell'ambito della macro. Ad esempio, supponiamo di aver utilizzato system_restricted_prop perché le proprietà del 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 (come vendor_init ), vietare i processi del fornitore che non necessitano dell'accesso in lettura.

Utilizzare la seguente sintassi per limitare l'accesso in scrittura e 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 le regole neverallow nel file system/sepolicy/private/{domain}.te se la regola neverallow è associata a un dominio specifico. Per regole più ampie di Neverallow, utilizza domini generali come questi, ove appropriato:

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

Nel file system/sepolicy/private/audio.te , inserire quanto segue:

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

Nel file system/sepolicy/private/property.te , inserire 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. Quindi {domain -coredomain -vendor_init} significa "tutti i processi del fornitore tranne vendor_init ".

Infine, associa una proprietà di sistema al contesto della proprietà. Ciò garantisce che l'accesso concesso e le regole neverallow applicate ai contesti delle proprietà vengano applicati alle proprietà effettive. Per fare ciò, aggiungi una voce al file property_contexts , un file che descrive la mappatura tra proprietà di sistema e contesti di proprietà. In questo file è possibile 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 è possibile specificare il tipo di proprietà, che può essere uno dei seguenti:

  • bool
  • int
  • uint
  • double
  • enum [list of possible values...]
  • string (Utilizzare string per le proprietà dell'elenco.)

Assicurati che ogni voce abbia il suo tipo designato 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

Quando una voce esatta e una voce prefisso sono in conflitto, la voce esatta ha la precedenza. Per ulteriori esempi, vedere system/sepolicy/private/property_contexts .

Passaggio 4: determinazione dei requisiti di stabilità

La stabilità è un altro aspetto delle proprietà del sistema e differisce dall'accessibilità. La stabilità riguarda la possibilità o meno di modificare una proprietà di sistema (ad esempio ribattezzarla o addirittura rimuoverla) in futuro. Ciò è particolarmente importante poiché il sistema operativo Android diventa modulare. Con Treble, le partizioni di sistema, fornitore e prodotto possono essere aggiornate indipendentemente l'una dall'altra. Con Mainline, alcune parti del sistema operativo sono modularizzate come moduli aggiornabili (in APEX o APK).

Se una proprietà di sistema deve essere utilizzata su parti di software aggiornabili, ad esempio su partizioni di sistema e fornitore, deve essere stabile. Tuttavia, se viene utilizzato solo all'interno, ad esempio, di uno specifico modulo Mainline, è possibile modificarne il nome, il tipo o i contesti delle proprietà e persino rimuoverlo.

Porre le seguenti domande per determinare la stabilità di una proprietà di sistema:

  • Questa proprietà di sistema è destinata ad essere configurata dai partner (o configurata in modo diverso per dispositivo)? Se sì, deve essere stabile.
  • Questa proprietà di sistema definita da AOSP è destinata ad essere scritta o letta dal codice (non dal processo) che esiste in partizioni non di sistema come vendor.img o product.img ? Se sì, deve essere stabile.
  • È possibile accedere a questa proprietà di sistema attraverso i moduli Mainline o attraverso un modulo Mainline e la parte non aggiornabile della piattaforma? Se sì, deve essere stabile.

Per le proprietà di sistema stabili, definirle formalmente ciascuna come API e utilizzare l'API per accedere alla proprietà di sistema, come spiegato nel Passaggio 6 .

Passaggio 5: impostazione delle proprietà in fase di compilazione

Imposta le proprietà in fase di compilazione con le variabili makefile. Tecnicamente, i valori vengono inseriti in {partition}/build.prop . Quindi init legge {partition}/build.prop per impostare le proprietà. Esistono due insiemi di tali variabili: PRODUCT_{PARTITION}_PROPERTIES e TARGET_{PARTITION}_PROP .

PRODUCT_{PARTITION}_PROPERTIES contiene un elenco di valori di 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 per ogni immobile.

{prop}?={value} è un'assegnazione facoltativa; {prop} imposta su {value} solo se non ci sono assegnazioni {prop}={value} . Se esistono più assegnazioni facoltative, prevale la prima.

# 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 inviato direttamente a {partition}/build.prop . Ogni file contiene un elenco di coppie {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

Per maggiori dettagli, vedere build/make/core/sysprop.mk .

Passaggio 6: accedere alle proprietà in fase di esecuzione

Naturalmente, le proprietà possono essere lette e scritte in fase di esecuzione.

Script di inizializzazione

I file di script init (solitamente file *.rc) possono leggere una proprietà tramite ${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 setprop comando.

# 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}

comandi della shell getprop e setprop

È possibile 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 API generate automaticamente che sono concrete e tipizzate. L'impostazione scope con Public rende inoltre disponibili le API generate ai moduli oltre i confini e garantisce la stabilità dell'API. Ecco un esempio di un file .sysprop , di un modulo Android.bp e del 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 ulteriori informazioni, consulta Implementazione delle proprietà di sistema come API .

Funzioni e metodi di proprietà di basso livello C/C++, Java e Rust

Quando 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 di proprietà di sistema C++. libc ha l'API sottostante, mentre le funzioni libbase e libcutils sono wrapper. Se possibile, utilizzare le funzioni sysprop libbase ; sono i più convenienti e i binari host possono utilizzare le funzioni libbase . Per ulteriori dettagli, vedere sys/system_properties.h ( libc ), android-base/properties.h ( libbase ) e cutils/properties.h ( libcutils ).

La classe android.os.SystemProperties offre metodi di proprietà del sistema Java.

Il modulo rustutils::system_properties offre funzioni e tipi di proprietà del sistema Rust.

Appendice: aggiunta di proprietà specifiche del fornitore

I partner (inclusi i Googler che lavorano nel contesto dello sviluppo di Pixel) desiderano definire proprietà di sistema specifiche dell'hardware (o del dispositivo). Le proprietà specifiche del fornitore sono proprietà di proprietà del partner univoche per il proprio hardware o dispositivo, non per la piattaforma. Poiché dipendono dall'hardware o dal dispositivo, sono pensati per essere utilizzati all'interno delle partizioni /vendor o /odm .

A partire da Project Treble, le proprietà della piattaforma e le proprietà del fornitore sono state completamente divise per evitare conflitti. Di seguito viene descritto come definire le proprietà del fornitore e vengono indicate le proprietà del fornitore che devono essere sempre utilizzate.

Spazio dei nomi sui nomi di proprietà e contesto

Tutte le proprietà del fornitore devono iniziare con uno dei seguenti prefissi 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 usarlo per proprietà normali.

I seguenti esempi utilizzano tutti uno dei prefissi elencati in precedenza:

  • 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 è anche per compatibilità. Quelli che seguono sono esempi:

  • vendor_radio_prop .
  • vendor_faceauth_prop .
  • vendor_usb_prop .

È responsabilità del fornitore nominare e gestire le proprietà, quindi segui il formato suggerito nel passaggio 2 , oltre ai requisiti degli spazi dei nomi del fornitore.

Regole SEPolicy e property_contexts 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 Coral.

Nel file BoardConfig.mk (o in qualsiasi BoardConfig.mk include), 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: ridenominazione delle proprietà esistenti

Quando devi deprecare una proprietà e passare a una nuova, utilizza Sysprop come API per rinominare le proprietà esistenti. Ciò mantiene la compatibilità con le versioni precedenti specificando sia il nome legacy che il nuovo nome della proprietà. Nello specifico, puoi impostare il nome legacy 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 prop int in una prop string . Puoi cambiare solo il nome.

  • In secondo luogo, solo l'API di lettura ritorna al nome legacy. L'API di scrittura non esegue il fallback. Se il sysprop è scrivibile, non puoi rinominarlo.