VPN

Android menyediakan API bagi developer untuk membuat solusi virtual private network (VPN). Setelah membaca panduan ini, Anda akan mengetahui cara mengembangkan dan menguji klien VPN Anda sendiri untuk perangkat yang didukung Android.

Ringkasan

VPN memungkinkan perangkat yang secara fisik tidak berada di jaringan untuk mengakses jaringan dengan aman.

Android menyertakan klien VPN bawaan (PPTP dan L2TP/IPSec), yang terkadang disebut VPN lama. Android 4.0 (API Level 14) memperkenalkan API sehingga developer aplikasi dapat menyediakan solusi VPN mereka sendiri. Anda mengemas solusi VPN ke dalam aplikasi yang diinstal orang ke perangkat. Developer biasanya membuat aplikasi VPN karena salah satu alasan berikut:

  • Untuk menawarkan protokol VPN yang tidak didukung oleh klien bawaan.
  • Untuk membantu orang terhubung ke layanan VPN tanpa konfigurasi yang rumit.

Bagian selanjutnya dari panduan ini menjelaskan cara mengembangkan aplikasi VPN (termasuk VPN selalu aktif dan per aplikasi) dan tidak mencakup klien VPN bawaan.

Pengalaman pengguna

Android menyediakan antarmuka pengguna (UI) untuk membantu seseorang mengonfigurasi, memulai, dan menghentikan solusi VPN Anda. UI sistem juga membuat pengguna perangkat mengetahui koneksi VPN yang aktif. Android menampilkan komponen UI berikut untuk koneksi VPN:

  • Sebelum aplikasi VPN dapat aktif untuk pertama kalinya, sistem akan menampilkan dialog permintaan koneksi. Dialog ini akan meminta pengguna perangkat untuk mengonfirmasi bahwa mereka memercayai VPN dan menerima permintaan tersebut.
  • Layar setelan VPN (Setelan > Jaringan & Internet > VPN) menampilkan aplikasi VPN tempat pengguna menerima permintaan koneksi. Ada tombol untuk mengkonfigurasi opsi sistem atau melupakan VPN.
  • Baki Setelan Cepat menampilkan panel informasi saat koneksi aktif. Mengetuk label akan menampilkan dialog berisi informasi selengkapnya dan link ke Setelan.
  • Status bar berisi ikon (kunci) VPN, menunjukkan bahwa koneksi sedang aktif.

Aplikasi Anda juga harus menyediakan UI agar orang yang menggunakan perangkat dapat mengonfigurasi opsi layanan Anda. Misalnya, solusi Anda mungkin perlu mengambil setelan autentikasi akun. Aplikasi akan menampilkan UI berikut:

  • Kontrol untuk memulai dan menghentikan koneksi secara manual. VPN yang selalu aktif dapat terhubung saat diperlukan, tetapi izinkan pengguna untuk mengonfigurasi koneksi saat pertama kali menggunakan VPN Anda.
  • Notifikasi yang tidak dapat ditutup saat layanan aktif. Notifikasi dapat menampilkan status koneksi atau memberikan informasi selengkapnya, seperti statistik jaringan. Mengetuk notifikasi akan membawa aplikasi ke latar depan. Hapus notifikasi setelah layanan tidak aktif.

Layanan VPN

Aplikasi Anda menghubungkan jaringan sistem untuk pengguna (atau profil kerja) ke gateway VPN. Setiap pengguna (atau profil kerja) dapat menjalankan aplikasi VPN yang berbeda. Anda dapat membuat layanan VPN yang digunakan sistem untuk memulai dan menghentikan VPN, serta melacak status koneksi. Layanan VPN Anda mewarisi dari VpnService.

Layanan ini juga berfungsi sebagai container untuk koneksi gateway VPN dan antarmuka perangkat lokalnya. Instance layanan Anda memanggil metode VpnService.Builder untuk membuat antarmuka lokal baru.

Gambar 1. Cara VpnService menghubungkan jaringan Android ke gateway VPN
Diagram blok-arsitektur yang menunjukkan cara VpnService membuat antarmuka TUN
         lokal dalam jaringan sistem.

