Nemôžete sa Androida opýtať, či je VPN pripojená. VpnService neposkytuje getter stavu pripojenia a ikonu kľúča v stavovom riadku kreslí systém, nie niečo, čo by vaša aplikácia mohla prečítať. Môžete sa však opýtať spojovej vrstvy užšiu otázku: ktorú sieť používa prevádzka tejto aplikácie a prenáša táto sieť transport VPN. Táto príručka prechádza kontrolami v poradí, ktoré vylučuje falošné pozitívy, od príkazu shell v zariadení až po callback, ktorý necháte bežať v aplikácii.

Skontrolujte zariadenie z shellu, skôr než sa dotknete kódu aplikácie

Najprv získajte základný stav. adb shell dumpsys connectivity vypíše každú sieť, ktorú systém pozná, a každý blok uvádza svoje transporty, takže sieť VPN sa v tom zozname objaví s VPN. Najprv prečítajte blok predvolenej siete, potom hľadajte iné siete, ktoré prenášajú rovnaký transport.

Dva výstupy odpovedajú na rôzne otázky. Existencia siete VPN nie je to isté ako to, či ju vaša aplikácia používa, a v zariadení s pracovným profilom alebo druhým používateľom môžu byť obe pravdivé súčasne, kým váš proces komunikuje s internetom priamo. Práve riadok aktívnej siete rozhoduje o tom, čo robia vaše sockety.

Opýtajte sa na sieť, ktorú aplikácia používa

V kóde je test možností štyri riadky. Požiadajte o ACCESS_NETWORK_STATE v manifeste a potom porovnajte možnosti aktívnej siete 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);

Odpoveď je zámerne úzka: je predvolená sieť aplikácie práve teraz VPN. Nehovorí nič o tom, ktorá VPN to je, či je vaša, alebo či tunel dosiahne druhý koniec. true berte ako „prevádzka odchádza cez nejaký tunel“ a svoju vlastnú evidenciu veďte oddelene. Logická hodnota, ktorá potichu znamená tri veci naraz, je dôvod, prečo obrazovka nastavení hlási VPN ako vypnutú, kým používateľ cez ňu prehliada.

Keď naopak potrebujete vedieť, či existuje nejaká sieť VPN, iterujte cez cm.getAllNetworks() a otestujte každú množinu možností. Obe odpovede sa rozchádzajú v zariadení, kde beží VPN pre jednotlivé aplikácie, ktorá pokrýva iné aplikácie, ale nie vašu, a práve tento rozchod je zdrojom väčšiny hlásení o chybách, ktoré tvrdia, že aplikácia nevidí zjavne zapnutú VPN.

Sledujte zmeny namiesto dopytovania

Dopytovanie s časovačom je pre túto otázku nesprávny tvar, pretože zmeny stavu prichádzajú ako udalosti. Zaregistrujte požiadavku, ktorá zodpovedá transportu VPN, a nechajte rámec, aby vám povedal, keď sa zodpovedajúca sieť objaví, zmizne alebo zmení:

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

Sieť VPN nikdy neprenáša NET_CAPABILITY_NOT_VPN, takže výslovné odstránenie dokumentuje zámer. Bez neho požiadavka stále sedí, ale ďalší človek čítajúci kód na to musí prísť sám.

Registrácia patrí rozsahu, ktorý pozorovanie potrebuje, zvyčajne obrazovke alebo službe v popredí. Odregistrujte v onStop alebo onDestroy a nenechávajte callback pripevnený k aktivite, ktorá už zmizla. Aplikácia bez viditeľnej komponenty je zlý hostiteľ pre trvalého pozorovateľa a zabudnuté callbacky sú tie, ktoré padnú po zmene konfigurácie.

Držte tri otázky oddelene

Väčšina zmätku v tejto oblasti pochádza z toho, že sa tri rôzne kontroly považujú za jednu. Používajú rôzne API a každá môže byť pravdivá, kým ostatné sú nepravdivé.

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

Tretí riadok je ten, na ktorý sa ľudia snažia odpovedať v klientovi, a tam sa odpovedať nedá. Dajte klientom VPN vlastný fond adries a čítajte zdrojovú adresu na serveri; je to jediný dôkaz, ktorý prežije zmenu tunela používateľom uprostred relácie.

Poradie overenia, ktoré vylučuje falošné pozitívy

Starší vzorový kód, ktorý ešte koluje, volá getNetworkInfo(ConnectivityManager.TYPE_VPN). To volanie je zastarané a vo viacerých vydaniach vracalo null aj pri pripojenej VPN, a preto toľko skopírovaných útržkov vyzerá pokazených v zariadení, ktoré funguje. Kontroly možností obchádzajú tú pascu a správajú sa rovnako od Androidu 5.0. Ak udržiavate kódovú základňu, ktorá ho ešte volá, nahraďte volanie namiesto toho, aby ste ho zabalili do záložnej vetvy, pretože práve záložná vetva vytvorila nesprávnu odpoveď.

Jedna skratka si zaslúži varovanie. Porovnanie verejnej adresy hlásenej externou službou s výstupnou adresou VPN niečo dokazuje o ceste, ale pridáva požiadavku tretej strany do každej kontroly, závisí od toho, že tá služba zostane dostupná, a môže byť vo vyrovnávacej pamäti alebo zablokovaná. Držte ju ako manuálnu diagnostiku, nikdy ako kontrolu stavu vnútri aplikácie.

Vykonajte kontroly v tomto poradí a zastavte pri prvej, ktorá sa nezhoduje s hlásením používateľa: zoznam transportov zo shellu, možnosti aktívnej siete, udalosti callbacku pri zapínaní a vypínaní tunela, potom zdrojová adresa na strane servera. Ak všetky štyri sedia a aplikácia sa stále správa nesprávne, chyba je vo vašom vlastnom stavebnom automatu, nie v pohľade Androidu na sieť.

Súvisiace články