Usando o Binder IPC

Esta página descreve as alterações no driver do binder no Android 8, fornece detalhes sobre o uso do IPC do binder e lista a política SELinux necessária.

Mudanças no driver do fichário

A partir do Android 8, a estrutura do Android e os HALs agora se comunicam entre si usando o binder. Como essa comunicação aumenta drasticamente o tráfego do fichário, o Android 8 inclui diversas melhorias projetadas para manter o IPC do fichário rápido. Os fornecedores de SoC e OEMs devem se fundir diretamente dos ramos relevantes do Android-4.4, Android-4.9 e superior do projeto kernel/common .

Vários domínios de fichário (contextos)

Common-4.4 e superior, incluindo upstream

Para dividir claramente o tráfego do fichário entre o código da estrutura (independente do dispositivo) e do fornecedor (específico do dispositivo), o Android 8 introduziu o conceito de contexto do fichário . Cada contexto do binder possui seu próprio nó de dispositivo e seu próprio gerenciador de contexto (serviço). Você pode acessar o gerenciador de contexto apenas através do nó do dispositivo ao qual ele pertence e, ao passar um nó binder por um determinado contexto, ele fica acessível a partir desse mesmo contexto apenas por outro processo, isolando completamente os domínios entre si. Para obter detalhes sobre como usar, consulte vndbinder e vndservicemanager .

Coleta de dispersão

Common-4.4 e superior, incluindo upstream

Nas versões anteriores do Android, cada dado em uma chamada de binder era copiado três vezes:

  • Uma vez para serializá-lo em um Parcel no processo de chamada
  • Uma vez no driver do kernel para copiar o Parcel para o processo de destino
  • Uma vez para desserializar o Parcel no processo de destino

O Android 8 usa otimização de coleta de dispersão para reduzir o número de cópias de 3 para 1. Em vez de serializar os dados em um Parcel primeiro, os dados permanecem em sua estrutura e layout de memória originais e o driver os copia imediatamente para o processo de destino. Depois que os dados estão no processo de destino, a estrutura e o layout da memória são os mesmos e os dados podem ser lidos sem a necessidade de outra cópia.

Bloqueio refinado

Common-4.4 e superior, incluindo upstream

Nas versões anteriores do Android, o driver binder usava um bloqueio global para proteger contra acesso simultâneo a estruturas de dados críticas. Embora houvesse uma disputa mínima pelo bloqueio, o principal problema era que, se um thread de baixa prioridade obtivesse o bloqueio e fosse interrompido, isso poderia atrasar seriamente os threads de prioridade mais alta que precisavam obter o mesmo bloqueio. Isso causou instabilidade na plataforma.

As tentativas iniciais de resolver esse problema envolveram a desativação da preempção enquanto mantinha o bloqueio global. No entanto, isso foi mais um hack do que uma solução verdadeira e acabou sendo rejeitado pelo upstream e descartado. As tentativas subsequentes se concentraram em tornar o bloqueio mais refinado, uma versão que está em execução em dispositivos Pixel desde janeiro de 2017. Embora a maioria dessas alterações tenha sido tornada pública, melhorias substanciais foram feitas nas versões subsequentes.

Depois de identificar pequenos problemas na implementação detalhada do bloqueio, desenvolvemos uma solução aprimorada com uma arquitetura de bloqueio diferente e enviamos as alterações em todas as ramificações comuns do kernel. Continuamos testando esta implementação em um grande número de dispositivos diferentes; como não temos conhecimento de quaisquer problemas pendentes, esta é a implementação recomendada para dispositivos fornecidos com Android 8.

Herança de prioridade em tempo real

Common-4.4 e common-4.9 (upstream em breve)

O driver binder sempre suportou uma boa herança de prioridade. Como um número crescente de processos no Android são executados com prioridade em tempo real, em alguns casos agora faz sentido que, se um thread em tempo real fizer uma chamada de binder, o thread no processo que trata dessa chamada também seja executado com prioridade em tempo real. . Para dar suporte a esses casos de uso, o Android 8 agora implementa herança de prioridade em tempo real no driver binder.

Além da herança de prioridade no nível da transação, a herança de prioridade do nó permite que um nó (objeto de serviço binder) especifique uma prioridade mínima na qual as chamadas para esse nó devem ser executadas. Versões anteriores do Android já suportavam herança de prioridade de nó com bons valores, mas o Android 8 adiciona suporte para herança de nó de políticas de agendamento em tempo real.

Mudanças no espaço do usuário

O Android 8 inclui todas as alterações de espaço do usuário necessárias para trabalhar com o driver binder atual no kernel comum, com uma exceção: a implementação original para desativar a herança de prioridade em tempo real para /dev/binder usava um ioctl . O desenvolvimento subsequente mudou o controle da herança de prioridade para um método mais refinado que é por modo de fichário (e não por contexto). Assim, o ioctl não está no branch comum do Android e, em vez disso, é submetido em nossos kernels comuns .

O efeito dessa alteração é que a herança de prioridade em tempo real é desabilitada por padrão para cada nó. A equipe de desempenho do Android achou benéfico habilitar a herança de prioridade em tempo real para todos os nós no domínio hwbinder . Para obter o mesmo efeito, escolha essa mudança no espaço do usuário.

SHAs para kernels comuns

Para obter as alterações necessárias no driver do fichário, sincronize com o SHA apropriado:

  • Comum-3.18
    cc8b90c121de ANDROID: fichário: não verifique as permissões prio na restauração.
  • Comum-4.4
    76b376eac7a2 ANDROID: fichário: não verifique as permissões prio na restauração.
  • Comum-4.9
    ecd972d4f9b5 ANDROID: fichário: não verifique as permissões prio na restauração.