Aplikasi Anda mentransfer data berikut untuk menghubungkan perangkat ke gateway VPN:

  • Membaca paket IP keluar dari deskriptor file antarmuka lokal, mengenkripsinya, dan mengirimkannya ke gateway VPN.
  • Menulis paket masuk (diterima dan didekripsi dari gateway VPN) ke deskripsi file antarmuka lokal.

Hanya ada satu layanan aktif per pengguna atau profil. Memulai layanan baru, menghentikan layanan yang ada secara otomatis.

Tambahkan layanan

Untuk menambahkan layanan VPN ke aplikasi Anda, buat layanan Android yang diwarisi dari VpnService. Deklarasikan layanan VPN di file manifes aplikasi Anda dengan tambahan berikut:

  • Lindungi layanan dengan izin BIND_VPN_SERVICE sehingga hanya sistem yang dapat mengikat ke layanan Anda.
  • Beri tahu layanan dengan filter intent "android.net.VpnService" sehingga sistem dapat menemukan layanan Anda.

Contoh ini menunjukkan cara mendeklarasikan layanan di file manifes aplikasi Anda:

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

Setelah aplikasi Anda mendeklarasikan layanan, sistem dapat otomatis memulai dan menghentikan layanan VPN aplikasi Anda saat diperlukan. Misalnya, sistem mengontrol layanan Anda saat menjalankan VPN selalu aktif.

Menyiapkan layanan

Untuk menyiapkan aplikasi untuk menjadi layanan VPN pengguna saat ini, panggil VpnService.prepare(). Jika pengguna perangkat belum memberikan izin untuk aplikasi Anda, metode ini akan menampilkan intent aktivitas. Gunakan intent ini untuk memulai aktivitas sistem yang meminta izin. Sistem menampilkan dialog yang serupa dengan dialog izin lainnya, seperti akses kamera atau kontak. Jika aplikasi Anda sudah siap, metode ini akan menampilkan null.

Hanya satu aplikasi yang dapat menjadi layanan VPN yang disiapkan saat ini. Selalu panggil VpnService.prepare() karena seseorang mungkin telah menetapkan aplikasi lain sebagai layanan VPN sejak aplikasi Anda terakhir kali memanggil metode. Untuk mempelajari lebih lanjut, lihat bagian Siklus proses layanan.

Menghubungkan layanan

Setelah layanan berjalan, Anda dapat membuat antarmuka lokal baru yang terhubung ke gateway VPN. Untuk meminta izin dan menghubungkan layanan Anda ke gateway VPN, Anda harus menyelesaikan langkah-langkah dalam urutan berikut:

  1. Panggil VpnService.prepare() untuk meminta izin (jika diperlukan).
  2. Panggil VpnService.protect() untuk mempertahankan soket tunnel aplikasi Anda di luar VPN sistem dan menghindari koneksi sirkular.
  3. Panggil DatagramSocket.connect() untuk menghubungkan soket tunnel aplikasi Anda ke gateway VPN.
  4. Panggil metode VpnService.Builder untuk mengonfigurasi antarmuka TUN lokal baru di perangkat untuk traffic VPN.
  5. Panggil VpnService.Builder.establish() agar sistem menetapkan antarmuka TUN lokal dan mulai merutekan traffic melalui antarmuka.

Gateway VPN biasanya menyarankan setelan untuk antarmuka TUN lokal selama handshaking. Aplikasi Anda memanggil metode VpnService.Builder untuk mengonfigurasi layanan seperti ditunjukkan dalam contoh berikut:

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();

Contoh di bagian VPN per aplikasi menunjukkan konfigurasi IPv6 yang menyertakan opsi lainnya. Anda perlu menambahkan nilai VpnService.Builder berikut sebelum membuat antarmuka baru:

addAddress()
Tambahkan setidaknya satu alamat IPv4 atau IPv6 beserta subnet mask yang ditetapkan oleh sistem sebagai alamat antarmuka TUN lokal. Aplikasi Anda biasanya menerima alamat IP dan subnet mask dari gateway VPN selama handshake.
addRoute()
Tambahkan setidaknya satu rute jika Anda ingin sistem mengirim traffic melalui antarmuka VPN. Merutekan filter berdasarkan alamat tujuan. Untuk menerima semua lalu lintas, tetapkan rute terbuka seperti 0.0.0.0/0 atau ::/0.

