Esta página fornece um método canônico para adicionar ou definir propriedades do sistema no Android, com diretrizes para refatorar propriedades existentes do sistema. Certifique-se de usar as diretrizes ao refatorar, a menos que você tenha um forte problema de compatibilidade que indique o contrário.
Etapa 1: Definindo a propriedade do sistema
Ao adicionar uma propriedade do sistema, decida um nome para a propriedade e associe-a a um contexto de propriedade do SELinux. Se não houver um contexto existente apropriado, crie um novo. O nome é utilizado no acesso à propriedade; o contexto da propriedade é usado para controlar a acessibilidade em termos do SELinux. Os nomes podem ser qualquer string, mas o AOSP recomenda que você siga um formato estruturado para torná-los claros.
Nome da propriedade
Use este formato com o case snake_case:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Use “” (omitido), ro
(para propriedades definidas apenas uma vez) ou persist
(para propriedades que persistem durante reinicializações) para o elemento prefix
.
Ressalvas
Use ro
somente quando tiver certeza de que não precisa prefix
para poder ser gravado no futuro. ** Não especifique o prefixo ro
.** Em vez disso, confie na sepolicy para tornar prefix
somente leitura (em outras palavras, gravável apenas por init
).
Use persist
apenas quando tiver certeza de que o valor deve persistir durante as reinicializações e que usar as propriedades do sistema é sua única opção. (Para obter detalhes, consulte a seção Preparação .)
O Google analisa rigorosamente as propriedades do sistema que possuem propriedades ro
ou persist
.
O termo group
é usado para agregar propriedades relacionadas. Pretende ser um nome de subsistema semelhante em uso a audio
ou telephony
. Não use termos ambíguos ou sobrecarregados, como sys
, system
, dev
, default
ou config
.
É prática comum usar o nome do tipo de domínio de um processo que possui acesso exclusivo de leitura ou gravação às propriedades do sistema. Por exemplo, para as propriedades do sistema às quais o processo vold
tem acesso de gravação, é comum usar vold
(o nome do tipo de domínio do processo) como nome do grupo.
Se necessário, adicione subgroup
para categorizar ainda mais as propriedades, mas evite termos ambíguos ou sobrecarregados para descrever este elemento. (Você também pode ter mais de um subgroup
.)
Muitos nomes de grupos já foram definidos. Verifique o arquivo system/sepolicy/private/property_contexts
e use nomes de grupos existentes sempre que possível, em vez de criar novos. A tabela a seguir fornece exemplos de nomes de grupos usados com frequência.
Domínio | Grupo (e subgrupo) |
---|---|
relacionado ao bluetooth | bluetooth |
sysprops do cmdline do kernel | boot |
sysprops que identificam uma compilação | build |
relacionado à telefonia | telephony |
relacionado a áudio | audio |
gráficos relacionados | graphics |
vold relacionado | vold |
O seguinte define o uso de name
e type
no exemplo de regex anterior.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
identifica uma propriedade do sistema dentro de um grupo.type
é um elemento opcional que esclarece o tipo ou intenção da propriedade do sistema. Por exemplo, em vez de nomear um sysprop comoaudio.awesome_feature_enabled
ou apenasaudio.awesome_feature
, renomeie-o comoaudio.awesome_feature.enabled
para refletir o tipo e a intenção da propriedade do sistema.
Não existe uma regra específica sobre qual deve ser o tipo; estas são recomendações de uso:
-
enabled
: use se o tipo for uma propriedade booleana do sistema usada para ativar ou desativar um recurso. -
config
: Use se a intenção é esclarecer que a propriedade do sistema não representa um estado dinâmico do sistema; representa um valor pré-configurado (por exemplo, algo somente leitura). -
List
: Use se for uma propriedade do sistema cujo valor é uma lista. -
Timeoutmillis
: Use se for uma propriedade do sistema para um valor de tempo limite em unidades de ms.
Exemplos:
-
persist.radio.multisim.config
-
drm.service.enabled
Contexto da propriedade
O novo esquema de contexto de propriedades do SELinux permite granularidade mais refinada e nomes mais descritivos. Semelhante ao que é usado para nomes de propriedades, o AOSP recomenda o seguinte formato:
{group}[_{subgroup}]*_prop
Os termos são definidos da seguinte forma:
group
e subgroup
têm o mesmo significado definido para o exemplo anterior de regex . Por exemplo, vold_config_prop
significa propriedades que são configurações de um fornecedor e devem ser definidas por vendor_init
, enquanto vold_status_prop
ou apenas vold_prop
significa propriedades que devem expor o status atual de vold
.
Ao nomear um contexto de propriedade, escolha nomes que reflitam o uso geral das propriedades. Em particular, evite os seguintes tipos de termos:
- Termos que parecem muito gerais e ambíguos, como
sys
,system
,default
. - Termos que codificam diretamente a acessibilidade: como
exported
,apponly
,ro
,public
,private
.
Prefira usos de nomes como vold_config_prop
a exported_vold_prop
ou vold_vendor_writable_prop
e assim por diante.
Tipo
Um tipo de propriedade pode ser um dos seguintes, conforme listado na tabela.
Tipo | Definição |
---|---|
boleano | true ou 1 para verdadeiro, false ou 0 para falso |
Inteiro | inteiro assinado de 64 bits |
Inteiro não assinado | inteiro não assinado de 64 bits |
Dobro | ponto flutuante de precisão dupla |
Corda | qualquer string UTF-8 válida |
enumeração | os valores podem ser qualquer string UTF-8 válida sem espaços em branco |
Lista acima | Uma vírgula ( , ) é usada como delimitadorA lista de inteiros [1, 2, 3] é armazenada como 1,2,3 |
Internamente, todas as propriedades são armazenadas como strings. Você pode impor o tipo especificando-o como um arquivo property_contexts
. Para obter mais informações, consulte property_contexts
na Etapa 3 .
Etapa 2: Determinar os níveis de acessibilidade necessários
Existem quatro macros auxiliares que definem uma propriedade.
Tipo de acessibilidade | Significado |
---|---|
system_internal_prop | Propriedades que são usadas apenas em /system |
system_restricted_prop | Propriedades que são lidas fora /system , mas não escritas |
system_vendor_config_prop | Propriedades que são lidas fora /system e escritas apenas por vendor_init |
system_public_prop | Propriedades que são lidas e escritas fora de /system |
Defina o escopo do acesso às propriedades do sistema da forma mais restrita possível. No passado, o amplo acesso resultou em falhas de aplicativos e vulnerabilidades de segurança. Considere as seguintes questões ao definir o escopo:
- Esta propriedade do sistema precisa ser persistida? (se sim, por quê?)
- Qual processo deveria ter acesso de leitura a esta propriedade?
- Qual processo deve ter acesso de gravação a esta propriedade?
Use as perguntas anteriores e a árvore de decisão a seguir como ferramentas para determinar um escopo apropriado de acesso.
Figura 1. Árvore de decisão para determinar o escopo de acesso às propriedades do sistema
Etapa 3: Adicionando ao sistema/sepolicy
Ao acessar o sysprop, o SELinux controla a acessibilidade dos processos. Depois de determinar qual nível de acessibilidade é necessário, defina os contextos de propriedade em system/sepolicy
, juntamente com regras adicionais de permissão e neverallow sobre o que os processos têm (ou não) permissão para ler ou gravar.
Primeiro, defina o contexto da propriedade no arquivo system/sepolicy/public/property.te
. Se a propriedade for interna do sistema, defina-a no arquivo system/sepolicy/private/property.te
. Use uma das macros system_[accessibility]_prop([context])
que fornece a acessibilidade necessária de sua propriedade de sistema. Este é um exemplo para o arquivo system/sepolicy/public/property.te
:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Exemplo para adicionar no arquivo system/sepolicy/private/property.te
:
system_internal_prop(audio_baz_prop)
Segundo, conceda acesso de leitura e (ou) gravação ao contexto da propriedade. Use as macros set_prop
e get_prop
para conceder acesso, no arquivo system/sepolicy/public/{domain}.te
ou system/sepolicy/private/{domain}.te
. Use private
sempre que possível; public
é adequado apenas se a macro set_prop
ou get_prop
afetar quaisquer domínios fora do domínio principal.
Exemplo, no arquivo system/sepolicy/private/audio.te
:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Exemplo, no arquivo system/sepolicy/public/domain.te
:
get_prop(domain, audio_bar_prop)
Terceiro, adicione algumas regras neverallow para reduzir ainda mais a acessibilidade abrangida pela macro. Por exemplo, suponha que você usou system_restricted_prop
porque as propriedades do sistema devem ser lidas pelos processos do fornecedor. Se o acesso de leitura não for exigido por todos os processos do fornecedor e for exigido apenas por um determinado conjunto de processos (como vendor_init
), proíba os processos do fornecedor que não precisam do acesso de leitura.
Use a seguinte sintaxe para restringir o acesso de gravação e leitura:
Para restringir o acesso de gravação:
neverallow [domain] [context]:property_service set;
Para restringir o acesso de leitura:
neverallow [domain] [context]:file no_rw_file_perms;
Coloque as regras neverallow no arquivo system/sepolicy/private/{domain}.te
se a regra neverallow estiver vinculada a um domínio específico. Para regras nunca permitidas mais amplas, use domínios gerais como estes sempre que apropriado:
-
system/sepolicy/private/property.te
-
system/sepolicy/private/coredomain.te
-
system/sepolicy/private/domain.te
No arquivo system/sepolicy/private/audio.te
, coloque o seguinte:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
No arquivo system/sepolicy/private/property.te
, coloque o seguinte:
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
Observe que {domain -coredomain}
captura todos os processos do fornecedor. Portanto, {domain -coredomain -vendor_init}
significa “todos os processos do fornecedor , exceto vendor_init
”.
Finalmente, associe uma propriedade do sistema ao contexto da propriedade. Isso garante que o acesso concedido e as regras neverallow aplicadas aos contextos de propriedade sejam aplicadas às propriedades reais. Para fazer isso, adicione uma entrada ao arquivo property_contexts
, um arquivo que descreve o mapeamento entre propriedades do sistema e contextos de propriedades. Neste arquivo, você pode especificar uma única propriedade ou um prefixo para propriedades a serem mapeadas em um contexto.
Esta é a sintaxe para mapear uma única propriedade:
[property_name] u:object_r:[context_name]:s0 exact [type]
Esta é a sintaxe para mapear um prefixo:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
Opcionalmente, você pode especificar o tipo da propriedade, que pode ser um dos seguintes:
-
bool
-
int
-
uint
-
double
-
enum [list of possible values...]
-
string
(Usestring
para propriedades da lista.)
Certifique-se de que cada entrada tenha seu tipo designado sempre que possível, pois type
é aplicado ao definir property
. O exemplo a seguir mostra como escrever um mapeamento:
# 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 uma entrada exata e uma entrada de prefixo entram em conflito, a entrada exata tem precedência. Para obter mais exemplos, consulte system/sepolicy/private/property_contexts
.
Passo 4: Determinando os requisitos de estabilidade
A estabilidade é outro aspecto das propriedades do sistema e difere da acessibilidade. A estabilidade diz respeito à possibilidade ou não de uma propriedade do sistema ser alterada (por exemplo, renomeada ou mesmo removida) no futuro. Isto é particularmente importante à medida que o sistema operacional Android se torna modular. Com o Treble, as partições de sistema, fornecedor e produto podem ser atualizadas independentemente umas das outras. Com o Mainline, algumas partes do sistema operacional são modularizadas como módulos atualizáveis (em APEXes ou APKs).
Se uma propriedade do sistema for usada em softwares atualizáveis, por exemplo, em partições de sistema e de fornecedor, ela deverá ser estável. No entanto, se for usado apenas dentro, por exemplo, de um módulo Mainline específico, você poderá alterar seu nome, tipo ou contextos de propriedade e até mesmo removê-lo.
Faça as seguintes perguntas para determinar a estabilidade de uma propriedade do sistema:
- Esta propriedade do sistema deve ser configurada por parceiros (ou configurada de forma diferente por dispositivo)? Se sim, deve ser estável.
- Esta propriedade de sistema definida pelo AOSP destina-se a ser gravada ou lida a partir de código (não de processo) que existe em partições que não são do sistema, como
vendor.img
ouproduct.img
? Se sim, deve ser estável. - Esta propriedade do sistema é acessada através dos módulos Mainline ou através de um módulo Mainline e da parte não atualizável da plataforma? Se sim, deve ser estável.
Para as propriedades estáveis do sistema, defina formalmente cada uma como uma API e use a API para acessar a propriedade do sistema, conforme explicado na Etapa 6 .
Etapa 5: definir propriedades no momento da construção
Defina propriedades em tempo de construção com variáveis makefile. Tecnicamente, os valores são inseridos em {partition}/build.prop
. Então init
lê {partition}/build.prop
para definir as propriedades. Existem dois conjuntos dessas variáveis: PRODUCT_{PARTITION}_PROPERTIES
e TARGET_{PARTITION}_PROP
.
PRODUCT_{PARTITION}_PROPERTIES
contém uma lista de valores de propriedade. A sintaxe é {prop}={value}
ou {prop}?={value}
.
{prop}={value}
é uma atribuição normal que garante que {prop}
seja definido como {value}
; apenas uma dessas atribuições é possível por uma única propriedade.
{prop}?={value}
é uma atribuição opcional; {prop}
é definido como {value}
somente se não houver nenhuma atribuição {prop}={value}
. Se existirem várias atribuições opcionais, a primeira vence.
# 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
contém uma lista de arquivos, que é emitida diretamente para {partition}/build.prop
. Cada arquivo contém uma lista de pares {prop}={value}
.
# example.prop
ro.cp_system_other_odex=0
ro.adb.secure=0
ro.control_privapp_permissions=disable
# emits example.prop to system/build.prop
TARGET_SYSTEM_PROP += example.prop
Para obter mais detalhes, consulte build/make/core/sysprop.mk
.
Etapa 6: acessar as propriedades em tempo de execução
Claro, as propriedades podem ser lidas e escritas em tempo de execução.
Scripts de inicialização
Arquivos de script de inicialização (geralmente arquivos *.rc) podem ler uma propriedade por ${prop}
ou ${prop:-default}
, podem definir uma ação que é executada sempre que uma propriedade se torna um valor específico e podem escrever as propriedades usando o 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}
Comandos shell getprop e setprop
Você pode usar os comandos shell getprop
ou setprop
, respectivamente, para ler ou gravar as propriedades. Para obter mais detalhes, invoque getprop --help
ou setprop --help
.
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop como API para C++/Java/Rust
Com sysprop como API, você pode definir propriedades do sistema e usar API gerada automaticamente que são concretas e digitadas. Definir scope
com Public
também disponibiliza APIs geradas para módulos além dos limites e garante a estabilidade da API. Aqui está um exemplo de um arquivo .sysprop
, um módulo Android.bp
e código C++, Java e Rust usando-os.
# AudioProps.sysprop
# module becomes static class (Java) / namespace (C++) for serving API
module: "android.sysprop.AudioProps"
# owner can be Platform or Vendor or Odm
owner: Platform
# one prop defines one property
prop {
prop_name: "ro.audio.volume.level"
type: Integer
scope: Public
access: ReadWrite
api_name: "volume_level"
}
…
// Android.bp
sysprop_library {
name: "AudioProps",
srcs: ["android/sysprop/AudioProps.sysprop"],
property_owner: "Platform",
}
// Rust, Java and C++ modules can link against the sysprop_library
rust_binary {
rustlibs: ["libaudioprops_rust"],
…
}
java_library {
static_libs: ["AudioProps"],
…
}
cc_binary {
static_libs: ["libAudioProps"],
…
}
// Rust code accessing generated API.
// Get volume. Use 50 as the default value.
let vol = audioprops::volume_level()?.unwrap_or_else(50);
// Java codes accessing generated API
// get volume. use 50 as the default value.
int vol = android.sysprop.AudioProps.volume_level().orElse(50);
// add 10 to the volume level.
android.sysprop.AudioProps.volume_level(vol + 10);
// C++ codes accessing generated API
// get volume. use 50 as the default value.
int vol = android::sysprop::AudioProps::volume_level().value_or(50);
// add 10 to the volume level.
android::sysprop::AudioProps::volume_level(vol + 10);
Para obter mais informações, consulte Implementando propriedades do sistema como APIs .
Funções e métodos de propriedade de baixo nível C/C++, Java e Rust
Quando possível, use Sysprop como API, mesmo que funções C/C++ ou Rust de baixo nível ou métodos Java de baixo nível estejam disponíveis para você.
libc
, libbase
e libcutils
oferecem funções de propriedade do sistema C++. libc
possui a API subjacente, enquanto as funções libbase
e libcutils
são wrappers. Se for possível, use as funções sysprop libbase
; eles são os mais convenientes e os binários do host podem usar as funções libbase
. Para obter mais detalhes, consulte sys/system_properties.h
( libc
), android-base/properties.h
( libbase
) e cutils/properties.h
( libcutils
).
A classe android.os.SystemProperties
oferece métodos de propriedade do sistema Java.
O módulo rustutils::system_properties
oferece funções e tipos de propriedades do sistema Rust.
Apêndice: Incluindo propriedades específicas do fornecedor
Os parceiros (incluindo Googlers que trabalham no contexto do desenvolvimento do Pixel) desejam definir propriedades do sistema específicas do hardware (ou do dispositivo). Propriedades específicas do fornecedor são propriedades de propriedade de parceiros exclusivas para seu próprio hardware ou dispositivo, não para a plataforma. Como dependem de hardware ou dispositivo, devem ser usados nas partições /vendor
ou /odm
.
Desde o Projeto Treble, as propriedades da plataforma e do fornecedor foram completamente divididas para evitar conflitos. A seguir descrevemos como definir propriedades do fornecedor e informamos quais propriedades do fornecedor sempre devem ser usadas.
Namespace em nomes de propriedades e contextos
Todas as propriedades do fornecedor devem começar com um dos prefixos a seguir para evitar colisão entre elas e as propriedades de outras partições.
-
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.
Observe que ro.hardware.
é permitido como prefixo, mas apenas para compatibilidade. Não o use para propriedades normais.
Todos os exemplos a seguir usam um dos prefixos listados acima:
-
vendor.display.primary_red
-
persist.vendor.faceauth.use_disk_cache
-
ro.odm.hardware.platform
Todos os contextos de propriedade do fornecedor devem começar com vendor_
. Isso também é para compatibilidade. A seguir estão exemplos:
-
vendor_radio_prop
. -
vendor_faceauth_prop
. -
vendor_usb_prop
.
É responsabilidade do fornecedor nomear e manter as propriedades, portanto siga o formato sugerido na Etapa 2 , além dos requisitos de namespaces do fornecedor.
Regras SEPolicy e property_contexts específicos do fornecedor
As propriedades do fornecedor podem ser definidas pela macro vendor_internal_prop
. Coloque as regras específicas do fornecedor definidas no diretório BOARD_VENDOR_SEPOLICY_DIRS
. Por exemplo, suponha que você esteja definindo uma propriedade faceauth do fornecedor em coral.
No arquivo BoardConfig.mk
(ou em qualquer inclusão BoardConfig.mk
), coloque o seguinte:
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
No arquivo device/google/coral-sepolicy/private/property.te
, coloque o seguinte:
vendor_internal_prop(vendor_faceauth_prop)
No arquivo device/google/coral-sepolicy/private/property_contexts
, coloque o seguinte:
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
Limitações das propriedades do fornecedor
Como as partições do sistema e do produto não podem depender do fornecedor, nunca permita que as propriedades do fornecedor sejam acessadas a partir das partições system
, system-ext
ou product
.
Apêndice: Renomeando propriedades existentes
Quando você precisar descontinuar uma propriedade e mudar para uma nova, use Sysprop como APIs para renomear suas propriedades existentes. Isso mantém a compatibilidade com versões anteriores especificando o nome herdado e o novo nome da propriedade. Especificamente, você pode definir o nome legado pelo campo legacy_prop_name
no arquivo .sysprop
. A API gerada tenta ler prop_name
e usa legacy_prop_name
se prop_name
não existir.
Por exemplo, as etapas a seguir renomeiam awesome_feature_foo_enabled
para foo.awesome_feature.enabled
.
No arquivo 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"
}
Em código C++
// is_awesome_feature_enabled() reads "foo.awesome_feature.enabled".
// If it doesn't exist, reads "awesome_feature_foo_enabled" instead
using android::sysprop::foo;
bool enabled = foo::is_awesome_feature_enabled().value_or(false);
Observe as seguintes advertências:
Primeiro, você não pode alterar o tipo do sysprop. Por exemplo, você não pode transformar um suporte
int
em um suportestring
. Você só pode alterar o nome.Em segundo lugar, apenas a API de leitura retorna ao nome legado. A API de gravação não faz fallback. Se o sysprop for gravável, você não poderá renomeá-lo.