Kako u kodu provjeriti je li VPN povezan na Androidu
Android nema getter za stanje VPN veze. Pitaj mrežne mogućnosti, prati promjene transporta i drži odvojeno saglasnost aplikacije, stanje uređaja i dokaz sa servera.
Ne možete pitati Android je li VPN povezan. VpnService ne izlaže getter stanja veze, a ikonu ključa u statusnoj traci crta sistem, ne nešto što vaša aplikacija može pročitati. Ono što možete jest da steku povezanosti postavite uže pitanje: koju mrežu koristi saobraćaj ove aplikacije i prenosi li ta mreža VPN transport. Ovaj vodič prolazi kroz provjere redoslijedom koji izbjegava lažno pozitivne rezultate, od shell komande na uređaju do callbacka koji ostavljate da radi u aplikaciji.
Provjerite uređaj iz shella prije nego što dirnete kod aplikacije
Prvo uzmite osnovno stanje. adb shell dumpsys connectivity ispisuje svaku mrežu koju sistem zna, a svaki blok navodi svoje transporte, pa se VPN mreža pojavljuje sa VPN na tom spisku. Prvo pročitajte blok podrazumijevane mreže, zatim tražite druge mreže koje prenose isti transport.
Dva izlaza odgovaraju na različita pitanja. Postojanje VPN mreže nije isto što i to da je vaša aplikacija koristi, a na uređaju sa radnim profilom ili drugim korisnikom oboje može biti tačno istovremeno dok vaš proces razgovara direktno sa internetom. Linija aktivne mreže je ona koja odlučuje šta rade vaši socketi.
Pitajte koju mrežu aplikacija koristi
U kodu je test mogućnosti četiri linije. Zatražite ACCESS_NETWORK_STATE u manifestu, a zatim uporedite mogućnosti aktivne mreže sa 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 podrazumijevana mreža aplikacije trenutno VPN. Ne govori ništa o tome koji VPN, je li vaš ili doseže li tunel drugi kraj. Tretirajte true kao „saobraćaj izlazi kroz neki tunel“ i vodite svoju evidenciju odvojeno. Logička vrijednost koja tiho znači tri stvari istovremeno razlog je zašto ekran postavki prijavljuje VPN kao isključen dok korisnik pregleda kroz njega.
Kada, nasuprot tome, trebate znati postoji li ikakva VPN mreža, iterirajte cm.getAllNetworks() i testirajte svaki skup mogućnosti. Dva odgovora se razilaze na uređaju koji pokreće VPN za pojedine aplikacije koji pokriva druge aplikacije ali ne vašu, i to razilaženje izvor je većine prijava grešaka koje kažu da aplikacija ne vidi VPN koji je očigledno uključen.
Pratite promjene umjesto anketiranja
Anketiranje po tajmeru pogrešan je oblik za ovo pitanje, jer promjene stanja dolaze kao događaji. Registrujte 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 prenosi NET_CAPABILITY_NOT_VPN, pa eksplicitno uklanjanje dokumentuje namjeru. Bez toga zahtjev se i dalje poklapa, ali sljedeća osoba koja čita kod mora to sama shvatiti.
Registracija pripada opsegu kojem je potrebno posmatranje, obično ekranu ili usluzi u prvom planu. Odjavite u onStop ili onDestroy i ne ostavljajte callback prikačen za aktivnost koje više nema. Aplikacija bez vidljive komponente loš je domaćin za stalnog posmatrača, a zaboravljeni callbacki su oni koji padaju nakon promjene konfiguracije.
Držite tri pitanja odvojeno
Većina zabune u ovoj oblasti dolazi od tretiranja tri različite provjere kao jedne. One koriste različite API-je i svaka može biti tačna dok su ostale netač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 i tu se ne može odgovoriti. Dajte VPN klijentima vlastiti bazen adresa i čitajte izvornu adresu na serveru; to je jedini dokaz koji preživi promjenu tunela usred sesije.
Redoslijed provjere koji isključuje lažno pozitivne rezultate
Stariji primjer koda koji još cirkuliše poziva getNetworkInfo(ConnectivityManager.TYPE_VPN). Taj poziv je zastario i na nekoliko izdanja vraćao je null čak i dok je VPN bio povezan, zbog čega mnogi kopirani isječci izgledaju pokvareni na uređaju koji radi. Provjere mogućnosti izbjegavaju tu 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 rezervno rješenje, jer je upravo rezervno rješenje proizvelo pogrešan odgovor.
Jedan prečica zaslužuje upozorenje. Poređenje javne adrese koju prijavljuje vanjska usluga sa izlaznom adresom VPN-a dokazuje nešto o putu, ali dodaje zahtjev treće strane u svaku provjeru, zavisi od toga da ta usluga ostane dostupna i može biti keširano ili blokirano. Držite to kao ručnu dijagnostiku, nikada kao provjeru stanja unutar aplikacije.
Izvršite provjere ovim redoslijedom i zaustavite se na prvoj koja se ne slaže s izvještajem korisnika: spisak transporta iz shella, mogućnosti aktivne mreže, callback događaji dok uključujete i isključujete tunel, zatim izvorna adresa na strani servera. Ako se sva četiri slažu i aplikacija se i dalje ponaša pogrešno, greška je u vašoj vlastitoj mašini stanja, a ne u Androidovom viđenju mreže.