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.

Alterações no driver do fichário

A partir do Android 8, a estrutura do Android e as HALs agora se comunicam usando o binder. Como essa comunicação aumenta drasticamente o tráfego do binder, o Android 8 inclui várias melhorias projetadas para manter o IPC do binder rápido. Os fornecedores e OEMs de SoC devem se fundir diretamente das ramificações relevantes do android-4.4, android-4.9 e superior do projeto kernel/common .

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

Comum-4.4 e superior, incluindo upstream

Para dividir de forma clara o tráfego do binder entre o código do framework (independente do dispositivo) e do fornecedor (específico do dispositivo), o Android 8 introduziu o conceito de um binder context . Cada contexto de binder tem 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 é 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 .

Dispersar

Comum-4.4 e superior, incluindo upstream

Nas versões anteriores do Android, todos os dados em uma chamada do binder eram copiados 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 scatter-gather 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 original e layout de memória 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 exigir outra cópia.

Bloqueio de granulação fina

Comum-4.4 e superior, incluindo upstream

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

As tentativas iniciais de resolver esse problema envolviam desabilitar a 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á sendo executada em dispositivos Pixel desde janeiro de 2017. Embora a maioria dessas alterações tenha sido divulgada, melhorias substanciais foram feitas nas versões subsequentes.

Depois de identificar pequenos problemas na implementação de bloqueio refinada, criamos 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 essa implementação em um grande número de dispositivos diferentes; como não temos conhecimento de problemas pendentes, esta é a implementação recomendada para dispositivos enviados com o Android 8.

Herança de prioridade em tempo real

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

O driver do fichário sempre deu suporte à herança de prioridade agradável. Como um número crescente de processos no Android é executado 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 lida com essa chamada também seja executado com prioridade em tempo real . Para dar suporte a esses casos de uso, o Android 8 agora implementa a herança de prioridade em tempo real no driver do binder.

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

Alterações 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 do binder atual no kernel comum com uma exceção: a implementação original para desabilitar a herança de prioridade em tempo real para /dev/binder usou 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á na ramificação comum do Android e, em vez disso, é enviado 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 vantajoso 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 alteração no espaço do usuário.

SHAs para kernels comuns

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

  • Comum-3.18
    cc8b90c121de ANDROID: binder: não verifique as permissões prio na restauração.
  • Comum-4.4
    76b376eac7a2 ANDROID: binder: não verifique as permissões prioritárias na restauração.
  • Comum-4,9
    ecd972d4f9b5 ANDROID: binder: não verifique as permissões prioritárias na restauração.

Usando o IPC do fichário

Historicamente, os processos do fornecedor usaram a comunicação entre processos do binder (IPC) para se comunicar. No Android 8, o nó do dispositivo /dev/binder torna-se exclusivo para os 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 IPC do binder conforme descrito abaixo.

vndbinder

O Android 8 oferece suporte a um novo domínio de fichário para uso pelos serviços do fornecedor, 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 framework/app 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 comuns do kernel do Android).

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

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

vndservicemanager

Anteriormente, os serviços do binder eram registrados com servicemanager , onde podiam ser recuperados por outros processos. No Android 8, o servicemanager agora é usado exclusivamente por processos de framework e aplicativos e os processos de fornecedores não podem mais acessá-lo.

No entanto, os serviços do fornecedor agora podem usar vndservicemanager , uma nova instância do servicemanager que usa /dev/vndbinder em vez de /dev/binder e que é construída a partir das mesmas fontes que o framework servicemanager . Os processos do fornecedor não precisam fazer alterações para falar com o 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 dispositivo padrão do Android.

Política SELinux

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

  1. Acesso a /dev/vndbinder .
  2. Binder {transfer, call} se conecta ao vndservicemanager .
  3. binder_call(A, B) para qualquer domínio do fornecedor A que deseja 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 atender aos 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) para os processos do fornecedor A e B que precisam conversar sobre o binder 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 de serviço, rótulos de serviço e 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ço 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, o vndservicemanager carrega o arquivo vndservice_contexts . Os serviços do fornecedor migrando para o 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;