VPN

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

O Android oferece APIs para que os desenvolvedores criem soluções de rede privada virtual (VPN, na sigla em inglês). Depois de ler este guia, você saberá como desenvolver e testar o próprio cliente VPN para dispositivos com tecnologia Android.

Visão geral

As VPNs permitem que dispositivos acessem a rede com segurança mesmo sem estar conectados fisicamente a ela.

O Android inclui um cliente VPN integrado (PPTP e L2TP/IPSec), que também é conhecido como VPN legada. O Android 4.0 (API nível 14) introduziu as APIs para que os desenvolvedores de apps pudessem oferecer as próprias soluções de VPN. Empacote sua solução de VPN em um app que as pessoas instalam no dispositivo. Normalmente, os desenvolvedores criam um app de VPN por um dos seguintes motivos:

  • Oferecer protocolos VPN incompatíveis com o cliente integrado.
  • Ajudar as pessoas a se conectarem a um serviço de VPN sem configuração complexa.

O restante deste guia explica como desenvolver apps de VPN (incluindo VPN sempre ativa e por app) e não abrange o cliente VPN integrado.

Experiência do usuário

O Android oferece uma interface do usuário (IU) para ajudar a configurar, iniciar e interromper sua solução de VPN. A IU do sistema também avisa o usuário do dispositivo sobre uma conexão VPN ativa. O Android mostra os seguintes componentes de IU para conexões VPN:

  • Antes de um app de VPN ser ativado pela primeira vez, o sistema exibe uma caixa de diálogo solicitando conexão. A caixa de diálogo solicita que o usuário do dispositivo confirme se confia na VPN e aceita a solicitação.
  • A tela de configurações da VPN (Configurações > Rede e internet > VPN) mostra os apps de VPN em que o usuário aceitou as solicitações de conexão. Existe um botão para configurar opções do sistema ou esquecer a VPN.
  • A bandeja "Configurações rápidas" mostra um painel de informações quando uma conexão está ativa. O toque na etiqueta exibe uma caixa de diálogo com mais informações e um link para "Configurações".
  • A barra de status inclui o ícone de VPN (chave) para indicar uma conexão ativa.

O app também precisa oferecer uma IU para que o usuário possa configurar as opções do seu serviço. Por exemplo, sua solução pode precisar capturar as configurações de autenticação da conta. Os apps devem exibir a seguinte IU:

  • Controles para iniciar e interromper manualmente uma conexão. A VPN sempre ativa pode conectar quando necessário, mas permite que as pessoas configurem a conexão na primeira vez que usarem sua VPN.
  • Uma notificação não dispensável quando o serviço está ativo. A notificação pode mostrar o status da conexão ou disponibilizar mais informações, como estatísticas da rede. O toque na notificação coloca o app em primeiro plano. Remova a notificação depois que o serviço ficar inativo.

Serviço VPN

Seu app conecta a rede do sistema de um usuário (ou um perfil de trabalho) a um gateway de VPN. Cada usuário (ou perfil de trabalho) pode executar um app de VPN diferente. Crie um serviço de VPN que o sistema use para iniciar e interromper a VPN e rastreie o status da conexão. Seu serviço de VPN é herdado de VpnService.

O serviço também funciona como seu contêiner para as conexões de gateway da VPN e as interfaces de dispositivos locais relacionadas. Sua instância de serviço chama os métodos VpnService.Builder para estabelecer uma nova interface local.

Figura 1. Como VpnService conecta a rede do Android ao gateway da VPN.
Diagrama de arquitetura de blocos mostrando como o VpnService cria uma interface TUN local na rede do sistema.

O app transfere os seguintes dados para conectar o dispositivo ao gateway da VPN:

  • Lê os pacotes IP de saída a partir do descritor de arquivos da interface local, os criptografa e envia ao gateway da VPN.
  • Grava os pacotes de entrada (recebidos e descriptografados do gateway da VPN) no descritor de arquivos da interface local.

Há apenas um serviço ativo por usuário ou perfil. A inicialização de um novo serviço automaticamente interrompe outro existente.

Adicionar um serviço

Para adicionar um serviço de VPN ao seu app, crie um serviço do Android herdado de VpnService. Declare o serviço de VPN no arquivo de manifesto do app com as seguintes adições:

  • Proteja o serviço com a permissão BIND_VPN_SERVICE para que somente o sistema possa se vincular a ele.
  • Anuncie o serviço com o filtro de intent "android.net.VpnService" para que o sistema possa encontrar seu serviço.

