Slik sjekker du i kode om en VPN er tilkoblet på Android
Android har ingen getter for VPN-tilkobling. Spør om nettverksfunksjonene, følg med på transportendringer, og hold appens samtykke, enhetstilstand og serverbevis atskilt.
Du kan ikke spørre Android om en VPN er tilkoblet. VpnService eksponerer ingen getter for tilkoblingstilstand, og nøkkelikonet i statuslinjen tegnes av systemet, ikke av noe appen din kan lese. Det du kan gjøre er å stille tilkoblingsstakken et smalere spørsmål: hvilket nettverk bruker trafikken til denne appen, og bærer det nettverket VPN-transporten. Denne veiledningen går gjennom kontrollene i den rekkefølgen som unngår falske positive, fra en shell-kommando på enheten til callbacken du lar kjøre i appen.
Sjekk enheten fra en shell før du rører appkoden
Skaff en baseline først. adb shell dumpsys connectivity skriver ut hvert nettverk systemet kjenner, og hver blokk lister opp transportene sine, så et VPN-nettverk vises med VPN i den listen. Les blokken for standardnettverket før noe annet, og se deretter etter andre nettverk som bærer samme transport.
To utdata svarer på forskjellige spørsmål. Om et VPN-nettverk finnes er ikke det samme som om appen din bruker det, og på en enhet med arbeidsprofil eller en annen bruker kan begge være sanne samtidig mens prosessen din snakker direkte med internett. Linjen for aktivt nettverk er den som avgjør hva socketene dine gjør.
Spør etter nettverket appen bruker
I kode er funksjonstesten fire linjer. Be om ACCESS_NETWORK_STATE i manifestet, og sammenlign deretter det aktive nettverkets funksjoner med VPN-transporten:
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);
Svaret er med vilje smalt: er appens standardnettverk en VPN akkurat nå. Det sier ingenting om hvilken VPN, om den er din, eller om tunnelen når den andre enden. Behandle en true som «trafikk går ut gjennom en eller annen tunnel» og hold din egen bokføring atskilt. En boolsk verdi som stille betyr tre ting samtidig, er grunnen til at en innstillingsskjerm rapporterer VPN som av mens brukeren surfer gjennom den.
Når du i stedet trenger å vite om noe VPN-nettverk er til stede, iterer cm.getAllNetworks() og test hvert funksjonssett. De to svarene spriker på en enhet som kjører en appspesifikk VPN som dekker andre apper men ikke din, og den spriken er kilden til de fleste feilrapportene som sier at appen ikke ser en VPN som tydelig er på.
Følg med på endringer i stedet for å polle
Polling på en timer er feil form for dette spørsmålet, fordi tilstandsendringer kommer som hendelser. Registrer en forespørsel som samsvarer med VPN-transporten, og la rammeverket fortelle deg når et samsvarende nettverk dukker opp, forsvinner eller endres:
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(request, callback);
Et VPN-nettverk bærer aldri NET_CAPABILITY_NOT_VPN, så den eksplisitte fjerningen dokumenterer hensikten. Uten den samsvarer forespørselen fortsatt, men neste person som leser koden må finne det ut selv.
Registreringen hører til omfanget som trenger observasjonen, vanligvis en skjerm eller en forgrunnstjeneste. Avregistrer i onStop eller onDestroy, og ikke la en callback henge på en aktivitet som allerede er borte. En app uten synlige komponenter er en dårlig vert for en permanent observatør, og callbackene du glemmer er de som krasjer etter en konfigurasjonsendring.
Hold tre spørsmål fra hverandre
Mesteparten av forvirringen i dette området kommer av å behandle tre forskjellige kontroller som én. De bruker forskjellige API-er, og hver av dem kan være sann mens de andre er falske.
| 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 |
Den tredje raden er den folk prøver å svare på i klienten, og den kan ikke besvares der. Gi VPN-klienter sin egen adressepool og les kildeadressen på serveren; det er det eneste beviset som overlever at en bruker bytter tunnel midt i en økt.
Verifiseringsrekkefølge som utelukker falske positive
Eldre eksempelkode som fortsatt sirkulerer kaller getNetworkInfo(ConnectivityManager.TYPE_VPN). Det kallet er utdatert, og på flere versjoner returnerte det null selv mens en VPN var tilkoblet, og det er derfor så mange kopierte biter ser ødelagte ut på en enhet som fungerer. Funksjonskontroller unngår fellen og oppfører seg likt fra Android 5.0 og utover. Hvis du vedlikeholder en kodebase som fortsatt kaller det, erstatt kallet i stedet for å pakke det inn i en reserve, fordi reserven er det som produserte det gale svaret i utgangspunktet.
En snarvei fortjener en advarsel. Å sammenligne den offentlige adressen som en ekstern tjeneste rapporterer med VPN-ens utgangsadresse beviser noe om banen, men det legger til en tredjepartsforespørsel i hver kontroll, avhenger av at tjenesten holder seg oppe, og kan bli bufret eller blokkert. Hold det som en manuell diagnose, aldri som tilstandskontrollen inne i appen.
Kjør kontrollene i denne rekkefølgen og stopp ved den første som ikke stemmer med brukerens rapport: transportliste fra shell, funksjonene til aktivt nettverk, callback-hendelser mens du slår tunnelen av og på, deretter kildeadresse på serversiden. Hvis alle fire stemmer og appen fortsatt oppfører seg feil, er feilen i din egen tilstandsmaskin og ikke i Androids syn på nettverket.