Ler o DNS do Android corretamente: por rede, não no resolv.conf
Seu app Android resolve por meio de um resolvedor por rede, não pelo resolv.conf que você acabou de ler. Inspecione primeiro LinkProperties, os vínculos de socket e o DNS privado.
Você obtém um shell no dispositivo, lê /etc/resolv.conf ou desenterra um valor antigo de net.dns1, e recebe um endereço. O app à sua frente resolve por algo totalmente diferente. Em uma versão atual do Android, o resolvedor é por rede: o sistema mantém uma política para cada Network e a aplica ao socket que fez a requisição. O arquivo e a propriedade legada são sobras, e nenhum dos dois descreve o caminho que sua consulta realmente percorreu.
Essa lacuna aparece durante implantações de split tunnel e DNS privado, em que dois apps no mesmo telefone podem legitimamente ver respostas diferentes. Este guia começa na consulta errada ou ausente e volta até a configuração que é dona dela.
O resolvedor pertence a uma rede, não ao dispositivo
A resolução de nomes no Android 9 e posteriores passa por um resolvedor do sistema que se baseia na Network à qual um socket está vinculado. Cada rede carrega suas próprias LinkProperties, e essas propriedades contêm a lista de servidores DNS, os domínios de busca e o estado do DNS privado apenas daquela rede. Nada nesse modelo corresponde a uma lista única do dispositivo que você possa ler de um arquivo.
Uma VPN torna a divisão óbvia. Quando o túnel sobe, o app de VPN fornece servidores DNS registrados na rede própria da VPN, então apps roteados por ela resolvem nesses servidores enquanto apps excluídos continuam usando a rede subjacente. Um dispositivo, dois caminhos de resposta, nenhuma contradição.
Ler o instantâneo de dentro do app afetado
Pergunte ao próprio app. Um método que tenha um Context e a permissão normal android.permission.ACCESS_NETWORK_STATE pode ler o instantâneo sem tocar em propriedades do sistema:
val cm = getSystemService(ConnectivityManager::class.java) ?: return
val network = cm.activeNetwork
val links = network?.let { cm.getLinkProperties(it) }
val dnsServers = links?.dnsServers
val privateDnsActive = links?.isPrivateDnsActive
val privateDnsName = links?.privateDnsServerNameactiveNetwork é a rede padrão do seu processo, não a única do dispositivo, e pode ser null quando o chamador não tem um padrão utilizável. Uma VPN padrão bloqueada é o motivo habitual. isPrivateDnsActive diz se as consultas naquela rede vão por TLS até um resolvedor nomeado, o que muda o que uma sonda UDP 53 simples mostrará. A superfície de API está documentada em LinkProperties, e cada valor nela é um instantâneo da configuração, não prova de que uma consulta específica chegou a um servidor específico.
Quando a rede padrão não é a que faz o trabalho
O instantâneo responde apenas pela rede padrão. Um app pode vincular seu processo ou um único socket a outra rede, e então a consulta segue essa escolha enquanto seu diagnóstico continua relatando a padrão. Um navegador que traz seu próprio resolvedor DNS-over-HTTPS nunca consulta a lista do sistema, então suas respostas não dizem nada sobre o resto do dispositivo. Um app de perfil de trabalho roda sob outro usuário com política própria. Verifique o vínculo antes de culpar os servidores.
A integração de VPN é a outra metade. Receber uma linha dhcp-option DNS em um perfil OpenVPN e registrar esse endereço na rede VPN do Android são dois passos separados, e uma rota ausente até o servidor falha tão silenciosamente quanto um endereço errado. De uma máquina de desenvolvimento, adb shell dumpsys connectivity imprime as mesmas propriedades de link por rede quando a build permite, o que confirma a lista sem escrever código.
Comparar a configuração com uma consulta que você pode fazer
Escolha um nome que precise resolver no espaço privado e um nome público, e anote os dois resultados ao lado do instantâneo: rede, servidores configurados, estado do DNS privado e o texto exato do erro. Uma consulta direta a um servidor nomeado prova que aquele servidor responde a você; não prova que a resolução normal da aplicação o escolheu. Uma resposta em cache não é um teste novo, então limpe o cache ou use um nome que ninguém consultou hoje.
Se o resolvedor só atende clientes de uma rede de acesso específica, o que ele vê após a saída da VPN pode decidir o resultado, e isso é uma questão de política para quem o opera, não evidência de que as operadoras bloqueiam UDP 53. Onde o DNS privado está ativo, um nome de host não qualificado pode ser recusado por projeto enquanto o mesmo servidor responde nomes públicos normalmente. Ler as propriedades diz qual modo está em vigor, então você para de adivinhar filtros de pacotes.
Corrigir a camada que é dona da divergência
Suba em vez de editar o estado global. Se o app está vinculado à rede errada, corrija o vínculo. Se o perfil traz a opção DNS errada, corrija o perfil. Se o DNS privado é estrito em uma rede cujo resolvedor não consegue atender sua zona privada, isso é uma decisão de gestão de dispositivos, não um motivo para desligar o recurso em todos os lugares.
Forçar resolvedores públicos em um gancho DHCP ou em uma propriedade do sistema é o reparo que não para de causar incidentes: nomes privados deixam de resolver, a mudança sobrevive à sessão de VPN, e a próxima pessoa no dispositivo encontra um endereço obsoleto sem dono. Mantenha a mudança dentro da VPN, do app ou da camada de gestão que é dona da divergência, e depois releia o instantâneo para confirmar que a lista de servidores realmente mudou.
O que você quer no fim é uma requisição de app ligada a uma rede, uma lista de resolvedores e um estado de DNS privado. Quando você consegue mostrar essa cadeia, um endereço familiar em um arquivo deixa de ser prova de qualquer coisa.