Esta página fornece um método canônico para adicionar ou definir propriedades do sistema no Android, com diretrizes para refatorar propriedades do sistema atuais. Use as diretrizes ao refatorar, a menos que você tenha um problema de compatibilidade grave que exija outra abordagem.
Etapa 1: definir a propriedade do sistema
Ao adicionar uma propriedade do sistema, decida um nome para ela e associe-a a um contexto de propriedade do SELinux. Se não houver um contexto adequado, crie um novo. O nome é usado ao acessar a propriedade, e o contexto da propriedade é usado para controlar a acessibilidade em termos de SELinux. Os nomes podem ser qualquer string, mas o AOSP recomenda que você siga um formato estruturado para facilitar a compreensão.
Nome da propriedade
Use este formato com letras minúsculas e sublinhado:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
Use "" (omitido), ro
(para propriedades definidas apenas uma vez) ou persist
(para propriedades que persistem em reinicializações) para o elemento prefix
.
Avisos
Use ro
somente quando tiver certeza de que não precisará que prefix
seja gravável
no futuro. ** Não especifique o prefixo ro
.** Em vez disso, confie na sepolicy para
tornar prefix
somente leitura (ou seja, gravável apenas por init
).
Use persist
somente quando tiver certeza de que o valor precisa ser mantido em
reinicializações e que usar as propriedades do sistema é sua única opção.
O Google analisa rigorosamente as propriedades do sistema que têm propriedades ro
ou persist
.
O termo group
é usado para agregar propriedades relacionadas. Ele foi criado para 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
.
É comum usar o nome do tipo de domínio de um processo que tem acesso exclusivo de leitura ou gravação às propriedades do sistema. Por exemplo, para as propriedades do sistema a que o processo vold
tem acesso de gravação, é comum usar vold
(o nome do tipo de domínio do processo) como o nome do grupo.
Se necessário, adicione subgroup
para categorizar ainda mais as propriedades, mas evite termos ambíguos ou sobrecarregados para descrever esse elemento. Também é possível ter mais de um subgroup
.
Muitos nomes de grupos já foram definidos. Verifique o arquivo
system/sepolicy/private/property_contexts
e use os nomes de grupos atuais
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 da linha de comando do kernel | boot |
sysprops que identificam um build | build
|
relacionado à telefonia | telephony |
relacionado a áudio | audio |
relacionados a gráficos | graphics |
vold related | vold |
A seguir, definimos o uso de name
e type
no exemplo de regex anterior.
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
identifica uma propriedade do sistema em um grupo.type
é um elemento opcional que esclarece o tipo ou a finalidade da propriedade do sistema. Por exemplo, em vez de nomear uma sysprop comoaudio.awesome_feature_enabled
ou apenasaudio.awesome_feature
, renomeie comoaudio.awesome_feature.enabled
para refletir o tipo e a finalidade da propriedade do sistema.
Não há 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 for esclarecer que a propriedade do sistema não representa um estado dinâmico do sistema, mas 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 propriedade do SELinux permite uma 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 maneira:
group
e subgroup
têm o mesmo significado definido para a expressão regular de exemplo anterior. 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 expõem o status atual de vold
.
Ao nomear um contexto de propriedade, escolha nomes que reflitam o uso geral das propriedades. Em especial, evite os seguintes tipos de termos:
- Termos muito gerais e ambíguos, como
sys
,system
edefault
. - Termos que codificam diretamente a acessibilidade, como
exported
,apponly
,ro
,public
eprivate
.
Prefira usos de nomes como vold_config_prop
em vez de exported_vold_prop
ou vold_vendor_writable_prop
.
Tipo
Um tipo de propriedade pode ser um dos seguintes, conforme listado na tabela.
Tipo | Definição |
---|---|
Booleano | true ou 1 para verdadeiro, false ou 0 para falso |
Número inteiro | inteiro assinado de 64 bits |
Número inteiro sem assinatura | inteiro de 64 bits sem sinal |
Duplo | ponto flutuante de precisão dupla |
String | qualquer string UTF-8 válida |
enum | Os valores podem ser qualquer string UTF-8 válida sem espaços em branco. |
Lista dos itens acima | Uma vírgula (, ) é usada como delimitadorA lista de números inteiros [1, 2, 3] é armazenada como 1,2,3 |
Internamente, todas as propriedades são armazenadas como strings. É possível aplicar o tipo especificando-o como um arquivo property_contexts
. Para mais informações, consulte
property_contexts
na Etapa 3.
Etapa 2: determinar os níveis de acessibilidade necessários
Há quatro macros auxiliares que definem uma propriedade.
Tipo de acessibilidade | Significado |
---|---|
system_internal_prop |
Propriedades usadas apenas em /system |
system_restricted_prop |
Propriedades lidas fora de /system , mas não gravadas |
system_vendor_config_prop |
Propriedades lidas fora de /system e gravadas apenas por vendor_init |
system_public_prop |
Propriedades lidas e gravadas fora de /system |
Restrinja o acesso às propriedades do sistema o máximo possível. No passado, o acesso amplo resultou em falhas de apps e vulnerabilidades de segurança. Considere as seguintes perguntas ao definir o escopo:
- Essa propriedade do sistema precisa ser persistida? (se sim, por quê?)
- Qual processo precisa ter acesso de leitura a essa propriedade?
- Qual processo deve ter acesso de gravação a essa propriedade?
Use as perguntas anteriores e a árvore de decisão a seguir como ferramentas para determinar um escopo de acesso adequado.
Figura 1. Árvore de decisão para determinar o escopo do acesso às propriedades do sistema
Etapa 3: adicionar a system/sepolicy
Ao acessar sysprop, o SELinux controla a acessibilidade dos processos. Depois de determinar o nível de acessibilidade necessário, defina contextos de propriedade em system/sepolicy
, além de outras regras allow e neverallow sobre o que os processos podem (ou não) 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 fornecem a
acessibilidade necessária da propriedade do sistema. Este é um exemplo do arquivo
system/sepolicy/public/property.te
:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
Exemplo para adicionar ao arquivo system/sepolicy/private/property.te
:
system_internal_prop(audio_baz_prop)
Em segundo lugar, 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
só é adequado se a macro set_prop
ou get_prop
afetar domínios fora do domínio principal.
Por exemplo, no arquivo system/sepolicy/private/audio.te
:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
Por 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
definida pela macro. Por exemplo, suponha que você tenha usado
system_restricted_prop
porque as propriedades do sistema precisam ser lidas por processos
do fornecedor. Se o acesso de leitura não for necessário para todos os processos do fornecedor, e for necessário apenas para 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 leitura e gravação:
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 regras neverallow no arquivo system/sepolicy/private/{domain}.te
se a regra estiver vinculada a um domínio específico. Para regras "neverallow" 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;
{domain -coredomain}
captura todos os processos do fornecedor. Portanto, {domain -coredomain -vendor_init}
significa "todos os processos do fornecedor exceto
vendor_init
".
Por fim, 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 aplicados às propriedades reais. Para isso, adicione uma entrada ao arquivo property_contexts
, que descreve o mapeamento entre propriedades do sistema e contextos de propriedade. Nesse arquivo, é possível 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]
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 de lista).
Verifique se cada entrada tem o tipo designado sempre que possível, já que type
é
aplicado ao definir property
. O exemplo a seguir mostra como gravar 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 de prefixo entram em conflito, a entrada exata tem precedência. Para mais exemplos, consulte system/sepolicy/private/property_contexts
.
Etapa 4: determinar os requisitos de estabilidade
A estabilidade é outro aspecto das propriedades do sistema e difere da acessibilidade. A estabilidade se refere à possibilidade de uma propriedade do sistema ser alterada (por exemplo, renomeada ou até mesmo removida) no futuro. Isso é importante principalmente porque o SO Android está se tornando modular. Com o Treble, as partições de sistema, fornecedor e produto podem ser atualizadas de forma independente. Com o Mainline, algumas partes do SO são modularizadas como módulos atualizáveis (em APEXes ou APKs).
Se uma propriedade do sistema for usada em partes atualizáveis de software, por exemplo, em partições do sistema e do fornecedor, ela precisará ser estável. No entanto, se ele for usado apenas em um módulo Mainline específico, por exemplo, você poderá mudar o nome, o tipo ou os contextos de propriedade e até mesmo remover o elemento.
Faça as seguintes perguntas para determinar a estabilidade de uma propriedade do sistema:
- Essa propriedade do sistema foi criada para ser configurada por parceiros (ou configurada de maneira diferente por dispositivo)? Se sim, ele precisa ser estável.
- Essa propriedade do sistema definida pelo AOSP foi criada para ser gravada ou lida por
código (não processo) que existe em partições não relacionadas ao sistema, como
vendor.img
ouproduct.img
? Se sim, ele precisa ser estável. - Essa propriedade do sistema é acessada em módulos Mainline ou em um módulo Mainline e na parte não atualizável da plataforma? Se sim, ele precisa 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 tempo de build
Defina propriedades no momento da build com variáveis do makefile. Tecnicamente, os valores são incorporados a {partition}/build.prop
. Em seguida, init
lê
{partition}/build.prop
para definir as propriedades. Há dois conjuntos de 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}
. Só é possível uma atribuição desse tipo por propriedade.
{prop}?={value}
é uma atribuição opcional. {prop}
é definido como {value}
somente se não houver atribuições de {prop}={value}
. Se houver várias atribuições opcionais, a primeira vai ganhar.
# 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 mais detalhes, consulte
build/make/core/sysprop.mk
.
Etapa 6: acessar propriedades no ambiente de execução
As propriedades podem ser lidas e gravadas no tempo de execução.
Scripts de inicialização
Os arquivos de script de inicialização (geralmente arquivos *.rc) podem ler uma propriedade por ${prop}
ou
${prop:-default}
, definir uma ação que é executada sempre que uma propriedade assume um
valor específico e gravar as propriedades usando o 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}
Comandos shell getprop e setprop
Use os comandos do shell getprop
ou setprop
, respectivamente, para ler ou
gravar as propriedades. Para 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, é possível definir propriedades do sistema e usar APIs
geradas automaticamente, que são concretas e tipadas. Definir scope
com Public
também disponibiliza APIs geradas para módulos em todos os limites e garante a estabilidade da API. Confira um exemplo de arquivo .sysprop
, um módulo Android.bp
e código em C++, Java e Rust usando esses elementos.
# 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 mais informações, consulte Implementar propriedades do sistema como APIs.
Funções e métodos de propriedade de baixo nível em C/C++, Java e Rust
Sempre que possível, use Sysprop como API, mesmo que funções de baixo nível em C/C++ ou Rust ou métodos Java de baixo nível estejam disponíveis.
libc
, libbase
e libcutils
oferecem funções de propriedade do sistema C++. libc
tem a API subjacente, enquanto as funções libbase
e libcutils
são wrappers. Se possível, use as funções sysprop libbase
. Elas são as mais convenientes, e os binários do host podem usar as funções libbase
. Para 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: adicionar propriedades específicas do fornecedor
Os parceiros (incluindo Googlers que trabalham no contexto do desenvolvimento do Pixel) querem
definir propriedades do sistema específicas do hardware (ou do dispositivo).
As propriedades específicas do fornecedor são de propriedade do parceiro e exclusivas do hardware ou dispositivo dele, não da plataforma. Como eles dependem de hardware ou dispositivo, devem ser usados nas partições /vendor
ou /odm
.
Desde o Project Treble, as propriedades da plataforma e do fornecedor foram completamente divididas para evitar conflitos. A seguir, descrevemos como definir propriedades do fornecedor e quais delas precisam ser usadas sempre.
Namespace em nomes de propriedades e contextos
Todas as propriedades do fornecedor precisam começar com um dos seguintes prefixos para evitar conflitos 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.
ro.hardware.
é permitido como prefixo, mas apenas para compatibilidade.
Não use para propriedades normais.
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 precisam começar com vendor_
. Isso também é para compatibilidade. Confira alguns 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 que você definir no diretório BOARD_VENDOR_SEPOLICY_DIRS
.
Por exemplo, suponha que você esteja definindo uma propriedade de autenticação facial do fornecedor em coral.
No arquivo BoardConfig.mk
(ou em qualquer inclusão de 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 das partições system
, system-ext
ou product
.
Apêndice: renomear propriedades atuais
Quando for necessário descontinuar uma propriedade e migrar para uma nova, use Sysprop como APIs
para renomear as propriedades atuais. Isso mantém a compatibilidade com versões anteriores especificando o nome legado 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"
}
No 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);
Observações:
Primeiro, não é possível mudar o tipo da sysprop. Por exemplo, não é possível transformar uma propriedade
int
em uma propriedadestring
. Só é possível mudar o nome.Segundo, apenas a API de leitura volta ao nome legado. A API de gravação não faz fallback. Se a sysprop for gravável, não será possível renomeá-la.