No Android 10 e posteriores, uma aplicação que abre /proc/net/tcp recebe uma negação de acesso em vez de uma tabela de sockets. A interface VPN não se importa: os pacotes continuam a chegar, porque o túnel é uma decisão de encaminhamento, não uma permissão de ficheiro. O que mudou é a resposta a uma pergunta mais estreita: «que aplicação é dona desta ligação?» O substituto é ConnectivityManager.getConnectionOwnerUid, a consulta por ligação suportada, e a escolha entre o velho hábito e a nova API depende de qual das três tarefas realmente tem: receber pacotes, atribuir um fluxo ou somar consumo.

Duas formas de responder à pergunta do proprietário

Antes da API 29, um processo privilegiado ou root podia ler as tabelas de sockets do kernel, emparelhar o quádruplo e saber o UID proprietário. Isso dava um inventário de todo o dispositivo num único ficheiro. Desde a API 29 a vista do sistema de ficheiros está restrita, e o caminho suportado é uma consulta por ligação com o protocolo, o endereço local e o endereço remoto.

QuestionLegacy socket tablesgetConnectionOwnerUid
Who owns this flow?Scan and match the tupleAsk once per tuple
Device-wide inventory?Yes, when readableNo, one flow at a time
Historical usage?NoNo; that is NetworkStatsManager
Works without root?No on modern releasesYes, for authorized callers

A comparação não é «antigo contra novo». São três tarefas diferentes que antes eram satisfeitas por um único ficheiro legível e agora são satisfeitas por três interfaces diferentes com três contratos diferentes.

O endereço local é a extremidade da aplicação

A assinatura do método recebe um protocolo, um endereço de socket local e um endereço de socket remoto. «Local» significa o lado da aplicação na ligação, não a extremidade que no momento da inspeção é a origem do pacote. Um pacote que viaja do servidor remoto de volta para a aplicação tem origem e destino invertidos em relação a essa definição.

ConnectivityManager cm = getSystemService(ConnectivityManager.class);
int uid = cm.getConnectionOwnerUid(
    OsConstants.IPPROTO_TCP, appLocalAddress, appRemoteAddress);

Este erro de direção é a razão mais comum para uma consulta correta devolver a resposta errada, ou nenhuma. Registe ambas as extremidades antes da chamada e confirme o par contra a ligação que a aplicação abriu realmente. A família de endereços também importa: um endereço mapeado por IPv4 e um endereço IPv6 nativo são tuplas diferentes, tal como os fluxos TCP e UDP nas mesmas portas.

A autorização e os resultados desconhecidos fazem parte do contrato

A consulta está restrita a chamadores autorizados, incluindo a aplicação VPN ativa para o utilizador em causa. Uma aplicação de monitorização alheia não pode assumir que consegue enumerar as ligações de outro perfil, e uma chamada não autorizada pode lançar uma exceção de segurança em vez de devolver um erro amigável. Trate ambos os resultados de forma explícita.

Um proprietário ausente não é um nome de pacote. O fluxo pode ter fechado entre o pacote e a consulta, a tupla pode estar incompleta, ou pode simplesmente não ser permitido ao chamador saber. Guardar a primeira resposta bem-sucedida e reutilizá-la mais tarde atribui uma ligação nova a uma aplicação antiga, o que é pior do que reportar desconhecido.

Quando a atribuição falha, o tráfego continua a precisar de uma decisão: bloqueá-lo onde a política o exige, permitir apenas um âmbito que compreenda, ou expor a lacuna. Esse recurso pertence ao desenho, não a um comentário.

A contabilidade é outra pergunta

NetworkStatsManager reporta o consumo segundo as suas próprias regras de acesso. Pode dizer quanto um UID transferiu num período; não pode dizer o proprietário de um pacote vivo arbitrário. As capacidades de rede descrevem propriedades de uma rede, não um diretório de sockets. Misturar as três produz painéis que parecem coerentes e não são.

Para o caso simples de decidir que aplicações usam o túnel, as listas suportadas de aplicações permitidas e proibidas do Android já expressam a política sem extrair nada. Reserve a atribuição por fluxo para os casos que realmente precisam dela, e orçamente uma implementação mantida que trate direção, permissões e estados desconhecidos em conjunto.

Escolha a ferramenta pela tarefa e depois teste as arestas

Se o requisito é a seleção de túnel, use a lista da plataforma. Se é atribuição em tempo real, use a consulta de proprietário e trate cada tupla sem resposta como desconhecida. Se são totais históricos, use a API de estatísticas. Depois teste as três arestas que quebram implementações ingénuas: um pacote no sentido inverso, um fluxo que fecha antes da consulta e um chamador que não é a VPN ativa.

Nada disto exige desativar o SELinux, fazer root ao telemóvel ou tornar as tabelas do kernel legíveis globalmente. A restrição é o ponto: a plataforma moveu a atribuição para trás de um contrato para que a resposta esteja disponível para o componente que legitimamente precisa dela, e para mais ninguém.

Artigos relacionados