Usando o fichário IPC

Historicamente, os processos do fornecedor usaram a comunicação interprocessual (IPC) do binder para se comunicar. No Android 8, o nó do dispositivo /dev/binder torna-se exclusivo para processos de estrutura, o que significa que os processos do fornecedor não têm mais acesso a ele. Os processos do fornecedor podem acessar /dev/hwbinder , mas devem converter suas interfaces AIDL para usar HIDL. Para fornecedores que desejam continuar usando interfaces AIDL entre processos de fornecedores, o Android oferece suporte ao binder IPC conforme descrito abaixo. No Android 10, Stable AIDL permite que todos os processos usem /dev/binder ao mesmo tempo que resolve as garantias de estabilidade HIDL e /dev/hwbinder resolvidas. Para saber como usar AIDL estável, consulte AIDL para HALs .

vndbinder

O Android 8 oferece suporte a um novo domínio de fichário para uso por serviços de fornecedores, acessado usando /dev/vndbinder em vez de /dev/binder . Com a adição de /dev/vndbinder , o Android agora tem os três domínios IPC a seguir:

Domínio IPC Descrição
/dev/binder IPC entre processos de estrutura/aplicativo com interfaces AIDL
/dev/hwbinder IPC entre processos de estrutura/fornecedor com interfaces HIDL
IPC entre processos de fornecedores com interfaces HIDL
/dev/vndbinder IPC entre processos de fornecedor/fornecedor com interfaces AIDL

Para que /dev/vndbinder apareça, certifique-se de que o item de configuração do kernel CONFIG_ANDROID_BINDER_DEVICES esteja definido como "binder,hwbinder,vndbinder" (este é o padrão nas árvores de kernel comuns do Android).

Normalmente, os processos do fornecedor não abrem o driver do fichário diretamente e, em vez disso, vinculam-se à biblioteca do espaço do usuário libbinder , que abre o driver do fichário. Adicionar um método para ::android::ProcessState() seleciona o driver do fichário para libbinder . Os processos do fornecedor devem chamar esse método antes de chamar ProcessState, IPCThreadState ou antes de fazer qualquer chamada de fichário em geral. Para usar, faça a seguinte chamada após main() de um processo de fornecedor (cliente e servidor):

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

Anteriormente, os serviços de binder eram registrados no servicemanager , onde podiam ser recuperados por outros processos. No Android 8, servicemanager agora é usado exclusivamente pelos processos da estrutura e do aplicativo, e os processos do fornecedor não podem mais acessá-lo.

No entanto, os serviços do fornecedor agora podem usar vndservicemanager , uma nova instância de servicemanager que usa /dev/vndbinder em vez de /dev/binder e que é construída a partir das mesmas fontes que a estrutura servicemanager . Os processos do fornecedor não precisam fazer alterações para conversar com vndservicemanager ; quando um processo de fornecedor abre / dev/vndbinder , as pesquisas de serviço vão automaticamente para vndservicemanager .

O binário vndservicemanager está incluído nos makefiles de dispositivos padrão do Android.

Política SELinux

Os processos do fornecedor que desejam usar a funcionalidade do binder para se comunicarem entre si precisam do seguinte:

  1. Acesso a /dev/vndbinder .
  2. Binder {transfer, call} conecta-se a vndservicemanager .
  3. binder_call(A, B) para qualquer domínio do fornecedor A que queira chamar o domínio do fornecedor B pela interface do fichário do fornecedor.
  4. Permissão para {add, find} serviços em vndservicemanager .

Para cumprir os requisitos 1 e 2, use a macro vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

Para atender ao requisito 3, o binder_call(A, B) dos processos do fornecedor A e B que precisam se comunicar por meio do fichário pode permanecer no local e não precisa ser renomeado.

Para atender ao requisito 4, você deve fazer alterações na forma como os nomes dos serviços, os rótulos dos serviços e as regras são tratados.

Para obter detalhes sobre o SELinux, consulte Linux com segurança aprimorada no Android . Para obter detalhes sobre o SELinux no Android 8.0, consulte SELinux para Android 8.0 .

Nomes de serviço

Anteriormente, o fornecedor processava nomes de serviços registrados em um arquivo service_contexts e adicionava regras correspondentes para acessar esse arquivo. Exemplo de arquivo service_contexts de device/google/marlin/sepolicy :

AtCmdFwd                              u:object_r:atfwd_service:s0
cneservice                            u:object_r:cne_service:s0
qti.ims.connectionmanagerservice      u:object_r:imscm_service:s0
rcs                                   u:object_r:radio_service:s0
uce                                   u:object_r:uce_service:s0
vendor.qcom.PeripheralManager         u:object_r:per_mgr_service:s0

No Android 8, vndservicemanager carrega o arquivo vndservice_contexts . Os serviços do fornecedor que estão migrando para vndservicemanager (e que já estão no antigo arquivo service_contexts ) devem ser adicionados ao novo arquivo vndservice_contexts .

Etiquetas de serviço

Anteriormente, rótulos de serviço como u:object_r:atfwd_service:s0 eram definidos em um arquivo service.te . Exemplo:

type atfwd_service,      service_manager_type;

No Android 8, você deve alterar o tipo para vndservice_manager_type e mover a regra para o arquivo vndservice.te . Exemplo:

type atfwd_service,      vndservice_manager_type;

Regras do gerenciador de serviços

Anteriormente, as regras concediam aos domínios acesso para adicionar ou localizar serviços do servicemanager . Exemplo:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

No Android 8, essas regras podem permanecer em vigor e usar a mesma classe. Exemplo:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;