Um app Android não consegue resolver um nome interno enquanto o navegador do próprio cliente VPN chega até ele, e a mesma build funciona em outro telefone. A URL não é a variável. A variável é qual rede o processo do app pode usar para resolução, e o Android não garante que a VPN ativa seja a rede que seus sockets usam. Faça estas verificações em ordem e pare quando a captura do resolver não bater com sua suposição.

Passo 1: prove em qual rede o app está

Leia a visão do próprio app, não a barra de status. Em ConnectivityManager, obtenha a rede ativa, seus LinkProperties, a lista de servidores DNS e a configuração de DNS privado. Se a lista contém o resolver da VPN, o app tem um caminho até ele; se contém o resolver do Wi-Fi, o túnel não é a rede padrão daquele processo.

Um ícone de VPN só diz que existe um túnel. Não diz que o processo do app foi vinculado a ele, e o Android nem sempre encaminha a resolução de nomes pela rede que parece ativa. Apps VPN locais que encaminham para outro dispositivo, redes Wi-Fi sem internet e builds de fabricantes podem deixar o app perguntando ao resolver padrão em vez dos servidores do túnel.

Passo 2: solicite o transporte VPN e vincule o processo

Quando o app precisa usar o resolver do túnel, solicite uma rede com transporte VPN e vincule o processo no callback, para que o código não adivinhe quando a rede aparece:

val request = NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .build()

connectivityManager.requestNetwork(request, object : NetworkCallback() {
    override fun onAvailable(network: Network) {
        val bound = connectivityManager.bindProcessToNetwork(network)
        Log.i(TAG, "bound to VPN network: " + bound)
    }
})

A chamada muda a rede padrão de todo o processo, então cada socket criado depois usa essa rede. Sockets que já existem mantêm a rede em que foram criados, e é por isso que um pool de conexões aberto antes do vínculo pode continuar resolvendo pelo caminho antigo. O valor retornado é booleano, e um resultado falso significa que o vínculo não ocorreu; tente de novo com um pequeno atraso e registre as duas tentativas.

Passo 3: limite o vínculo ao que precisa dele

O vínculo de todo o processo é grosseiro. Telemetria, verificações de atualização e SDKs de terceiros também passam para o túnel, e se o túnel não tiver internet eles começam a falhar. Para uma única requisição, use a fábrica de sockets da rede e mantenha o padrão do dispositivo para todo o resto. Escolha um modelo de propósito: um vínculo estreito por conexão, ou um vínculo de processo com unbind explícito quando a VPN desaparece.

Dois recursos do resolver podem derrotar o vínculo sem nenhum erro. O DNS privado em modo estrito envia consultas a um servidor DoT nomeado em vez do resolver da rede, e um app que traz seu próprio cliente DNS-over-HTTPS contorna totalmente o resolver do sistema. Verifique os dois antes de concluir que o vínculo falhou. Uma consulta que nunca chega ao servidor DNS da VPN não aparecerá em nenhum log do lado do túnel.

Passo 4: não confie numa restrição CONNECTED

NetworkType.CONNECTED no WorkManager significa que há uma rede conectada, e o transporte VPN a satisfaz mesmo quando o link Wi-Fi ou celular por baixo sumiu. A tarefa roda, toda requisição falha, e a política de repetição transforma isso numa tempestade que parece um bug de agendamento. O túnel é uma rede; a restrição nunca foi uma checagem de internet.

Verifique a capacidade de que o trabalho realmente precisa, como acesso à internet validado, e mantenha uma política de repetição limitada com backoff. Se a tarefa precisa de um nome privado, resolva esse nome na rede selecionada antes de fazer o trabalho, e falhe rápido com um erro claro em vez de repetir uma requisição que não pode ter sucesso.

Passo 5: registre as evidências e reteste a faixa

Registre os tipos de transporte da rede selecionada, os servidores DNS de LinkProperties, se o vínculo retornou true, e a falha exata da primeira requisição. Depois execute um nome privado e um nome público sob o mesmo vínculo. Esse par mostra se o app usou o resolver do túnel ou caiu silenciosamente no padrão do dispositivo, e essa é a diferença que o usuário sente.

Repita na versão mais antiga do Android que você suporta e em uma versão atual. O comportamento do resolver que faz o caminho padrão parecer correto é exatamente onde dispositivos e versões diferem, e uma build que só passa no dispositivo de teste mais novo não está verificada para a faixa que você envia. adb shell dumpsys connectivity é uma forma rápida de confirmar quais redes e capacidades o dispositivo relata enquanto você reproduz a falha.

Artigos relacionados