Nu poți întreba Android dacă un VPN este conectat. VpnService nu expune niciun getter pentru starea conexiunii, iar pictograma cu cheie din bara de stare este desenată de sistem, nu de ceva ce aplicația ta poate citi. Ce poți face este să pui stivei de conectivitate o întrebare mai îngustă: ce rețea folosește traficul acestei aplicații și dacă acea rețea transportă transportul VPN. Acest ghid parcurge verificările în ordinea care evită pozitivele false, de la o comandă shell pe dispozitiv până la callbackul pe care îl lași să ruleze în aplicație.

Verifică dispozitivul dintr-un shell înainte de a atinge codul aplicației

Obține mai întâi o bază. adb shell dumpsys connectivity afișează fiecare rețea pe care o cunoaște sistemul, iar fiecare bloc își listează transporturile, așa că o rețea VPN apare cu VPN în acea listă. Citește blocul rețelei implicite înainte de orice altceva, apoi caută alte rețele care transportă același transport.

Două ieșiri răspund la întrebări diferite. Faptul că există o rețea VPN nu este același lucru cu faptul că aplicația ta o folosește, iar pe un dispozitiv cu profil de lucru sau al doilea utilizator ambele pot fi adevărate simultan în timp ce procesul tău vorbește direct cu internetul. Linia rețelei active este cea care decide ce fac socketurile tale.

Întreabă ce rețea folosește aplicația

În cod testul capacităților are patru linii. Solicită ACCESS_NETWORK_STATE în manifest, apoi compară capacitățile rețelei active cu transportul 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);

Răspunsul este intenționat îngust: rețeaua implicită a aplicației este un VPN chiar acum. Nu spune nimic despre care VPN, dacă este al tău sau dacă tunelul ajunge la celălalt capăt. Tratează un true ca „traficul iese printr-un tunel oarecare” și ține evidența ta separată. Un boolean care înseamnă în tăcere trei lucruri simultan este motivul pentru care un ecran de setări raportează VPN-ul ca oprit în timp ce utilizatorul navighează prin el.

Când, dimpotrivă, trebuie să știi dacă există vreo rețea VPN, iterează cm.getAllNetworks() și testează fiecare set de capacități. Cele două răspunsuri diverg pe un dispozitiv care rulează un VPN per aplicație ce acoperă alte aplicații dar nu pe a ta, iar acea divergență este sursa majorității rapoartelor de eroare care spun că aplicația nu vede un VPN evident pornit.

Urmărește schimbările în loc să interoghezi periodic

Interogarea periodică cu un timer este forma greșită pentru această întrebare, pentru că schimbările de stare sosesc ca evenimente. Înregistrează o cerere care corespunde transportului VPN și lasă frameworkul să îți spună când o rețea corespunzătoare apare, dispare sau se schimbă:

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

O rețea VPN nu transportă niciodată NET_CAPABILITY_NOT_VPN, așa că eliminarea explicită documentează intenția. Fără ea cererea tot corespunde, dar următoarea persoană care citește codul trebuie să își dea seama singură.

Înregistrarea aparține domeniului care are nevoie de observație, de obicei un ecran sau un serviciu din prim-plan. Anulează înregistrarea în onStop sau onDestroy și nu lăsa un callback atașat de o activitate care a dispărut deja. O aplicație fără componente vizibile este o gazdă slabă pentru un observator permanent, iar callbackurile uitate sunt cele care se prăbușesc după o schimbare de configurație.

Ține trei întrebări separate

Cea mai mare parte a confuziei din acest domeniu vine din tratarea a trei verificări diferite ca una singură. Ele folosesc API-uri diferite și fiecare poate fi adevărată în timp ce celelalte sunt false.

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

Al treilea rând este cel pe care oamenii încearcă să îl răspundă pe client și nu poate fi răspuns acolo. Dă clienților VPN propriul pool de adrese și citește adresa sursă pe server; este singura dovadă care supraviețuiește unui utilizator care schimbă tunelul la mijlocul sesiunii.

Ordinea de verificare care elimină pozitivele false

Codul de exemplu mai vechi încă în circulație apelează getNetworkInfo(ConnectivityManager.TYPE_VPN). Acel apel este depreciat și, în mai multe versiuni, returna null chiar și cu un VPN conectat, motiv pentru care atât de multe fragmente copiate par stricate pe un dispozitiv care funcționează. Verificările de capacități ocolesc capcana și se comportă la fel de la Android 5.0 încoace. Dacă întreții o bază de cod care îl mai apelează, înlocuiește apelul în loc să îl înfășori într-un fallback, pentru că fallbackul este cel care a produs răspunsul greșit de la început.

O scurtătură merită un avertisment. Compararea adresei publice raportate de un serviciu extern cu adresa de ieșire a VPN-ului dovedește ceva despre cale, dar adaugă o cerere către terți la fiecare verificare, depinde de acel serviciu să rămână disponibil și poate fi pusă în cache sau blocată. Ține-o ca diagnostic manual, niciodată ca verificare de stare în interiorul aplicației.

Rulează verificările în această ordine și oprește-te la prima care nu se acordă cu raportul utilizatorului: lista transporturilor din shell, capacitățile rețelei active, evenimentele de callback în timp ce pornești și oprești tunelul, apoi adresa sursă din partea serverului. Dacă toate patru se aliniază și aplicația încă se comportă greșit, eroarea este în propria ta mașină de stări, nu în viziunea Android despre rețea.

Articole similare