Metode establish() menampilkan instance ParcelFileDescriptor yang digunakan aplikasi Anda untuk membaca dan menulis paket ke dan dari buffer antarmuka. Metode establish() menampilkan null jika aplikasi Anda belum siap atau seseorang mencabut izin.

Siklus proses layanan

Aplikasi Anda harus melacak status VPN yang dipilih sistem dan semua koneksi yang aktif. Update antarmuka pengguna (UI) aplikasi Anda agar pengguna perangkat mengetahui setiap perubahan.

Memulai layanan

Layanan VPN Anda dapat dimulai dengan cara berikut:

  • Aplikasi Anda memulai layanan—biasanya karena pengguna mengetuk tombol sambungkan.
  • Sistem memulai layanan karena VPN selalu aktif diaktifkan.

Aplikasi Anda memulai layanan VPN dengan meneruskan intent ke startService(). Untuk mempelajari lebih lanjut, baca Memulai layanan.

Sistem memulai layanan Anda di latar belakang dengan memanggil onStartCommand(). Namun, Android menerapkan pembatasan pada aplikasi latar belakang dalam versi 8.0 (API Level 26) atau yang lebih baru. Jika Anda mendukung API Level ini, Anda harus mentransisikan layanan ke latar depan dengan memanggil Service.startForeground(). Untuk mempelajari lebih lanjut, baca Menjalankan layanan di latar depan.

Menghentikan layanan

Pengguna perangkat dapat menghentikan layanan Anda dengan menggunakan UI aplikasi Anda. Menghentikan layanan, bukan hanya menutup koneksi. Sistem juga menghentikan koneksi aktif saat pengguna yang menggunakan perangkat melakukan hal berikut di layar VPN aplikasi Setelan:

  • memutus atau melupakan aplikasi VPN
  • menonaktifkan VPN selalu aktif untuk koneksi yang aktif

Sistem memanggil metode onRevoke() layanan Anda, tetapi panggilan ini mungkin tidak terjadi di thread utama. Saat sistem memanggil metode ini, antarmuka jaringan alternatif sudah merutekan traffic. Anda dapat membuang resource berikut dengan aman:

VPN selalu aktif

Android dapat memulai layanan VPN saat perangkat melakukan booting dan terus menjalankannya saat perangkat aktif. Fitur ini disebut VPN yang selalu aktif dan tersedia di Android 7.0 (Level API 24) atau yang lebih tinggi. Meskipun Android mengelola siklus proses layanan, layanan VPN Andalah yang bertanggung jawab atas koneksi gateway VPN. VPN selalu aktif juga dapat memblokir koneksi yang tidak menggunakan VPN.

Pengalaman pengguna

Di Android 8.0 atau yang lebih tinggi, sistem menampilkan dialog berikut untuk membuat orang yang menggunakan perangkat mengetahui VPN selalu aktif:

  • Jika koneksi VPN yang selalu aktif terputus atau tidak dapat terhubung, pengguna akan melihat notifikasi yang tidak dapat ditutup. Mengetuk notifikasi akan menampilkan dialog yang menjelaskan lebih lanjut. Notifikasi akan hilang saat VPN terhubung kembali atau seseorang menonaktifkan opsi VPN selalu aktif.
  • VPN yang selalu aktif memungkinkan pengguna perangkat memblokir koneksi jaringan apa pun yang tidak menggunakan VPN. Jika opsi ini diaktifkan, aplikasi Setelan akan memperingatkan pengguna bahwa mereka tidak memiliki koneksi internet sebelum VPN terhubung. Aplikasi Setelan akan meminta pengguna perangkat untuk melanjutkan atau membatalkan.

Karena sistem (dan bukan pengguna) memulai dan menghentikan koneksi yang selalu aktif, Anda perlu menyesuaikan perilaku aplikasi dan antarmuka pengguna:

  1. Nonaktifkan UI yang memutuskan koneksi karena sistem dan aplikasi Setelan mengontrol koneksi.
  2. Simpan konfigurasi apa pun antara setiap aplikasi yang dijalankan dan konfigurasi koneksi dengan setelan terbaru. Karena sistem memulai aplikasi sesuai permintaan, orang yang menggunakan perangkat mungkin tidak selalu ingin mengonfigurasi koneksi.