Este exemplo mostra como declarar o serviço no arquivo de manifesto do app:

<service android:name=".MyVpnService"
             android:permission="android.permission.BIND_VPN_SERVICE">
         <intent-filter>
             <action android:name="android.net.VpnService"/>
         </intent-filter>
    </service>
    

Agora que seu app declara o serviço, o sistema pode iniciar e interromper o serviço de VPN do app automaticamente quando necessário. Por exemplo, o sistema controla seu serviço ao executar a VPN sempre ativa.

Preparar um serviço

Se quiser preparar o app para que ele seja o serviço de VPN atual do usuário, chame VpnService.prepare(). Se o usuário ainda não deu permissão para seu app, o método retornará um intent de atividade. Use esse intent para iniciar uma atividade do sistema que solicite a permissão. O sistema exibe uma caixa semelhante às outras caixas de diálogo de permissão, como acesso à câmera ou aos contatos. Se o app estiver preparado, o método retornará null.

Apenas um app pode ser o atual serviço de VPN preparado. Sempre chame VpnService.prepare(), porque um app diferente pode ter sido definido como o serviço de VPN desde que seu app chamou o método pela última vez. Para saber mais, consulte a seção Ciclo de vida do serviço.

Conectar um serviço

Quando o serviço estiver em execução, será possível estabelecer uma nova interface local conectada ao gateway da VPN. Para solicitar permissão e conectar seu serviço ao gateway da VPN, é necessário seguir as etapas na seguinte ordem:

  1. Chame VpnService.prepare() para pedir permissão (quando necessário).
  2. Chame VpnService.protect() para manter o soquete do túnel do seu app fora da VPN do sistema e evitar uma conexão circular.
  3. Chame DatagramSocket.connect() para conectar o soquete do túnel do seu app ao gateway da VPN.
  4. Chame os métodos VpnService.Builder para configurar uma nova interface local TUN (em inglês) no dispositivo para o tráfego da VPN.
  5. Chame VpnService.Builder.establish() para que o sistema estabeleça a interface TUN local e comece a rotear o tráfego pela interface.

Normalmente, o gateway da VPN sugere configurações para a interface TUN local durante o handshake. O app chama os métodos VpnService.Builder para configurar um serviço, conforme mostrado no exemplo a seguir:

Kotlin

// Configure a new interface from our VpnService instance. This must be done
    // from inside a VpnService.
    val builder = Builder()

    // Create a local TUN interface using predetermined addresses. In your app,
    // you typically use values returned from the VPN gateway during handshaking.
    val localTunnel = builder
            .addAddress("192.168.2.2", 24)
            .addRoute("0.0.0.0", 0)
            .addDnsServer("192.168.1.1")
            .establish()

Java

// Configure a new interface from our VpnService instance. This must be done
    // from inside a VpnService.
    VpnService.Builder builder = new VpnService.Builder();

    // Create a local TUN interface using predetermined addresses. In your app,
    // you typically use values returned from the VPN gateway during handshaking.
    ParcelFileDescriptor localTunnel = builder
        .addAddress("192.168.2.2", 24)
        .addRoute("0.0.0.0", 0)
        .addDnsServer("192.168.1.1")
        .establish();

O exemplo na seção VPN por app mostra uma configuração de IPv6 com mais opções. É necessário adicionar os seguintes valores de VpnService.Builder antes de estabelecer uma nova interface:

addAddress()
Adicione pelo menos um endereço IPv4 ou IPv6 com uma máscara de sub-rede atribuída pelo sistema como o endereço da interface TUN local. Geralmente, seu app recebe os endereços IP e as máscaras de sub-rede de um gateway da VPN durante o handshake.
addRoute()
Adicione pelo menos uma rota caso queira que o sistema envie tráfego por meio da interface de VPN. As rotas são filtradas por endereços de destino. Para aceitar todo o tráfego, defina uma rota aberta, como 0.0.0.0/0 ou ::/0.

O método establish() retorna uma instância ParcelFileDescriptor usada pelo app para ler e gravar pacotes para o buffer da interface e a partir dele. O método establish() retornará null se o app não estiver preparado ou se a permissão for revogada.

Ciclo de vida do serviço

O app deve rastrear o status da VPN do sistema selecionada e quaisquer conexões ativas. Atualize a interface do usuário (IU) do seu app para manter o usuário informado sobre qualquer mudança.

