Vaša aplikacija pokliče VpnService.prepare, prejme null, zgradi tunel in deskriptor je videti veljaven, a nič ne doseže strežnika. Ali prepare vrne Intent, uporabnik ga odobri, in establish še vedno ne uspe. Oba izida običajno pomenita isto: druga aplikacija že ima vmesnik VPN za tega uporabnika, vaši klici protect pa označujejo vtičnice, ki jih vaš tunel ne more uporabiti. Pojdite skozi spodnja preverjanja po vrsti in se ustavite pri prvem, ki ne uspe, ker poznejši korak ne more popraviti prejšnjega.

Preden se dotaknete protect, preverite mesto VPN

Android daje vsakemu uporabniku natanko en aktivni vmesnik VPN. To je pravilo platforme, ne dovoljenje, ki bi ga vaša aplikacija lahko zahtevala, zato dva odjemalca, ki hočeta biti tunel, ne moreta obstajati hkrati.

Vedno vklopljen VPN, nastavljen za drugo aplikacijo, je najtežji primer, ker sistem drži to aplikacijo povezano in zavrne prevzem. Filter prometa, kot je AdGuard, poslovni odjemalec ali potrošniški VPN, lahko zasede mesto, medtem ko je uporabnik pozabil, da je nameščen. Prosita uporabnika, naj odpre Nastavitve in prebere vnos pod VPN; ime tam je lastnik, s katerim tekmujete. Dokler ta vnos ni prekinjen ali nastavitev vedno vklopljeno ni izbrisana, vaš VpnService ne more postati vmesnik.

Korak 1: preberite, kaj vrne prepare

VpnService.prepare odgovarja na eno vprašanje: ali se sme ta aplikacija stati VPN? Vrne null, ko soglasje že obstaja, in Intent, ko ga mora uporabnik še odobriti, kar morate zagnati s startActivityForResult. Vrnjeni null obravnavajte kot dovoljenje za nadaljevanje, ne kot dokaz, da bo establish uspel.

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

Če se pot Intent vrne in je uporabnik zavrnil, bo prepare ob naslednjem poskusu še naprej vračal Intent. Zapišite kodo rezultata, namesto da poskušate znova v zanki, ker je zavrnjeno soglasje odločitev uporabnika, ne prehodna napaka.

Korak 2: potrdite, da vmesnik pripada vaši aplikaciji

Po establish je ParcelFileDescriptor, ki ga prejmete, vaše dokazilo o lastništvu, in vredno ga je preveriti pred kakršnim koli delom z vtičnicami. Deskriptor, ki je null ali že zaprt, pomeni, da vmesnik nikoli ni bil ustvarjen, tudi če se ni pojavila nobena izjema. Primerjajte ime vmesnika iz graditelja s tistim, ki ga sistem sporoča za aktivni VPN, z uporabo dumpsys na testni napravi. Če se razlikujeta, tunel, ki ste ga zgradili, ni tunel, ki prenaša promet.

adb shell dumpsys connectivity

Ista neskladnost pojasnjuje povezano ikono z mrtvim prometom. Sistem prikazuje tunel druge aplikacije, medtem ko vaša koda drži deskriptor vmesnika, ki ga ne poseduje.

Korak 3: pokličite protect pred connect

Ko vmesnik obstaja, so vtičnice, ki jih odpre vaša aplikacija, privzeto usmerjene v tunel. Zunanji transport ne more slediti temu pravilu, sicer bi se inkapsulirani paket vrnil v vmesnik, zato VpnService protect označi vtičnico, da obide VPN in uporabi spodnje omrežje. Klic vrne boolean, njegovo ignoriranje pa je pot do vtičnice, ki je videti zdrava in ne prenaša ničesar.

Pokličite protect pred connect ali bind, nikoli po njem. Zaščita mora biti na mestu, preden je vtičnica povezana ali vezana, ker si je povezana vtičnica že izbrala pot in poznejši protect je ne premakne zanesljivo. Če protect vrne false, se ustavite in znova preverite lastništvo: ne uspe, ko vmesnik ne pripada vaši aplikaciji, kar je natanko stanje, ki ga pusti drug VPN. Vrnitev false je znak, da se vrnete na Korak 1, ne da znova poskusite isto vtičnico.

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

Korak 4: povežite proces, ko je vtičnica nerodna

Nekateri transporti niso ena vtičnica: knjižnica jih lahko odpre več ali pa želite, da vsaka povezava v pomožnem procesu uporabi isti izhod. ConnectivityManager.bindProcessToNetwork poveže proces z izbranim omrežjem, starejši setProcessDefaultNetwork pa je delal isto, preden je bil opuščen. Zahtevajte spodnjo povezavo z NET_CAPABILITY_NOT_VPN, da vezava izbere omrežje pod vašim tunelom in ne tunela samega.

NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();

Vezava procesa ni drugi VPN. Izbere izhod za ta proces, kar je koristno za vaš transport in neuporabno za hkratno delovanje dveh tunelov.

Popravite napravo uporabnika, ne poti kode

Ni API-ja, ki bi dvema aplikacijama dovolil deliti mesto VPN, zato popravek spada k stanju naprave. Uporabniku ponudite dve konkretni poti: prekinite ali izbrišite vedno vklopljeno za drugo aplikacijo, da lahko vaš prepare prevzame, ali odprite nastavitve po aplikacijah te aplikacije in izvzemite svojo aplikacijo iz njenega tunela. Izvzetje ohrani filter delujoč za vse ostalo in omogoči vaši aplikaciji uporabo običajne povezave, kar je pogosto rezultat, ki ga uporabnik res želi.

Zapišite obe poti v svojo dokumentacijo za podporo, ker uporabniki redko vedo, katera aplikacija ima VPN. Prosite jih, naj pošljejo ime aktivnega VPN iz Nastavitev in rezultate prepare, establish ter protect iz vaših dnevnikov, in v minuti lahko poveste, kateri korak ni uspel. Nato preverite z resničnim prometom: iskanje imena, povezavo TCP in eno zahtevo skozi tunel, ne le povezane ikone. Veljaven deskriptor in zelen indikator dokazujeta lastništvo največ, nikoli tega, da je transport preživel pravila usmerjanja.

Če vsa štiri preverjanja uspejo in promet še vedno umira, lastništvo ni več vaša težava. Nadaljujte z usmerjanjem in dosegljivostjo strežnika ter preizkusite iste poverilnice z naprave brez nameščene aplikacije VPN.

Sorodni članki