VpnService apsauga nepavyksta, kai kitas VPN jau aktyvus
Android leidžia vieną VPN sąsają vienam naudotojui. Atlikite prepare, establish, protect ir nuosavybės patikras iš eilės, tada pasirinkite atšaukimą arba išimtį pagal programą.
Jūsų programa iškviečia VpnService.prepare, gauna null, sukuria tunelį, aprašas atrodo galiojantis, tačiau niekas nepasiekia serverio. Arba prepare grąžina Intent, naudotojas jį patvirtina, o establish vis tiek nepavyksta. Abu rezultatai paprastai reiškia tą patį: kita programa jau turi šio naudotojo VPN sąsają, o jūsų protect kvietimai žymi lizdus, kurių jūsų tunelis negali naudoti. Peržiūrėkite žemiau pateiktas patikras iš eilės ir sustokite ties pirmąja, kuri nepavyksta, nes vėlesnis žingsnis negali pataisyti ankstesnio.
Patikrinkite VPN vietą prieš liesdami protect
Android suteikia kiekvienam naudotojui tiksliai vieną aktyvią VPN sąsają. Tai platformos taisyklė, o ne leidimas, kurio jūsų programa gali prašyti, todėl du klientai, kurie abu nori būti tuneliu, negali egzistuoti kartu.
Kitos programos nustatytas visada įjungtas VPN yra sunkiausias atvejis, nes sistema palaiko tą programą prijungtą ir atsisako perėmimo. Eismo filtras, pavyzdžiui, AdGuard, įmonės klientas ar vartotojo VPN, gali užimti vietą, kol naudotojas pamiršo, kad jis įdiegtas. Paprašykite naudotojo atidaryti Nustatymus ir perskaityti įrašą, nurodytą skiltyje VPN; ten esantis pavadinimas yra savininkas, su kuriuo konkuruojate. Kol tas įrašas nebus atjungtas arba visada įjungta nuostata neištrinta, jūsų VpnService negali tapti sąsaja.
1 veiksmas: perskaitykite, ką grąžina prepare
VpnService.prepare atsako į vieną klausimą: ar ši programa gali tapti VPN? Jis grąžina null, kai sutikimas jau yra, ir Intent, kai naudotojas dar turi jį patvirtinti, kurį turite paleisti su startActivityForResult. Traktuokite null grąžinimą kaip leidimą tęsti, o ne kaip įrodymą, kad establish pavyks.
Intent intent = VpnService.prepare(context);
if (intent != null) {
startActivityForResult(intent, REQUEST_VPN);
}Jei Intent kelias grįžta ir naudotojas atsisakė, prepare ir toliau grąžins Intent kitą kartą. Registruokite rezultato kodą, o ne kartokite cikle, nes atsisakytas sutikimas yra naudotojo sprendimas, ne laikina klaida.
2 veiksmas: patvirtinkite, kad sąsaja priklauso jūsų programai
Po establish gautas ParcelFileDescriptor yra jūsų nuosavybės įrodymas, ir verta jį patikrinti prieš bet kokį darbą su lizdais. Aprašas, kuris yra null arba jau uždarytas, reiškia, kad sąsaja niekada nebuvo sukurta, net jei išimtis nepasirodė. Palyginkite sąsajos pavadinimą iš builder su tuo, kurį sistema nurodo aktyviam VPN, naudodami dumpsys testiniame įrenginyje. Jei jie skiriasi, jūsų sukurtas tunelis nėra tunelis, kuris perduoda eismą.
adb shell dumpsys connectivityTa pati neatitiktis paaiškina prijungtą piktogramą su mirusiu eismu. Sistema rodo kitos programos tunelį, o jūsų kodas laiko aprašą sąsajos, kurios jis neturi.
3 veiksmas: iškvieskite protect prieš connect
Kai sąsaja egzistuoja, lizdai, kuriuos atidaro jūsų programa, pagal numatytuosius nustatymus nukreipiami į tunelį. Išorinis transportas negali laikytis šios taisyklės, kitaip inkapsuliuotas paketas grįžtų į sąsają, todėl VpnService protect pažymi lizdą, kad apeitų VPN ir naudotų pagrindinį tinklą. Kvietimas grąžina boolean, o jo ignoravimas yra būdas atsidurti su lizdu, kuris atrodo sveikas ir nieko neperduoda.
Iškvieskite protect prieš connect arba bind, niekada po. Apsauga turi būti vietoje prieš prijungiant ar susiejant lizdą, nes prijungtas lizdas jau pasirinko maršrutą, o vėlesnis protect jo patikimai neperkelia. Jei protect grąžina false, sustokite ir iš naujo patikrinkite nuosavybę: jis nepavyksta, kai sąsaja nepriklauso jūsų programai, o tai yra būtent ta būsena, kurią palieka kitas VPN. false grąžinimas yra signalas grįžti į 1 veiksmą, o ne kartoti tą patį lizdą.
DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
return;
}
socket.connect(serverAddress, serverPort);4 veiksmas: susiekite procesą, kai lizdas nepatogus
Kai kurie transportai nėra vienas lizdas: biblioteka gali atidaryti kelis, arba galite norėti, kad kiekvienas ryšys pagalbiniame procese naudotų tą pačią išvestį. ConnectivityManager.bindProcessToNetwork susieja procesą su pasirinktu tinklu, o senesnis setProcessDefaultNetwork darė tą patį, kol nebuvo pripažintas pasenusiu. Prašykite pagrindinio ryšio su NET_CAPABILITY_NOT_VPN, kad susiejimas pasirinktų tinklą po jūsų tuneliu, o ne patį tunelį.
NetworkRequest request = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();Proceso susiejimas nėra antras VPN. Jis pasirenka išvestį tam procesui, kas naudinga jūsų transportui ir nenaudinga vykdyti du tunelius vienu metu.
Taisykite naudotojo įrenginį, o ne kodo kelią
Nėra API, leidžiančios dviem programoms dalytis VPN vieta, todėl pataisa priklauso įrenginio būsenai. Pasiūlykite naudotojui du konkrečius kelius: atjunkite arba ištrinkite visada įjungta kitai programai, kad jūsų prepare galėtų perimti, arba atidarykite tos programos nustatymus pagal programą ir pašalinkite savo programą iš jos tunelio. Išimtis palaiko filtrą veikiantį viskam kitam ir leidžia jūsų programai naudoti įprastą ryšį, o tai dažnai yra rezultatas, kurio naudotojas iš tikrųjų nori.
Įrašykite abu kelius į savo palaikymo dokumentaciją, nes naudotojai retai žino, kuri programa turi VPN. Paprašykite jų atsiųsti aktyvaus VPN pavadinimą iš Nustatymų ir prepare, establish bei protect rezultatus iš jūsų žurnalų, ir per minutę galite pasakyti, kuris žingsnis nepavyko. Tada patikrinkite tikru eismu: vardo užklausą, TCP ryšį ir vieną užklausą per tunelį, o ne tik prijungtą piktogramą. Galiojantis aprašas ir žalias indikatorius geriausiu atveju įrodo nuosavybę, bet niekada to, kad transportas išgyveno maršrutizavimo taisykles.
Jei visos keturios patikros praeina, o eismas vis tiek miršta, nuosavybė nebėra jūsų problema. Pereikite prie maršrutizavimo ir serverio pasiekiamumo ir išbandykite tuos pačius prisijungimo duomenis įrenginyje, kuriame nėra įdiegtos VPN programos.