Androida ne morete vprašati, ali je VPN povezan. VpnService ne izpostavlja getterja za stanje povezave, ikono ključa v vrstici stanja pa nariše sistem, ne nekaj, kar bi vaša aplikacija lahko prebrala. Lahko pa skladu povezljivosti zastavite ožje vprašanje: katero omrežje uporablja promet te aplikacije in ali to omrežje prenaša transport VPN. Ta vodnik pelje skozi preverjanja v vrstnem redu, ki se izogne lažno pozitivnim rezultatom, od ukaza shell v napravi do povratnega klica, ki ga pustite teči v aplikaciji.

Preverite napravo iz lupine, preden se dotaknete kode aplikacije

Najprej pridobite osnovno stanje. adb shell dumpsys connectivity izpiše vsako omrežje, ki ga sistem pozna, vsak blok pa navaja svoje transporte, zato se omrežje VPN pojavi s VPN na tem seznamu. Najprej preberite blok privzetega omrežja, nato poiščite druga omrežja, ki prenašajo isti transport.

Dva izhoda odgovarjata na različni vprašanji. Obstoj omrežja VPN ni isto kot to, ali ga vaša aplikacija uporablja, in v napravi z delovnim profilom ali drugim uporabnikom sta lahko oba resnična hkrati, medtem ko vaš proces govori neposredno z internetom. Prav vrstica aktivnega omrežja odloča, kaj počnejo vaši vtičnice.

Vprašajte, katero omrežje uporablja aplikacija

V kodi je preizkus zmogljivosti štiri vrstice. Zahtevajte ACCESS_NETWORK_STATE v manifestu, nato primerjajte zmogljivosti aktivnega omrežja s transportom VPN:

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 namerno ozek: ali je privzeto omrežje aplikacije trenutno VPN. Ne pove ničesar o tem, kateri VPN je, ali je vaš, ali tunel doseže drugi konec. true obravnavajte kot »promet odhaja skozi nek tunel« in svoje knjigovodstvo vodite ločeno. Logična vrednost, ki tiho pomeni tri stvari hkrati, je razlog, da zaslon z nastavitvami poroča VPN kot izklopljen, medtem ko uporabnik brska skozi njega.

Ko nasprotno potrebujete vedeti, ali je prisotno kakšno omrežje VPN, ponavljajte čez cm.getAllNetworks() in preizkusite vsak nabor zmogljivosti. Odgovora se razhajata v napravi, kjer teče VPN za posamezne aplikacije, ki pokriva druge aplikacije, ne pa vaše, in to razhajanje je vir večine poročil o napakah, ki pravijo, da aplikacija ne vidi očitno vklopljenega VPN.

Spremljajte spremembe namesto anketiranja

Anketiranje s časovnikom je za to vprašanje napačna oblika, ker spremembe stanja prihajajo kot dogodki. Registrirajte zahtevo, ki ustreza transportu VPN, in pustite ogrodju, da vam pove, kdaj se ujemajoče omrežje pojavi, izgine ali spremeni:

NetworkRequest request = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();
cm.registerNetworkCallback(request, callback);

Omrežje VPN nikoli ne prenaša NET_CAPABILITY_NOT_VPN, zato izrecna odstranitev dokumentira namen. Brez nje se zahteva še vedno ujema, a naslednja oseba, ki bere kodo, mora to ugotoviti sama.

Registracija pripada obsegu, ki potrebuje opazovanje, običajno zaslonu ali storitvi v ospredju. Odjavite v onStop ali onDestroy in ne puščajte povratnega klica pripetega na aktivnost, ki je že ni več. Aplikacija brez vidne komponente je slab gostitelj za stalnega opazovalca, pozabljeni povratni klici pa so tisti, ki se sesujejo po spremembi konfiguracije.

Tri vprašanja držite ločeno

Večina zmede na tem področju izhaja iz tega, da tri različna preverjanja obravnavamo kot eno. Uporabljajo različne API-je in vsako je lahko resnično, medtem ko so druga napačna.

QuestionHow you answer itWhat a yes means
Did the user grant VPN consent to my app?VpnService.prepare() returns nullConsent exists; the tunnel may still be down
Is the device sending default traffic through a VPN?TRANSPORT_VPN on the active networkSomething is tunneling, not necessarily your app
Did this request arrive through the VPN?Source address seen by the serverProof for that connection, nothing wider

Tretja vrstica je tista, na katero ljudje poskušajo odgovoriti v odjemalcu, in tam odgovoriti ni mogoče. Odjemalcem VPN dajte svoj nabor naslovov in berite izvorni naslov na strežniku; to je edini dokaz, ki preživi menjavo tunela sredi seje.

Vrstni red preverjanja, ki izključi lažno pozitivne rezultate

Starejša vzorčna koda, ki še kroži, kliče getNetworkInfo(ConnectivityManager.TYPE_VPN). Ta klic je zastarel in je v več izdajah vrnil null tudi, ko je bil VPN povezan, zato je toliko kopiranih odlomkov videti pokvarjenih v napravi, ki deluje. Preizkusi zmogljivosti se izognejo tej pasti in se obnašajo enako od Androida 5.0 dalje. Če vzdržujete kodno zbirko, ki ga še vedno kliče, zamenjajte klic, namesto da ga ovijete v rezervno pot, saj je prav rezervna pot ustvarila napačen odgovor.

Ena bližnjica si zasluži opozorilo. Primerjanje javnega naslova, ki ga poroča zunanja storitev, z izhodnim naslovom VPN nekaj dokaže o poti, a vsakemu preverjanju doda zahtevo tretje strani, je odvisno od tega, da storitev ostane dostopna, in je lahko predpomnilnik ali blokirano. Obdržite jo kot ročno diagnostiko, nikoli kot preverjanje stanja znotraj aplikacije.

Izvedite preverjanja v tem vrstnem redu in ustavite se pri prvem, ki se ne ujema s poročilom uporabnika: seznam transportov iz lupine, zmogljivosti aktivnega omrežja, dogodki povratnega klica med vklapljanjem in izklapljanjem tunela, nato izvorni naslov na strani strežnika. Če se vsi štirje ujemajo in se aplikacija še vedno obnaša napačno, je hrošč v vašem lastnem stroju stanj, ne v Androidovem pogledu na omrežje.

Sorodni članki