Adicionar propriedades do sistema

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 existentes. Use as diretrizes ao refatorar, a menos que você tenha um grande problema de compatibilidade que determine o contrário.

Etapa 1: definir a propriedade do sistema

Ao adicionar uma propriedade do sistema, escolha um nome para a propriedade e associe-a a um contexto de propriedade do SELinux. Se não houver contexto apropriado, crie um novo. O nome é usado ao acessar a propriedade. 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 torná-los claros.

Nome da propriedade

Use este formato com capitalização snake_case:

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

Use "" (omitido), ro (para propriedades definidas apenas uma vez) ou persist (para propriedades que persistem após reinicializações) para o elemento prefix.

Avisos

Use ro somente quando tiver certeza de que não precisa que prefix seja gravável 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 precisa ser mantido nas reinicializações e de que o uso das propriedades do sistema é sua única opção.

O Google analisa estritamente as propriedades do sistema que têm propriedades ro ou persist.

O termo group é usado para agregar propriedades relacionadas. O objetivo é 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.

É uma prática comum usar o nome do tipo de domínio de um processo que tenha 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 grupo já foram definidos. Verifique o arquivo system/sepolicy/private/property_contexts e use 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 do cmdline do kernel boot
sysprop que identificam um build build
serviços de telefonia telephony
relacionado a áudio audio
relacionados a gráficos graphics
relacionado ao vold vold

Veja a seguir a definição do uso de name e type no exemplo anterior de regex.

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

  • name identifica uma propriedade do sistema dentro de um grupo.

  • type é um elemento opcional que esclarece o tipo ou a intent da propriedade do sistema. Por exemplo, em vez de nomear um sysprop como audio.awesome_feature_enabled ou apenas audio.awesome_feature, renomeie-o como audio.awesome_feature.enabled para refletir o tipo de propriedade e a intent do sistema.

Não há regra específica sobre qual precisa ser o tipo. Estas são as 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 intent for esclarecer que a propriedade do sistema não representa um estado dinâmico do sistema. Ela representa um valor pré-configurado (por exemplo, um item somente leitura).
  • List: use se for uma propriedade do sistema com o valor de 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. Assim como é 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 o exemplo de regex anterior. Por exemplo, vold_config_prop significa propriedades que são configurações de um fornecedor e precisam ser definidas por vendor_init, enquanto vold_status_prop ou apenas vold_prop significa propriedades que vão expor o status atual de vold.

Ao nomear um contexto de propriedade, escolha nomes que reflitam o uso geral delas. Especificamente, evite os seguintes tipos de termos:

  • Termos que parecem muito gerais e ambíguos, como sys, system e default.
  • Termos que codificam diretamente a acessibilidade: como exported, apponly, ro, public, private.

Prefira usos de nome como vold_config_prop a exported_vold_prop ou vold_vendor_writable_prop.

Tipo

O 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 número inteiro de 64 bits com sinal
Número inteiro sem assinatura inteiro de 64 bits não assinado
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 acima Uma vírgula (,) é usada como o delimitador
A lista de números inteiros [1, 2, 3] é armazenada como 1,2,3

Internamente, todas as propriedades são armazenadas como strings. Você pode 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 somente em /system
system_restricted_prop Propriedades que são lidas fora de /system, mas não gravadas
system_vendor_config_prop Propriedades que são lidas fora de /system e gravadas apenas pelo vendor_init
system_public_prop Propriedades que são lidas e gravadas fora de /system

Definir o acesso às propriedades do sistema da forma mais restrita possível. No passado, o acesso amplo resultou em falhas de aplicativos e vulnerabilidades de segurança. Considere as seguintes perguntas ao definir o escopo:

  • Essa propriedade do sistema precisa ser mantida? (Se sim, por quê?)
  • Qual processo precisa ter acesso de leitura a essa propriedade?
  • Qual processo precisa 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 apropriado para o acesso.

Árvore de decisão para determinar o escopo do acesso

Figura 1. Árvore de decisão para determinar o escopo de acesso às propriedades do sistema

Etapa 3: adicionar ao system/sepolicy

Ao acessar o sysprop, o SELinux controla a acessibilidade dos processos. Depois de determinar qual nível de acessibilidade é necessário, defina contextos de propriedade em system/sepolicy, além de outras regras allow e Neverallow sobre quais 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 para a propriedade do 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 ao arquivo system/sepolicy/private/property.te:

system_internal_prop(audio_baz_prop)

Depois 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 somente se a macro set_prop ou get_prop afetar qualquer domínio 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)

Em terceiro lugar, adicione algumas regras de nunca permitir 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 pelos processos do fornecedor. Se o acesso de leitura não for exigido por todos os processos do fornecedor e só for exigido 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 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 de nunca permitir no arquivo system/sepolicy/private/{domain}.te se ela estiver vinculada a um domínio específico. Para regras de nunca permitir 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".

Por fim, associe uma propriedade do sistema ao contexto da propriedade. Isso garante que o acesso concedido e as regras de bloqueio aplicadas aos contextos da propriedade sejam aplicados à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 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]

