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.

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

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.

Kapcsolódó cikkek