Anda juga dapat menggunakan konfigurasi terkelola untuk mengonfigurasi koneksi. Konfigurasi terkelola membantu admin IT mengonfigurasi VPN dari jarak jauh.

Mendeteksi selalu aktif

Android tidak menyertakan API untuk mengonfirmasi apakah sistem memulai layanan VPN Anda. Namun, saat aplikasi menandai instance layanan apa pun yang dimulai, Anda dapat berasumsi bahwa sistem memulai layanan yang tidak ditandai untuk VPN selalu aktif. Berikut contohnya:

  1. Buat instance Intent untuk memulai layanan VPN.
  2. Tandai layanan VPN dengan menempatkan tambahan ke intent.
  3. Dalam metode onStartCommand() layanan, cari flag dalam tambahan argumen intent.

Koneksi diblokir

Pengguna perangkat (atau admin IT) dapat memaksa semua traffic untuk menggunakan VPN. Sistem memblokir semua traffic jaringan yang tidak menggunakan VPN. Orang yang menggunakan perangkat dapat menemukan tombol Blokir koneksi tanpa VPN di panel opsi VPN di Setelan.

Menghentikan penggunaan selalu aktif

Jika saat ini aplikasi Anda tidak dapat mendukung VPN yang selalu aktif, Anda dapat memilih tidak ikut (di Android 8.1 atau yang lebih baru) dengan menyetel metadata layanan SERVICE_META_DATA_SUPPORTS_ALWAYS_ON ke false. Contoh manifes aplikasi berikut menunjukkan cara menambahkan elemen metadata:

<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>

Jika aplikasi Anda memilih untuk tidak menggunakan VPN selalu aktif, sistem akan menonaktifkan opsi kontrol UI di Setelan.

VPN per aplikasi

Aplikasi VPN dapat memfilter aplikasi terinstal mana yang diizinkan untuk mengirimkan traffic melalui koneksi VPN. Anda dapat membuat daftar yang diizinkan, atau, daftar yang tidak diizinkan, tetapi tidak keduanya. Jika Anda tidak membuat daftar yang diizinkan atau tidak diizinkan, sistem akan mengirimkan semua traffic jaringan melalui VPN.

Aplikasi VPN Anda harus menetapkan daftar sebelum koneksi dibuat. Jika Anda perlu mengubah daftar, buat koneksi VPN baru. Aplikasi harus diinstal di perangkat saat Anda menambahkannya ke daftar.

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();

Aplikasi yang diizinkan

Untuk menambahkan aplikasi ke daftar yang diizinkan, panggil VpnService.Builder.addAllowedApplication(). Jika daftar menyertakan satu atau beberapa aplikasi, hanya aplikasi dalam daftar yang dapat menggunakan VPN. Semua aplikasi lain (yang tidak ada dalam daftar) menggunakan jaringan sistem seolah-olah VPN tidak berjalan. Jika daftar yang diizinkan kosong, semua aplikasi dapat menggunakan VPN.

Aplikasi yang tidak diizinkan

Untuk menambahkan aplikasi ke daftar yang tidak diizinkan, panggil VpnService.Builder.addDisallowedApplication(). Aplikasi yang tidak diizinkan akan menggunakan jaringan sistem seolah-olah VPN tidak berjalan—semua aplikasi lain menggunakan VPN.

Mem-bypass VPN

VPN dapat mengizinkan aplikasi untuk mem-bypass VPN dan memilih jaringannya sendiri. Untuk mengabaikan VPN, panggil VpnService.Builder.allowBypass() saat membuat antarmuka VPN. Anda tidak dapat mengubah nilai ini setelah memulai layanan VPN. Jika aplikasi tidak mengikat prosesnya atau soket ke jaringan tertentu, traffic jaringan aplikasi akan dilanjutkan melalui VPN.

Aplikasi yang terikat ke jaringan tertentu tidak memiliki koneksi saat seseorang memblokir traffic yang tidak melalui VPN. Untuk mengirim traffic melalui jaringan tertentu, aplikasi memanggil metode, seperti ConnectivityManager.bindProcessToNetwork() atau Network.bindSocket() sebelum menghubungkan soket.

Kode contoh

Proyek Open Source Android mencakup aplikasi contoh yang disebut ToyVPN. Aplikasi ini menunjukkan cara menyiapkan dan menghubungkan layanan VPN.