Kako u kodu provjeriti je li VPN povezan na Androidu
Android nema getter za stanje VPN veze. Pitajte mrežne mogućnosti, pratite promjene transporta i držite odvojeno pristanak aplikacije, stanje uređaja i dokaz sa poslužitelja.
Ne možete pitati Android je li VPN povezan. VpnService ne izlaže getter za povezanost, a ikonu ključa u statusnoj traci crta sustav, a ne bilo što što vaša aplikacija može pročitati. Ono što možete jest pitati stog za povezanost uže pitanje: koju mrežu koristi promet ove aplikacije i nosi li ta mreža VPN transport. Ovaj priručnik prolazi kroz provjere redom koji izbjegava lažne pozitivne rezultate, od shell naredbe na uređaju do callbacka koji ostavljate da radi u aplikaciji.
Provjerite uređaj iz shella prije nego dirnete kod aplikacije
Prvo uzmite osnovnu liniju. adb shell dumpsys connectivity ispisuje svaku mrežu koju sustav poznaje, a svaki blok navodi svoje transporte, pa se VPN mreža pojavljuje s VPN na tom popisu. Pročitajte blok zadane mreže prije svega ostalog, zatim tražite druge mreže koje nose isti transport.
Dva izlaza odgovaraju na različita pitanja. Postojanje VPN mreže nije isto kao to koristi li je vaša aplikacija, a na uređaju s radnim profilom ili drugim korisnikom oboje može biti istovremeno točno dok vaš proces razgovara s internetom izravno. Linija aktivne mreže je ona koja odlučuje što rade vaši socketi.
Pitajte za mrežu koju aplikacija koristi
U kodu je test mogućnosti četiri reda. Zatražite ACCESS_NETWORK_STATE u manifestu, zatim usporedite mogućnosti aktivne mreže s VPN transportom:
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean vpnInUse = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Odgovor je namjerno uzak: je li zadana mreža aplikacije upravo sada VPN. Ne govori ništa o tome koji je VPN, pripada li vama i dospijeva li tunel do svog udaljenog kraja. Tretirajte true kao “promet izlazi kroz neki tunel” i držite svoje vlastito knjigovodstvo odvojeno. Boolean koji tiho znači tri stvari odjednom jest način na koji zaslon postavki na kraju prijavljuje da je VPN isključen dok korisnik pregledava kroz njega.
Kada umjesto toga trebate znati postoji li bilo koja VPN mreža, iterirajte cm.getAllNetworks() i testirajte svaki skup mogućnosti. Dva se odgovora razilaze na uređaju koji pokreće VPN po aplikaciji koji pokriva druge aplikacije ali ne vašu, a to razilaženje izvor je većine prijava grešaka koje kažu da aplikacija ne vidi VPN koji je očito uključen.
Pratite promjene umjesto anketiranja
Anketiranje po mjeraču vremena pogrešan je oblik za ovo pitanje, jer promjene stanja dolaze kao događaji. Registrirajte zahtjev koji odgovara VPN transportu i pustite okvir da vam kaže kada se odgovarajuća mreža pojavi, nestane ili promijeni:
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(request, callback);
VPN mreža nikada ne nosi NET_CAPABILITY_NOT_VPN, pa eksplicitno uklanjanje dokumentira namjeru. Bez njega zahtjev se i dalje podudara, ali sljedeća osoba koja čita kod mora to sama zaključiti.
Registracija pripada opsegu kojem je potrebno promatranje, obično zaslonu ili usluzi u prvom planu. Odjavite se u onStop ili onDestroy i ne ostavljajte callback vezan za aktivnost koje više nema. Aplikacija bez vidljive komponente loš je domaćin za trajnog promatrača, a callbacki koje zaboravite su oni koji padaju nakon promjene konfiguracije.
Držite tri pitanja odvojeno
Većina zabune u ovom području dolazi od tretiranja tri različite provjere kao jedne. One koriste različite API-je i svaka može biti točna dok su ostale netočne.
| Question | How you answer it | What a yes means |
|---|---|---|
| Did the user grant VPN consent to my app? | VpnService.prepare() returns null | Consent exists; the tunnel may still be down |
| Is the device sending default traffic through a VPN? | TRANSPORT_VPN on the active network | Something is tunneling, not necessarily your app |
| Did this request arrive through the VPN? | Source address seen by the server | Proof for that connection, nothing wider |
Treći red je onaj na koji ljudi pokušavaju odgovoriti na klijentu, a tamo se ne može odgovoriti. Dajte VPN klijentima vlastiti bazen adresa i čitajte izvornu adresu na poslužitelju; to je jedini dokaz koji preživi kada korisnik promijeni tunel usred sesije.
Redoslijed provjere koji isključuje lažne pozitivne rezultate
Stariji primjer koda koji je još u optjecaju poziva getNetworkInfo(ConnectivityManager.TYPE_VPN). Taj je poziv zastario i u nekoliko izdanja vraćao je null čak i kada je VPN bio povezan, zato mnogi kopirani isječci izgledaju pokvareno na uređaju koji radi. Provjere mogućnosti izbjegavaju zamku i ponašaju se isto od Androida 5.0. Ako održavate bazu koda koja ga još poziva, zamijenite poziv umjesto da ga umotate u rezervnu opciju, jer je rezervna opcija i proizvela pogrešan odgovor.
Jedan prečac zaslužuje upozorenje. Uspoređivanje javne adrese koju prijavljuje vanjska usluga s izlaznom adresom VPN-a dokazuje nešto o putanji, ali dodaje zahtjev treće strane u svaku provjeru, ovisi o tome da ta usluga ostane dostupna i može biti keširano ili blokirano. Držite ga kao ručnu dijagnostiku, nikada kao provjeru stanja unutar aplikacije.
Izvodite provjere ovim redom i zaustavite se na prvoj koja se ne slaže s korisničkim izvještajem: popis transporta u shellu, mogućnosti aktivne mreže, callback događaji dok uključujete i isključujete tunel, zatim izvorna adresa na strani poslužitelja. Ako se sve četiri poklope a aplikacija se i dalje ponaša loše, greška je u vašem vlastitom stroju stanja, a ne u Androidovu viđenju mreže.