Usando o Binder IPC

Esta página descreve as alterações no driver do fichário no Android 8, fornece detalhes sobre o uso do IPC do fichário 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 os HALs agora se comunicam entre si usando o fichário. Como essa comunicação aumenta drasticamente o tráfego do fichário, o Android 8 inclui várias melhorias projetadas para manter o IPC do fichário rápido. Fornecedores de SoC e OEMs devem se fundir diretamente das ramificações relevantes do android-4.4, android-4.9 e superiores do projeto kernel/common .

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

Comum-4.4 e superior, incluindo upstream

Para dividir de forma limpa 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 um contexto de fichário . Cada contexto de fichário 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ó do fichário por um determinado contexto, ele é acessível desse mesmo contexto apenas por outro processo, isolando completamente os domínios entre si. Para obter detalhes sobre como usar, consulte vndbinder e vndservicemanager .

Dispersão-reunião

Comum-4.4 e superior, incluindo upstream

Nas versões anteriores do Android, todos os dados em uma chamada do fichário 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 a otimização 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 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 a necessidade de outra cópia.

Bloqueio refinado

Comum-4.4 e superior, incluindo upstream

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

As tentativas iniciais de resolver esse problema envolviam 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 se tornado pública, melhorias substanciais foram feitas nas versões subsequentes.

Depois de identificar pequenos problemas na implementação de bloqueio refinada, 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 a testar essa 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 com 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 à boa herança de prioridade. 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 fichário, 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 herança de prioridade em tempo real no driver do fichário.

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. 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 de usuário necessárias para trabalhar com o driver de fichário 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 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). Portanto, 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 é desativada 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 fichário, sincronize com o SHA apropriado:

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

Usando o fichário IPC

Historicamente, os processos do fornecedor usaram a comunicação entre processos de fichário (IPC) para se comunicar. No Android 8, o nó do dispositivo /dev/binder torna-se exclusivo dos 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 os processos do fornecedor, o Android oferece suporte ao IPC do fichário, conforme descrito abaixo. No Android 10, Stable AIDL permite que todos os processos usem /dev/binder ao mesmo tempo em que resolve as garantias de estabilidade HIDL e /dev/hwbinder resolvidas. Para saber como usar o AIDL estável, consulte AIDL para HALs .

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 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 fornecedores/processos de fornecedores com interfaces AIDL

Para que /dev/vndbinder apareça, verifique se o item de configuração do kernel CONFIG_ANDROID_BINDER_DEVICES está 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 fichário diretamente e, em vez disso, vinculam-se à biblioteca libbinder userspace, que abre o driver do fichário. Adicionar um método para ::android::ProcessState() seleciona o driver de 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 o main() de um processo de fornecedor (cliente e servidor):

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

vndservicemanager

Anteriormente, os serviços de fichário eram registrados com servicemanager , onde podiam ser recuperados por outros processos. No Android 8, o servicemanager agora é usado exclusivamente por processos de estrutura e 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 do servicemanager que usa /dev/vndbinder em vez de /dev/binder e que é construída a partir das mesmas fontes do framework servicemanager . Os processos do fornecedor não precisam fazer alterações para se comunicar 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 do SELinux

Os processos do fornecedor que desejam usar a funcionalidade do fichário para se comunicar uns com os outros precisam do seguinte:

  1. Acesse /dev/vndbinder .
  2. Binder {transfer, call} conecta-se a vndservicemanager .
  3. binder_call(A, B) para qualquer domínio A do fornecedor que deseja chamar o domínio B do fornecedor por meio da 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 A e B do fornecedor 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 manipulados.

Para obter detalhes sobre o SELinux, consulte Security-Enhanced Linux in 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, o vndservicemanager carrega o arquivo vndservice_contexts . Os serviços do fornecedor que migram 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 acesso aos domínios 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;