Et voi kysyä Androidilta, onko VPN yhteydessä. VpnService ei paljasta yhteyden getteriä, ja tilarivin avainkuvakkeen piirtää järjestelmä, ei mikään, mitä sovelluksesi voisi lukea. Sen sijaan voit kysyä yhteyspinolta kapeamman kysymyksen: mitä verkkoa tämän sovelluksen liikenne käyttää ja kuljettaako se verkko VPN-transportia. Tämä opas käy tarkistukset läpi siinä järjestyksessä, joka välttää vääriä positiivisia, laitteen shell-komennosta sovelluksessa käynnissä pitämääsi callbackiin.

Tarkista laite shellistä ennen kuin kosket sovelluksen koodiin

Hanki ensin perustaso. adb shell dumpsys connectivity tulostaa jokaisen verkon, jonka järjestelmä tuntee, ja jokainen lohko luettelee transportinsa, joten VPN-verkko näkyy siinä luettelossa VPN-merkinnällä. Lue oletusverkon lohko ennen kaikkea muuta ja etsi sitten muita verkkoja, jotka kuljettavat samaa transportia.

Kaksi tulostetta vastaa eri kysymyksiin. VPN-verkon olemassaolo ei ole sama asia kuin se, käyttääkö sovelluksesi sitä, ja laitteessa, jossa on työprofiili tai toinen käyttäjä, molemmat voivat olla totta yhtä aikaa, kun prosessisi puhuu internetin kanssa suoraan. Aktiivisen verkon rivi on se, joka päättää, mitä socketisi tekevät.

Kysy verkkoa, jota sovellus käyttää

Koodissa ominaisuustesti on neljä riviä. Pyydä ACCESS_NETWORK_STATE manifestissa ja vertaa sitten aktiivisen verkon ominaisuuksia VPN-transportiin:

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);

Vastaus on tarkoituksella kapea: onko sovelluksen oletusverkko juuri nyt VPN. Se ei kerro, mikä VPN on kyseessä, kuuluuko se sinulle vai saavuttaako tunneli kauemman päänsä. Kohtele true-arvoa merkityksessä “liikenne lähtee jonkin tunnelin kautta” ja pidä oma kirjanpitosi erillään. Boolean, joka hiljaa tarkoittaa kolmea asiaa yhtä aikaa, on syy siihen, että asetusnäyttö lopulta ilmoittaa VPN:n olevan pois päältä, kun käyttäjä selaa sen kautta.

Kun sen sijaan tarvitset tietää, onko mitään VPN-verkkoa olemassa, iteroi cm.getAllNetworks() ja testaa jokainen ominaisuusjoukko. Vastaukset eroavat laitteessa, joka ajaa sovelluskohtaista VPN:ää, joka kattaa muut sovellukset mutta ei sinun, ja tämä ero on lähteensä useimmille virheraporteille, jotka sanovat, ettei sovellus näe VPN:ää, joka on selvästi päällä.

Tarkkaile muutoksia sen sijaan, että kyselisit

Ajastimella kysely on väärä muoto tähän kysymykseen, koska tilamuutokset saapuvat tapahtumina. Rekisteröi pyyntö, joka vastaa VPN-transportia, ja anna kehyksen kertoa sinulle, milloin vastaava verkko ilmestyy, katoaa tai muuttuu:

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

VPN-verkko ei koskaan kuljeta NET_CAPABILITY_NOT_VPN-merkintää, joten selkeä poisto dokumentoi tarkoituksen. Ilman sitä pyyntö vastaa edelleen, mutta seuraavan koodia lukevan on pääteltävä se itse.

Rekisteröinti kuuluu sille laajuudelle, joka havainnointia tarvitsee, yleensä näytölle tai etualan palvelulle. Poista rekisteröinti kohdassa onStop tai onDestroy, äläkä jätä callbackia aktiviteettiin, joka on jo poissa. Sovellus ilman näkyvää komponenttia on huono isäntä pysyvälle tarkkailijalle, ja ne callbackit, jotka unohdat, ovat niitä, jotka kaatuvat kokoonpanomuutoksen jälkeen.

Pidä kolme kysymystä erillään

Suurin osa tämän alueen sekaannuksesta tulee siitä, että kolmea eri tarkistusta kohdellaan yhtenä. Ne käyttävät eri API:ja, ja jokainen voi olla totta, kun muut ovat vääriä.

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

Kolmas rivi on se, johon ihmiset yrittävät vastata asiakkaassa, eikä siihen voi vastata siellä. Anna VPN-asiakkaille oma osoiteavaruus ja lue lähdeosoite palvelimella; se on ainoa todiste, joka kestää sen, että käyttäjä vaihtaa tunnelia kesken istunnon.

Tarkistusjärjestys, joka sulkee pois väärät positiiviset

Vanhempi esimerkkikoodi, joka on yhä kierrossa, kutsuu getNetworkInfo(ConnectivityManager.TYPE_VPN). Tämä kutsu on vanhentunut, ja useissa julkaisuissa se palautti null, vaikka VPN oli yhteydessä, minkä vuoksi niin monet kopioidut pätkät näyttävät rikkinäisiltä laitteessa, joka toimii. Ominaisuustestit välttävät ansan ja käyttäytyvät samoin Android 5.0:sta lähtien. Jos ylläpidät koodikantaa, joka kutsuu sitä yhä, korvaa kutsu sen sijaan, että käärisit sen varavaihtoehtoon, sillä varavaihtoehto tuotti väärän vastauksen alun perin.

Yksi oikotie ansaitsee varoituksen. Ulkoisen palvelun ilmoittaman julkisen osoitteen vertaaminen VPN:n lähtöosoitteeseen todistaa kyllä jotain reitistä, mutta se lisää kolmannen osapuolen pyynnön jokaiseen tarkistukseen, riippuu siitä, että palvelu pysyy pystyssä, ja se voidaan välimuistittaa tai estää. Pidä sitä käsikäyttöisenä diagnoosina, älä koskaan sovelluksen sisäisenä tilatarkistuksena.

Suorita tarkistukset tässä järjestyksessä ja pysähdy ensimmäiseen, joka on ristiriidassa käyttäjän raportin kanssa: transport-luettelo shellissä, aktiivisen verkon ominaisuudet, callback-tapahtumat, kun vaihdat tunnelia, ja lopuksi lähdeosoite palvelimen puolella. Jos kaikki neljä täsmäävät ja sovellus käyttäytyy yhä huonosti, vika on omassa tilakoneessasi, ei Androidin näkemyksessä verkosta.

Aiheeseen liittyvät artikkelit