Iniciar um serviço

O serviço de VPN pode ser iniciado das seguintes maneiras:

  • Seu app inicia o serviço, normalmente porque o usuário tocou em um botão de conexão.
  • O sistema inicia o serviço porque a VPN sempre ativa está ligada.

Seu app inicia o serviço de VPN transmitindo um intent para startService(). Para saber mais, leia Como iniciar um serviço.

O sistema inicia o serviço em segundo plano chamando onStartCommand(). No entanto, o Android coloca restrições nos apps em segundo plano, nas versões 8.0 (API nível 26) ou posteriores. Se você oferecer compatibilidade com esses níveis de API, será necessário fazer a transição do seu serviço para o primeiro plano. Para isso, chame Service.startForeground(). Para saber mais, leia Execução de serviço em primeiro plano.

Interromper um serviço

O usuário do dispositivo pode interromper o serviço usando a IU do seu app. O serviço é interrompido em vez de apenas encerrar a conexão. O sistema também interrompe uma conexão ativa quando o usuário do dispositivo faz o seguinte na tela da VPN do app Configurações:

  • Desconecta ou esquece o app de VPN.
  • Troca a VPN sempre ativa por uma conexão ativa.

O sistema chama o método onRevoke() do seu serviço, mas essa chamada pode não ocorrer na linha de execução principal. Quando o sistema chama esse método, uma interface de rede alternativa já está roteando o tráfego. Você pode descartar os seguintes recursos com segurança:

VPN sempre ativa

O Android pode iniciar um serviço de VPN quando o dispositivo é inicializado e manter esse serviço em execução enquanto o dispositivo estiver ligado. Esse recurso é denominado VPN sempre ativa e está disponível no Android 7.0 (API nível 24) ou versões posteriores. Embora o Android mantenha o ciclo de vida do serviço, seu serviço de VPN é responsável pela conexão de gateway da VPN. A VPN sempre ativa também pode bloquear conexões que não usam a VPN.

Experiência do usuário

No Android 8.0 ou versões posteriores, o sistema mostra as seguintes caixas de diálogo para manter o usuário do dispositivo informado sobre a VPN sempre ativa:

  • Quando as conexões de VPN sempre ativa se desconectam ou não conseguem se conectar, o usuário vê uma notificação não dispensável. O toque na notificação exibe uma caixa de diálogo que explica mais. A notificação desaparece quando a VPN é reconectada ou alguém desativa a opção da VPN sempre ativa.
  • A VPN sempre ativa permite que o usuário do dispositivo bloqueie qualquer conexão de rede que não use a VPN. Ao ativar essa opção, o app Configurações avisa o usuário de que ele não tem uma conexão de Internet antes que a VPN se conecte. O app Configurações solicita que o usuário do dispositivo continue ou cancele.

Como o sistema (e não uma pessoa) inicia e interrompe uma conexão sempre ativa, é necessário adaptar o comportamento e a interface do usuário do seu app:

  1. Desative qualquer IU que desconecte a conexão, porque o sistema e o app Configurações controlam a conexão.
  2. Salve toda configuração entre cada inicialização do app e configure uma conexão com as definições mais recentes. Como o sistema inicia seu app sob demanda, é possível que o usuário do dispositivo nem sempre queira configurar uma conexão.

Você também pode usar configurações gerenciadas para configurar uma conexão. As configurações gerenciadas ajudam o administrador de TI a configurar sua VPN remotamente.

Detectar a opção "sempre ativa"

O Android não inclui APIs para confirmar se o sistema iniciou o serviço de VPN. No entanto, quando seu app sinalizar qualquer instância de serviço iniciada, você poderá presumir que o sistema iniciou serviços não sinalizados para a VPN sempre ativa. Veja um exemplo:

  1. Crie uma instância Intent para iniciar o serviço de VPN.
  2. Sinalize o serviço de VPN colocando um extra no intent.
  3. No método onStartCommand() do serviço, procure a sinalização nos extras do argumento intent.

Conexões bloqueadas

Um usuário do dispositivo (ou um administrador de TI) pode forçar todo o tráfego a usar a VPN. O sistema bloqueará qualquer tráfego de rede que não use a VPN. O usuário do dispositivo pode encontrar a chave Bloquear conexões sem VPN acessando as "Configurações" e o painel de opções da VPN.

