Sie können Android nicht fragen, ob eine VPN verbunden ist. VpnService stellt keinen Getter für den Verbindungszustand bereit, und das Schlüsselsymbol in der Statusleiste zeichnet das System, nicht etwas, das Ihre App lesen kann. Was Sie tun können, ist dem Konnektivitäts-Stack eine engere Frage zu stellen: Welches Netzwerk nutzt der Datenverkehr dieser App, und trägt dieses Netzwerk den VPN-Transport. Dieses Runbook geht die Prüfungen in der Reihenfolge durch, die falsche Positivmeldungen vermeidet, von einem Shell-Befehl auf dem Gerät bis zum Callback, den Sie in der App laufen lassen.

Prüfen Sie das Gerät aus einer Shell, bevor Sie App-Code anfassen

Holen Sie sich zuerst eine Grundlinie. adb shell dumpsys connectivity gibt jedes Netzwerk aus, das das System kennt, und jeder Block listet seine Transporte auf, sodass ein VPN-Netzwerk mit VPN in dieser Liste erscheint. Lesen Sie den Block des Standardnetzwerks vor allem anderen und suchen Sie dann nach anderen Netzwerken, die denselben Transport tragen.

Zwei Ausgaben beantworten unterschiedliche Fragen. Ob ein VPN-Netzwerk existiert, ist nicht dasselbe wie ob Ihre App es nutzt, und auf einem Gerät mit Arbeitsprofil oder zweitem Benutzer kann beides gleichzeitig zutreffen, während Ihr Prozess direkt mit dem Internet spricht. Die Zeile des aktiven Netzwerks entscheidet, was Ihre Sockets tun.

Fragen Sie nach dem Netzwerk, das die App nutzt

Im Code besteht der Fähigkeitstest aus vier Zeilen. Fordern Sie ACCESS_NETWORK_STATE im Manifest an und vergleichen Sie dann die Fähigkeiten des aktiven Netzwerks mit dem VPN-Transport:

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);

Die Antwort ist bewusst eng: Ist das Standardnetzwerk der App gerade jetzt eine VPN. Sie sagt nichts darüber, welche VPN es ist, ob sie Ihnen gehört oder ob der Tunnel sein fernes Ende erreicht. Behandeln Sie true als „Datenverkehr verlässt das Gerät durch irgendeinen Tunnel“ und halten Sie Ihre eigene Buchführung getrennt. Ein Boolean, der stillschweigend drei Dinge zugleich bedeutet, führt dazu, dass ein Einstellungsbildschirm die VPN als aus meldet, während der Nutzer durch sie hindurch surft.

Wenn Sie stattdessen wissen müssen, ob überhaupt ein VPN-Netzwerk vorhanden ist, iterieren Sie über cm.getAllNetworks() und prüfen Sie jedes Fähigkeitsset. Die beiden Antworten gehen auf einem Gerät auseinander, das eine VPN pro App ausführt, die andere Anwendungen abdeckt, Ihre aber nicht, und diese Abweichung ist die Quelle der meisten Fehlerberichte, die sagen, die App sehe eine offensichtlich aktive VPN nicht.

Beobachten Sie Änderungen statt zu pollen

Polling per Timer ist die falsche Form für diese Frage, weil Zustandsänderungen als Ereignisse eintreffen. Registrieren Sie eine Anfrage, die zum VPN-Transport passt, und lassen Sie das Framework Ihnen sagen, wann ein passendes Netzwerk erscheint, verschwindet oder sich ändert:

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

Ein VPN-Netzwerk trägt niemals NET_CAPABILITY_NOT_VPN, daher dokumentiert das ausdrückliche Entfernen die Absicht. Ohne es passt die Anfrage weiterhin, aber die nächste Person, die den Code liest, muss sich das selbst erarbeiten.

Die Registrierung gehört zu dem Bereich, der die Beobachtung braucht, meist einem Bildschirm oder einem Vordergrunddienst. Melden Sie in onStop oder onDestroy ab und lassen Sie kein Callback an einer Activity hängen, die schon weg ist. Eine App ohne sichtbare Komponente ist ein schlechter Gastgeber für einen dauerhaften Beobachter, und die Callbacks, die Sie vergessen, sind die, die nach einer Konfigurationsänderung abstürzen.

Halten Sie drei Fragen getrennt

Der meiste Wirrwarr in diesem Bereich entsteht daraus, drei verschiedene Prüfungen als eine zu behandeln. Sie nutzen unterschiedliche APIs, und jede kann zutreffen, während die anderen falsch sind.

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

Die dritte Zeile ist die, die Leute auf dem Client zu beantworten versuchen, und dort ist sie nicht zu beantworten. Geben Sie VPN-Clients einen eigenen Adresspool und lesen Sie die Quelladresse auf dem Server; das ist der einzige Beweis, der überlebt, wenn ein Nutzer mitten in der Sitzung den Tunnel wechselt.

Prüfreihenfolge, die falsche Positivmeldungen ausschließt

Älterer Beispielcode, der noch im Umlauf ist, ruft getNetworkInfo(ConnectivityManager.TYPE_VPN) auf. Dieser Aufruf ist veraltet, und in mehreren Versionen gab er null zurück, selbst wenn eine VPN verbunden war, weshalb so viele kopierte Schnipsel auf einem funktionierenden Gerät kaputt aussehen. Fähigkeitstests vermeiden die Falle und verhalten sich ab Android 5.0 gleich. Wenn Sie eine Codebasis pflegen, die ihn noch aufruft, ersetzen Sie den Aufruf, statt ihn in einen Fallback zu wickeln, denn der Fallback hat die falsche Antwort ursprünglich erzeugt.

Eine Abkürzung verdient eine Warnung. Die öffentliche Adresse, die ein externer Dienst meldet, mit der VPN-Ausgangsadresse zu vergleichen, beweist zwar etwas über den Pfad, fügt aber jeder Prüfung eine Anfrage an Dritte hinzu, hängt davon ab, dass dieser Dienst erreichbar bleibt, und kann zwischengespeichert oder blockiert werden. Halten Sie es als manuelle Diagnose, niemals als Zustandsprüfung in der App.

Führen Sie die Prüfungen in dieser Reihenfolge aus und stoppen Sie bei der ersten, die dem Bericht des Nutzers widerspricht: Transportliste in der Shell, Fähigkeiten des aktiven Netzwerks, Callback-Ereignisse beim Umschalten des Tunnels, dann die Quelladresse auf der Serverseite. Wenn alle vier übereinstimmen und die App sich weiterhin falsch verhält, liegt der Fehler in Ihrer eigenen Zustandsmaschine und nicht in Androids Sicht auf das Netzwerk.

Ähnliche Artikel