Hogyan ellenőrizhető kódban, hogy csatlakozik-e VPN az Androidon
Az Androidnak nincs gettere a VPN-kapcsolathoz. Kérdezze le a hálózati képességeket, figyelje a transport változásait, és tartsa külön az alkalmazás hozzájárulását, az eszköz állapotát és a szerveroldali bizonyítékot.
Nem kérdezheti meg az Androidtól, hogy csatlakozik-e VPN. A VpnService nem tesz közzé kapcsolat-gettert, a státuszsorban lévő kulcsikont pedig a rendszer rajzolja, nem valami, amit az alkalmazása olvashatna. Amit tehet, hogy a kapcsolódási vermet egy szűkebb kérdéssel állítja elő: melyik hálózatot használja az alkalmazás forgalma, és hordozza-e az a hálózat a VPN transportot. Ez az útmutató abban a sorrendben járja végig az ellenőrzéseket, amely elkerüli a téves pozitívokat, az eszközön futó shell parancstól az alkalmazásban futva hagyott callbackig.
Ellenőrizze a készüléket shellből, mielőtt hozzányúlna az alkalmazás kódjához
Először szerezzen egy alapvonalat. A adb shell dumpsys connectivity kiírja az összes hálózatot, amelyet a rendszer ismer, és minden blokk felsorolja a transportjait, így egy VPN hálózat VPN jelöléssel jelenik meg abban a listában. Mindenek előtt olvassa el az alapértelmezett hálózat blokkját, majd keressen más hálózatokat, amelyek ugyanazt a transportot hordozzák.
Két kimenet két különböző kérdésre válaszol. Az, hogy létezik VPN hálózat, nem ugyanaz, mint hogy az alkalmazása használja-e, és egy munkaprofillal vagy második felhasználóval rendelkező eszközön mindkettő igaz lehet egyszerre, miközben a folyamata közvetlenül beszél az internettel. Az aktív hálózat sor az, amely eldönti, mit tesznek a socketjei.
Kérdezze meg az alkalmazás által használt hálózatot
A kódban a képességteszt négy sor. Kérje a ACCESS_NETWORK_STATE-et a manifestben, majd hasonlítsa össze az aktív hálózat képességeit a VPN transporttal:
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);
A válasz szándékosan szűk: az alkalmazás alapértelmezett hálózata most VPN-e. Nem mond semmit arról, melyik VPN, az öné-e, vagy eléri-e az alagút a távoli végét. Kezelje a true értéket úgy, hogy „a forgalom valamilyen alagúton keresztül távozik”, és tartsa külön a saját nyilvántartását. Egy boolean, amely csendesen három dolgot jelent egyszerre, az az oka, hogy egy beállítási képernyő végül kikapcsoltként jelenti a VPN-t, miközben a felhasználó azon keresztül böngészik.
Amikor viszont azt kell tudnia, hogy létezik-e bármilyen VPN hálózat, iteráljon a cm.getAllNetworks() fölött, és tesztelje az egyes képességkészleteket. A két válasz eltér egy olyan eszközön, amely alkalmazásonkénti VPN-t futtat, amely más alkalmazásokat lefed, de az önét nem, és ez az eltérés a forrása a legtöbb hiba jelentésnek, amely szerint az alkalmazás nem lát egy nyilvánvalóan bekapcsolt VPN-t.
Figyelje a változásokat a lekérdezgetés helyett
Az időzítőn végzett lekérdezgetés rossz forma ehhez a kérdéshez, mert az állapotváltozások eseményként érkeznek. Regisztráljon egy kérést, amely megfelel a VPN transportnak, és hagyja, hogy a keretrendszer megmondja, mikor jelenik meg, tűnik el vagy változik meg egy megfelelő hálózat:
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(request, callback);
Egy VPN hálózat soha nem hordoz NET_CAPABILITY_NOT_VPN-et, ezért az explicit eltávolítás dokumentálja a szándékot. Nélküle a kérés továbbra is illeszkedik, de a következő embernek, aki olvassa a kódot, magának kell ezt kikövetkeztetnie.
A regisztráció ahhoz a hatókörhöz tartozik, amelynek szüksége van a megfigyelésre, általában egy képernyőhöz vagy egy előtér-szolgáltatáshoz. Szüntesse meg a regisztrációt a onStop vagy a onDestroy metódusban, és ne hagyjon callbacket egy már nem létező activityn. A látható komponens nélküli alkalmazás rossz gazda egy állandó megfigyelő számára, és az elfelejtett callbackek azok, amelyek konfigurációváltozás után összeomlanak.
Tartsa külön a három kérdést
A legtöbb zavar ezen a területen abból ered, hogy három különböző ellenőrzést egyként kezelnek. Különböző API-kat használnak, és mindegyik igaz lehet, miközben a többi hamis.
| 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 |
A harmadik sor az, amelyre az emberek a kliensen próbálnak válaszolni, és ott nem lehet rá válaszolni. Adjon a VPN klienseknek saját címkészletet, és olvassa a forráscímet a szerveren; ez az egyetlen bizonyíték, amely túléli, ha a felhasználó a munkamenet közepén alagutat vált.
Ellenőrzési sorrend, amely kizárja a téves pozitívokat
Egy régebbi mintakód, amely még forgalomban van, a getNetworkInfo(ConnectivityManager.TYPE_VPN)-et hívja. Ez a hívás elavult, és több kiadásban null-t adott vissza akkor is, amikor VPN volt csatlakozva, ezért tűnik olyan sok másolt részlet töröttnek egy működő eszközön. A képességellenőrzések elkerülik a csapdát, és az Android 5.0-tól kezdve ugyanúgy viselkednek. Ha olyan kódbázist tart fenn, amely még hívja, cserélje le a hívást ahelyett, hogy tartalék útvonalba csomagolná, mert a tartalék útvonal volt az, ami a rossz választ eredetileg előállította.
Egy rövidítés figyelmeztetést érdemel. A külső szolgáltatás által jelentett nyilvános cím összehasonlítása a VPN kilépési címével valóban bizonyít valamit az útvonalról, de minden ellenőrzéshez hozzáad egy harmadik félhez intézett kérést, attól függ, hogy az a szolgáltatás elérhető marad-e, és gyorsítótárazható vagy blokkolható. Tartsa kézi diagnosztikaként, soha ne az alkalmazáson belüli állapotellenőrzésként.
Futtassa az ellenőrzéseket ebben a sorrendben, és álljon meg az elsőnél, amely nem egyezik a felhasználó jelentésével: transportlista a shellben, az aktív hálózat képességei, callback események az alagút ki- és bekapcsolása közben, majd a forráscím a szerver oldalán. Ha mind a négy egyezik, és az alkalmazás továbbra is rosszul viselkedik, a hiba a saját állapotgépében van, nem az Android hálózatképében.