Desativar a opção "sempre ativa"

Se seu app não é compatível com a VPN sempre ativa no momento, é possível desativar essa opção (no Android 8.1 ou versões posteriores) configurando os metadados de serviço SERVICE_META_DATA_SUPPORTS_ALWAYS_ON como false. O exemplo de manifesto do app a seguir mostra como adicionar o elemento de metadados:

<service android:name=".MyVpnService"
             android:permission="android.permission.BIND_VPN_SERVICE">
         <intent-filter>
             <action android:name="android.net.VpnService"/>
         </intent-filter>
         <meta-data android:name="android.net.VpnService.SUPPORTS_ALWAYS_ON"
                 android:value=false/>
    </service>
    

Quando o app recusa a VPN sempre ativa, o sistema desativa os controles de opções da IU em "Configurações".

VPN por app

Os apps de VPN podem filtrar quais apps instalados têm permissão para enviar tráfego por meio da conexão VPN. Você pode criar uma lista de permissões ou de proibições, mas não ambas. Se você não criar listas de permissão ou de proibição, o sistema enviará todo o tráfego de rede por meio da VPN.

O app de VPN precisa definir as listas antes que a conexão seja estabelecida. Se você precisar mudar as listas, estabeleça uma nova conexão VPN. O app precisa estar instalado no dispositivo ao ser adicionado a uma lista.

Kotlin

// The apps that will have access to the VPN.
    val appPackages = arrayOf(
            "com.android.chrome",
            "com.google.android.youtube",
            "com.example.a.missing.app")

    // Loop through the app packages in the array and confirm that the app is
    // installed before adding the app to the allowed list.
    val builder = Builder()
    for (appPackage in appPackages) {
        try {
            packageManager.getPackageInfo(appPackage, 0)
            builder.addAllowedApplication(appPackage)
        } catch (e: PackageManager.NameNotFoundException) {
            // The app isn't installed.
        }
    }

    // Complete the VPN interface config.
    val localTunnel = builder
            .addAddress("2001:db8::1", 64)
            .addRoute("::", 0)
            .establish()

Java

// The apps that will have access to the VPN.
    String[] appPackages = {
        "com.android.chrome",
        "com.google.android.youtube",
        "com.example.a.missing.app"};

    // Loop through the app packages in the array and confirm that the app is
    // installed before adding the app to the allowed list.
    VpnService.Builder builder = new VpnService.Builder();
    PackageManager packageManager = getPackageManager();
    for (String appPackage: appPackages) {
      try {
        packageManager.getPackageInfo(appPackage, 0);
        builder.addAllowedApplication(appPackage);
      } catch (PackageManager.NameNotFoundException e) {
        // The app isn't installed.
      }
    }

    // Complete the VPN interface config.
    ParcelFileDescriptor localTunnel = builder
        .addAddress("2001:db8::1", 64)
        .addRoute("::", 0)
        .establish();

Apps permitidos

Para adicionar um app à lista de permissões, chame VpnService.Builder.addAllowedApplication(). Se a lista incluir um ou mais apps, somente os que estiverem na lista usarão a VPN. Todos os outros apps que não estiverem na lista usarão as redes do sistema como se a VPN não estivesse em execução. Quando a lista de permissões está vazia, todos os apps usam a VPN.

Apps não permitidos

Para adicionar um app à lista de proibições, chame VpnService.Builder.addDisallowedApplication(). Os apps não permitidos usarão as redes do sistema como se a VPN não estivesse em execução. Todos os outros apps usarão a VPN.

Ignorar a VPN

A VPN pode permitir que os apps a ignorem e selecionem a própria rede. Para ignorar a VPN, chame VpnService.Builder.allowBypass() ao estabelecer uma interface de VPN. Não é possível mudar esse valor depois de iniciar seu serviço de VPN. Se um app não vincular o processo ou um soquete a uma rede específica, o tráfego de rede do app continuará por meio da VPN.

Os apps que se vinculam a uma rede específica não têm conexão quando alguém bloqueia o tráfego que não passa pela VPN. Para enviar tráfego por meio de uma rede específica, os apps chamam métodos, como ConnectivityManager.bindProcessToNetwork() ou Network.bindSocket(), antes de conectar o soquete.

Amostra de código

O Android Open Source Project inclui uma amostra de app denominada ToyVPN. Esse app mostra como configurar e conectar um serviço de VPN.