Aplikasi anda memanggil VpnService.prepare, menerima null, membina terowong, dan deskriptor kelihatan sah, tetapi tiada apa yang sampai ke pelayan. Atau prepare mengembalikan Intent, pengguna meluluskannya, dan establish masih gagal. Kedua-dua hasil biasanya bermaksud perkara yang sama: aplikasi lain sudah memiliki antara muka VPN untuk pengguna ini, dan panggilan protect anda menandakan soket yang terowong anda tidak dapat gunakan. Lalui semakan di bawah mengikut urutan dan berhenti pada yang pertama gagal, kerana langkah kemudian tidak boleh membaiki langkah terdahulu.

Semak slot VPN sebelum menyentuh protect

Android memberikan setiap pengguna tepat satu antara muka VPN aktif. Itu peraturan platform, bukan kebenaran yang boleh diminta aplikasi anda, jadi dua klien yang kedua-duanya mahu menjadi terowong tidak boleh wujud bersama.

VPN sentiasa aktif yang ditetapkan untuk aplikasi lain ialah kes paling sukar, kerana sistem mengekalkan aplikasi itu bersambung dan menolak pengambilalihan. Penapis trafik seperti AdGuard, klien korporat atau VPN pengguna boleh memegang slot itu sedangkan pengguna terlupa ia dipasang. Minta pengguna membuka Tetapan dan membaca entri yang disenaraikan di bawah VPN; nama di situ ialah pemilik yang anda saingi. Sehingga entri itu diputuskan atau tetapan sentiasa aktif dikosongkan, VpnService anda tidak boleh menjadi antara muka itu.

Langkah 1: baca apa yang prepare kembalikan

VpnService.prepare menjawab satu soalan: bolehkah aplikasi ini menjadi VPN? Ia mengembalikan null apabila persetujuan sudah ada, dan Intent apabila pengguna masih perlu meluluskannya, yang mesti anda lancarkan dengan startActivityForResult. Anggap pulangan null sebagai kebenaran untuk meneruskan, bukan bukti bahawa establish akan berjaya.

Intent intent = VpnService.prepare(context);
if (intent != null) {
    startActivityForResult(intent, REQUEST_VPN);
}

Jika laluan Intent kembali dan pengguna menolak, prepare akan terus mengembalikan Intent pada percubaan seterusnya. Log kod hasil dan bukannya mencuba semula dalam gelung, kerana persetujuan yang ditolak ialah keputusan pengguna, bukan ralat sementara.

Langkah 2: sahkan antara muka itu milik aplikasi anda

Selepas establish, ParcelFileDescriptor yang anda terima ialah bukti pemilikan anda, dan wajar diperiksa sebelum apa-apa kerja soket. Deskriptor yang null atau sudah ditutup bermaksud antara muka itu tidak pernah dicipta, walaupun tiada pengecualian muncul. Bandingkan nama antara muka daripada builder dengan yang dilaporkan sistem untuk VPN aktif, menggunakan dumpsys pada peranti ujian. Jika berbeza, terowong yang anda bina bukan terowong yang membawa trafik.

adb shell dumpsys connectivity

Ketidakseragaman yang sama menjelaskan ikon bersambung dengan trafik mati. Sistem menunjukkan terowong aplikasi lain, sedangkan kod anda memegang deskriptor untuk antara muka yang tidak dimilikinya.

Langkah 3: panggil protect sebelum connect

Setelah antara muka wujud, soket yang dibuka aplikasi anda dihalakan ke terowong secara lalai. Pengangkutan luar tidak boleh mengikut peraturan itu atau paket yang dikapsulkan akan kembali ke antara muka, jadi VpnService protect menandakan soket untuk memintas VPN dan menggunakan rangkaian di bawahnya. Panggilan itu mengembalikan boolean, dan mengabaikannya ialah cara anda berakhir dengan soket yang kelihatan sihat dan tidak membawa apa-apa.

Panggil protect sebelum connect atau bind, tidak pernah selepasnya. Perlindungan mesti ada sebelum soket disambungkan atau diikat, kerana soket yang bersambung sudah memilih laluannya dan protect yang kemudian tidak memindahkannya dengan pasti. Jika protect mengembalikan false, berhenti dan semak semula pemilikan: ia gagal apabila antara muka bukan milik aplikasi anda, iaitu tepat keadaan yang ditinggalkan VPN lain. Pulangan false ialah isyarat untuk kembali ke Langkah 1, bukan mencuba semula soket yang sama.

DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
    return;
}
socket.connect(serverAddress, serverPort);

Langkah 4: ikat proses apabila soket menyusahkan

Sesetengah pengangkutan bukan satu soket: pustaka mungkin membuka beberapa, atau anda mahu setiap sambungan dalam proses pembantu menggunakan jalan keluar yang sama. ConnectivityManager.bindProcessToNetwork mengikat proses itu kepada rangkaian yang dipilih, dan setProcessDefaultNetwork yang lebih lama melakukan perkara sama sebelum ia usang. Minta sambungan di bawahnya dengan NET_CAPABILITY_NOT_VPN supaya pengikatan memilih rangkaian di bawah terowong anda, bukan terowong itu 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 untuk pengangkutan anda dan tidak berguna untuk menjalankan dua terowong sekali gus.

Baiki peranti pengguna, bukan laluan kod

Tiada API yang membenarkan dua aplikasi berkongsi slot VPN, jadi pembaikan terletak pada keadaan peranti. Tawarkan dua laluan konkrit kepada pengguna: putuskan atau kosongkan sentiasa aktif bagi aplikasi lain supaya prepare anda boleh mengambil alih, atau buka tetapan mengikut aplikasi bagi aplikasi itu dan kecualikan aplikasi anda daripada terowongnya. Pengecualian itu mengekalkan penapis berjalan untuk semua yang lain dan membolehkan aplikasi anda menggunakan sambungan biasa, yang selalunya hasil yang benar-benar dikehendaki pengguna.

Tulis kedua-dua laluan dalam dokumentasi sokongan anda, kerana pengguna jarang tahu aplikasi mana yang memiliki VPN. Minta mereka menghantar nama VPN aktif daripada Tetapan dan hasil prepare, establish serta protect daripada log anda, dan anda boleh tahu dalam satu minit langkah mana yang gagal. Kemudian sahkan dengan trafik sebenar: carian nama, sambungan TCP dan satu permintaan melalui terowong, bukan sekadar ikon bersambung. Deskriptor sah dan penunjuk hijau membuktikan pemilikan paling baik, tidak pernah bahawa pengangkutan terselamat daripada peraturan penghalaan.

Jika keempat-empat semakan lulus dan trafik masih mati, pemilikan bukan lagi masalah anda. Teruskan ke penghalaan dan ketersampaian pelayan, dan uji kelayakan yang sama daripada peranti tanpa aplikasi VPN dipasang.

Artikel berkaitan