Perlindungan VpnService gagal saat VPN lain sudah aktif
Android mengizinkan satu antarmuka VPN per pengguna. Jalankan pemeriksaan prepare, establish, protect dan kepemilikan secara berurutan, lalu pilih pencabutan atau pengecualian per aplikasi.
Aplikasi Anda memanggil VpnService.prepare, mendapat null, membangun terowongan, dan deskriptornya tampak valid, tetapi tidak ada yang mencapai server. Atau prepare mengembalikan Intent, pengguna menyetujuinya, dan establish tetap gagal. Kedua hasil biasanya berarti hal yang sama: aplikasi lain sudah memiliki antarmuka VPN untuk pengguna ini, dan panggilan protect Anda menandai socket yang tidak dapat digunakan terowongan Anda. Jalankan pemeriksaan di bawah ini secara berurutan dan berhenti pada yang pertama gagal, karena langkah berikutnya tidak dapat memperbaiki langkah sebelumnya.
Periksa slot VPN sebelum menyentuh protect
Android memberi setiap pengguna tepat satu antarmuka VPN aktif. Itu aturan platform, bukan izin yang bisa diminta aplikasi Anda, jadi dua klien yang sama-sama ingin menjadi terowongan tidak dapat hidup berdampingan.
VPN selalu aktif yang disetel untuk aplikasi lain adalah kasus tersulit, karena sistem menjaga aplikasi itu tetap terhubung dan menolak pengambilalihan. Filter lalu lintas seperti AdGuard, klien korporat, atau VPN konsumen dapat menahan slot sementara pengguna lupa bahwa aplikasi itu terpasang. Minta pengguna membuka Setelan dan membaca entri yang tercantum di bawah VPN; nama di sana adalah pemilik yang Anda saingi. Sampai entri itu diputuskan atau setelan selalu aktif dihapus, VpnService Anda tidak dapat menjadi antarmuka.
Langkah 1: baca apa yang dikembalikan prepare
VpnService.prepare menjawab satu pertanyaan: bolehkah aplikasi ini menjadi VPN? Ia mengembalikan null ketika persetujuan sudah ada, dan Intent ketika pengguna masih harus menyetujuinya, yang harus Anda luncurkan dengan startActivityForResult. Perlakukan null sebagai izin untuk melanjutkan, bukan sebagai bukti bahwa establish akan berhasil.
Intent intent = VpnService.prepare(context);
if (intent != null) {
startActivityForResult(intent, REQUEST_VPN);
}Jika jalur Intent kembali dan pengguna menolak, prepare akan terus mengembalikan Intent pada percobaan berikutnya. Catat kode hasil alih-alih mencoba ulang dalam perulangan, karena persetujuan yang ditolak adalah keputusan pengguna, bukan galat sementara.
Langkah 2: pastikan antarmuka milik aplikasi Anda
Setelah establish, ParcelFileDescriptor yang Anda terima adalah bukti kepemilikan, dan layak diperiksa sebelum pekerjaan socket apa pun. Deskriptor yang null atau sudah tertutup berarti antarmuka tidak pernah dibuat, meskipun tidak ada pengecualian yang muncul. Bandingkan nama antarmuka dari builder dengan yang dilaporkan sistem untuk VPN aktif, memakai dumpsys pada perangkat uji. Jika berbeda, terowongan yang Anda bangun bukan terowongan yang membawa lalu lintas.
adb shell dumpsys connectivityKetidakcocokan yang sama menjelaskan ikon tersambung dengan lalu lintas mati. Sistem menampilkan terowongan aplikasi lain, sementara kode Anda memegang deskriptor untuk antarmuka yang tidak dimilikinya.
Langkah 3: panggil protect sebelum connect
Setelah antarmuka ada, socket yang dibuka aplikasi Anda dirutekan ke terowongan secara default. Transport luar tidak dapat mengikuti aturan itu atau paket yang dienkapsulasi akan kembali ke antarmuka, jadi VpnService protect menandai socket untuk melewati VPN dan memakai jaringan yang mendasarinya. Panggilan mengembalikan boolean, dan mengabaikannya adalah cara Anda berakhir dengan socket yang tampak sehat dan tidak membawa apa pun.
Panggil protect sebelum connect atau bind, jangan pernah sesudahnya. Perlindungan harus sudah ada sebelum socket tersambung atau terikat, karena socket yang tersambung sudah memilih rutenya dan protect yang lebih lambat tidak memindahkannya secara andal. Jika protect mengembalikan false, berhenti dan periksa ulang kepemilikan: ia gagal ketika antarmuka bukan milik aplikasi Anda, yang persis merupakan keadaan yang ditinggalkan VPN lain. Kembalian false adalah sinyal untuk kembali ke Langkah 1, bukan mencoba ulang socket yang sama.
DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
return;
}
socket.connect(serverAddress, serverPort);Langkah 4: ikat proses saat socket merepotkan
Beberapa transport bukan satu socket: pustaka mungkin membuka beberapa, atau Anda ingin setiap koneksi dalam proses pembantu memakai jalan keluar yang sama. ConnectivityManager.bindProcessToNetwork mengikat proses ke jaringan yang dipilih, dan setProcessDefaultNetwork yang lebih tua melakukan hal yang sama sebelum dinyatakan usang. Minta koneksi yang mendasarinya dengan NET_CAPABILITY_NOT_VPN agar pengikatan memilih jaringan di bawah terowongan Anda, bukan terowongannya sendiri.
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();Mengikat proses bukan VPN kedua. Ia memilih jalan keluar untuk proses itu, yang berguna bagi transport Anda dan tidak berguna untuk menjalankan dua terowongan sekaligus.
Perbaiki perangkat pengguna, bukan jalur kodenya
Tidak ada API yang memungkinkan dua aplikasi berbagi slot VPN, jadi perbaikannya ada pada keadaan perangkat. Tawarkan dua jalur konkret kepada pengguna: putuskan atau hapus selalu aktif untuk aplikasi lain agar prepare Anda dapat mengambil alih, atau buka setelan per aplikasi aplikasi itu dan kecualikan aplikasi Anda dari terowongannya. Pengecualian menjaga filter tetap berjalan untuk semua hal lain dan memungkinkan aplikasi Anda memakai koneksi biasa, yang sering menjadi hasil yang sebenarnya diinginkan pengguna.
Tuliskan kedua jalur itu dalam dokumentasi dukungan Anda, karena pengguna jarang tahu aplikasi mana yang memiliki VPN. Minta mereka mengirim nama VPN aktif dari Setelan dan hasil prepare, establish, serta protect dari log Anda, dan Anda dapat mengetahui dalam satu menit langkah mana yang gagal. Lalu verifikasi dengan lalu lintas nyata: pencarian nama, koneksi TCP, dan satu permintaan melalui terowongan, bukan hanya ikon tersambung. Deskriptor valid dan indikator hijau paling banyak membuktikan kepemilikan, tidak pernah bahwa transport selamat dari aturan perutean.
Jika keempat pemeriksaan lolos dan lalu lintas tetap mati, kepemilikan bukan lagi masalah Anda. Lanjutkan ke perutean dan keterjangkauan server, dan uji kredensial yang sama dari perangkat tanpa aplikasi VPN terpasang.