Também é possível especificar o tipo de propriedade, que pode ser um dos seguintes:

  • bool
  • int
  • uint
  • double
  • enum [list of possible values...]
  • string Use string 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 abaixo 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 prioridade. Para mais exemplos, consulte system/sepolicy/private/property_contexts.

Etapa 4: determinar os requisitos de estabilidade

A estabilidade é outro aspecto das propriedades do sistema, diferente da acessibilidade. A estabilidade significa se uma propriedade do sistema pode ou não ser alterada (por exemplo, renomeada ou até mesmo removida) no futuro. Isso é especialmente importante à medida que o SO Android se torna modular. Com o Treble, as partições do sistema, do fornecedor e do produto podem ser atualizadas de forma independente. Com o Mainline, algumas partes do SO são modularizadas como módulos atualizáveis (em APEXs ou APKs).

Se uma propriedade do sistema for usada em softwares atualizáveis, por exemplo, em partições do sistema e do fornecedor, ela precisa 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é removê-lo.

Faça as seguintes perguntas para determinar a estabilidade de uma propriedade do sistema:

  • Essa propriedade do sistema deve ser configurada por parceiros ou configurada de maneira diferente por dispositivo? Em caso afirmativo, ele precisa ser estável.
  • Essa propriedade do sistema definida pelo AOSP precisa ser gravada ou lida com um código, e não um processo, que existe em partições que não são do sistema, como vendor.img ou product.img? Em caso afirmativo, ele precisa ser estável.
  • Essa propriedade do sistema é acessada nos módulos Mainline ou em um módulo Mainline na parte não atualizável da plataforma? Em caso afirmativo, ele precisa ser estável.

Para as propriedades do sistema estáveis, 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 tempo de build com variáveis do makefile. Tecnicamente, os valores são incorporados em {partition}/build.prop. Em seguida, init{partition}/build.prop para definir as propriedades. Há 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}. Somente uma dessas atribuições é possível por propriedade.

{prop}?={value} é uma atribuição opcional. {prop} vai definir como {value} somente se não houver atribuições {prop}={value}. Se houver várias atribuições opcionais, a primeira vencerá.

# 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 são emitidos diretamente para {partition}/build.prop. Cada arquivo contém uma lista de pares de {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 momento da execução.

Scripts de inicialização

Os arquivos de script init (geralmente arquivos *.rc) podem ler uma propriedade usando ${prop} ou ${prop:-default}, definir uma ação que será executada sempre que uma propriedade se tornar 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 do shell getprop e setprop

É possível usar 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 o sysprop como API, você pode definir as propriedades do sistema e usar a API gerada automaticamente, que é concreta e tipada. Definir scope com Public também disponibiliza as APIs geradas para os módulos além dos limites e garante a estabilidade da API. Confira um exemplo de um arquivo .sysprop, um módulo Android.bp e códigos C++, Java e Rust que os usam.

# 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 do C/C++, Java e Rust

Sempre que possível, use o Sysprop como uma API, mesmo que as funções C/C++ ou Rust de baixo nível ou métodos Java de baixo nível estejam disponíveis.

libc, libbase e libcutils oferecem funções de propriedade do sistema C++. libc contém 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 propriedade do sistema Rust.

Apêndice: adicionar propriedades específicas do fornecedor

Os parceiros, incluindo Googlers que trabalham no contexto de 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 próprio hardware ou dispositivo, não da plataforma. Como dependem de hardware ou dispositivo, elas precisam ser usadas nas partições /vendor ou /odm.

Desde o Projeto Treble, as propriedades da plataforma e do fornecedor foram completamente divididas para evitar conflitos. Confira a seguir como definir as propriedades do fornecedor e informa quais delas sempre precisam ser usadas.

Namespace em nomes de propriedades e contextos

Todas as propriedades do fornecedor precisam 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 precisam começar com vendor_. Isso também se aplica a 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 de SEPolicy específicas do fornecedor e propriedades_contextos

As propriedades do fornecedor podem ser definidas pela macro vendor_internal_prop. Coloque as regras específicas do fornecedor que você definiu no diretório BOARD_VENDOR_SEPOLICY_DIRS. Por exemplo, suponha que você esteja definindo uma propriedade faceauth de fornecedor no 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 pelas partições system, system-ext ou product.

Apêndice: renomear propriedades existentes

Quando precisar suspender o uso de uma propriedade e movê-la para uma nova, use Sysprop como APIs para renomear as propriedades existentes. Isso mantém a compatibilidade com versões anteriores especificando o nome legado e o novo nome da propriedade. Especificamente, é possível 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, nas etapas a seguir, awesome_feature_foo_enabled é renomeado como 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);

Observe as seguintes ressalvas:

  • Primeiro, não é possível alterar o tipo do sysprop. Por exemplo, não é possível transformar uma propriedade int em uma string. Só é possível mudar o nome.

  • Segundo, somente a API de leitura retorna ao nome legado. A API de gravação não corresponde a isso. Se o sysprop for gravável, não será possível renomeá-lo.