La protekto de VpnService malsukcesas kiam alia VPN jam estas aktiva
Android permesas unu VPN-interfacon por uzanto. Plenumu la kontrolojn prepare, establish, protect kaj proprieto laŭorde, poste elektu revokon aŭ per-aplikan escepton.
Via aplikaĵo vokas VpnService.prepare, ricevas null, konstruas tunelon, kaj la priskribilo ŝajnas valida, tamen nenio atingas la servilon. Aŭ prepare redonas Intent, la uzanto aprobas ĝin, kaj establish ankoraŭ malsukcesas. Ambaŭ rezultoj kutime signifas la samon: alia aplikaĵo jam posedas la VPN-interfacon por ĉi tiu uzanto, kaj viaj vokoj protect markas poŝojn, kiujn via tunelo ne povas uzi. Trapasu la subajn kontrolojn laŭorde kaj haltu ĉe la unua, kiu malsukcesas, ĉar posta paŝo ne povas ripari antaŭan.
Kontrolu la VPN-lokon antaŭ ol tuŝi protect
Android donas al ĉiu uzanto ĝuste unu aktivan VPN-interfacon. Tio estas regulo de la platformo, ne permeso, kiun via aplikaĵo povas peti, do du klientoj, kiuj ambaŭ volas esti la tunelo, ne povas kunekzisti.
Ĉiam aktiva VPN agordita por alia aplikaĵo estas la plej malfacila kazo, ĉar la sistemo tenas tiun aplikaĵon konektita kaj rifuzas transprenon. Trafikfiltro kiel AdGuard, entreprena kliento aŭ konsumanta VPN povas teni la lokon dum la uzanto forgesis, ke ĝi estas instalita. Petu la uzanton malfermi Agordojn kaj legi la eron listigitan sub VPN; la nomo tie estas la posedanto, kun kiu vi konkeras. Ĝis tiu ero estas malkonektita aŭ la agordo ĉiam aktiva estas forviŝita, via VpnService ne povas fariĝi la interfaco.
Paŝo 1: legu, kion redonas prepare
VpnService.prepare respondas unu demandon: ĉu ĉi tiu aplikaĵo rajtas fariĝi la VPN? Ĝi redonas null kiam konsento jam ekzistas, kaj Intent kiam la uzanto ankoraŭ devas aprobi ĝin, kiun vi devas lanĉi per startActivityForResult. Traktu redonatan null kiel permeson daŭrigi, ne kiel pruvon, ke establish sukcesos.
Intent intent = VpnService.prepare(context);
if (intent != null) {
startActivityForResult(intent, REQUEST_VPN);
}Se la Intent-vojo revenas kaj la uzanto rifuzis, prepare daŭre redonos Intent ĉe la sekva provo. Registru la rezultkodon anstataŭ reprovi en buklo, ĉar rifuzita konsento estas decido de la uzanto, ne portempa eraro.
Paŝo 2: konfirmu, ke la interfaco apartenas al via aplikaĵo
Post establish, la ParcelFileDescriptor, kiun vi ricevas, estas via pruvo de proprieto, kaj indas kontroli ĝin antaŭ ajna laboro pri poŝoj. Priskribilo, kiu estas null aŭ jam fermita, signifas, ke la interfaco neniam estis kreita, eĉ se neniu escepto aperis. Komparu la interfacan nomon de la konstruanto kun tiu, kiun la sistemo raportas por la aktiva VPN, uzante dumpsys sur testa aparato. Se ili diferencas, la tunelo, kiun vi konstruis, ne estas la tunelo, kiu portas trafikon.
adb shell dumpsys connectivityLa sama malkongruo klarigas konektitan ikonon kun morta trafiko. La sistemo montras la tunelon de la alia aplikaĵo, dum via kodo tenas priskribilon por interfaco, kiun ĝi ne posedas.
Paŝo 3: voku protect antaŭ connect
Kiam la interfaco ekzistas, poŝoj, kiujn via aplikaĵo malfermas, estas defaŭlte enkursigataj en la tunelon. La ekstera transporto ne povas sekvi tiun regulon, alie la enkapsuligita pako reirus en la interfacon, tial VpnService protect markas poŝon por preterpasi la VPN kaj uzi la suban reton. La voko redonas boolean, kaj ignori ĝin estas la maniero fini kun poŝo, kiu aspektas sana kaj portas nenion.
Voku protect antaŭ connect aŭ bind, neniam poste. Protektado devas esti en loko antaŭ ol la poŝo estas konektita aŭ ligita, ĉar konektita poŝo jam elektis sian itineron kaj posta protect ne movas ĝin fidinde. Se protect redonas false, haltu kaj rekontrolu proprieton: ĝi malsukcesas kiam la interfaco ne apartenas al via aplikaĵo, kio estas ĝuste la stato, kiun alia VPN postlasas. Redono de false estas signalo reveni al Paŝo 1, ne reprovi la saman poŝon.
DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
return;
}
socket.connect(serverAddress, serverPort);Paŝo 4: ligu procezon kiam poŝo estas maloportuna
Kelkaj transportoj ne estas unu poŝo: biblioteko povas malfermi plurajn, aŭ vi eble volas, ke ĉiu konekto en helpa procezo uzu la saman eliron. ConnectivityManager.bindProcessToNetwork ligas la procezon al elektita reto, kaj la pli malnova setProcessDefaultNetwork faris la saman taskon antaŭ ol ĝi malrekomendiĝis. Petu la suban konekton per NET_CAPABILITY_NOT_VPN, por ke la ligado elektu la reton sub via tunelo anstataŭ la tunelon mem.
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();Ligi procezon ne estas dua VPN. Ĝi elektas eliron por tiu procezo, kio utilas por via transporto kaj senutilas por funkciigi du tunelojn samtempe.
Riparu la aparaton de la uzanto, ne la kodan vojon
Ne ekzistas API, kiu permesas al du aplikaĵoj kunhavigi la VPN-lokon, do la riparo apartenas al la stato de la aparato. Proponu al la uzanto du konkretajn vojojn: malkonektu aŭ forviŝu ĉiam aktiva por la alia aplikaĵo, por ke via prepare povu transpreni, aŭ malfermu la agordojn per aplikaĵo de tiu aplikaĵo kaj ekskludu vian aplikaĵon el ĝia tunelo. La escepto tenas la filtrilon funkcianta por ĉio alia kaj lasas vian aplikaĵon uzi la ordinaran konekton, kio ofte estas la rezulto, kiun la uzanto efektive volas.
Skribu ambaŭ vojojn en vian subtenan dokumentaron, ĉar uzantoj malofte scias, kiu aplikaĵo posedas la VPN. Petu ilin sendi la nomon de la aktiva VPN el Agordoj kaj la rezultojn de prepare, establish kaj protect el viaj protokoloj, kaj vi povas diri ene de minuto, kiu paŝo malsukcesis. Poste kontrolu per reala trafiko: nomserĉo, TCP-konekto kaj unu peto tra la tunelo, ne nur konektita ikono. Valida priskribilo kaj verda indikilo pruvas proprieton plej multe, neniam ke la transporto postvivis la enkursigajn regulojn.
Se ĉiuj kvar kontroloj pasas kaj trafiko ankoraŭ mortas, proprieto ne plu estas via problemo. Daŭrigu al enkursigo kaj servila atingeblo, kaj testu la samajn akreditaĵojn el aparato sen instalita VPN-a aplikaĵo.