Ihre App ruft VpnService.prepare auf, erhält null, baut einen Tunnel, und der Deskriptor sieht gültig aus, doch nichts erreicht den Server. Oder prepare gibt ein Intent zurück, der Benutzer stimmt zu, und establish schlägt trotzdem fehl. Beide Ergebnisse bedeuten meist dasselbe: Eine andere App besitzt die VPN-Schnittstelle für diesen Benutzer bereits, und Ihre protect-Aufrufe markieren Sockets, die Ihr Tunnel nicht nutzen kann. Arbeiten Sie die folgenden Prüfungen der Reihe nach ab und halten Sie bei der ersten an, die fehlschlägt, denn ein späterer Schritt kann einen früheren nicht reparieren.

Prüfen Sie den VPN-Platz, bevor Sie protect anfassen

Android gibt jedem Benutzer genau eine aktive VPN-Schnittstelle. Das ist eine Plattformregel, keine Berechtigung, die Ihre App anfordern kann, daher können zwei Clients, die beide der Tunnel sein wollen, nicht nebeneinander bestehen.

Ein immer aktives VPN, das für eine andere App eingerichtet ist, ist der schwierigste Fall, weil das System diese App verbunden hält und eine Übernahme ablehnt. Ein Traffic-Filter wie AdGuard, ein Unternehmensclient oder ein Verbraucher-VPN kann den Platz halten, während der Benutzer vergessen hat, dass es installiert ist. Bitten Sie den Benutzer, die Einstellungen zu öffnen und den unter VPN aufgeführten Eintrag zu lesen; der Name dort ist der Eigentümer, mit dem Sie konkurrieren. Bis dieser Eintrag getrennt oder die Einstellung für immer aktiv gelöscht ist, kann Ihr VpnService nicht die Schnittstelle werden.

Schritt 1: Lesen Sie, was prepare zurückgibt

VpnService.prepare beantwortet eine einzige Frage: Darf diese App das VPN werden? Es gibt null zurück, wenn die Zustimmung bereits vorliegt, und ein Intent, wenn der Benutzer sie noch erteilen muss, das Sie mit startActivityForResult starten müssen. Behandeln Sie eine null-Rückgabe als Erlaubnis fortzufahren, nicht als Beweis, dass establish gelingt.

Intent intent = VpnService.prepare(context);
if (intent != null) {
    startActivityForResult(intent, REQUEST_VPN);
}

Kehrt der Intent-Pfad zurück und hat der Benutzer abgelehnt, gibt prepare beim nächsten Versuch weiterhin ein Intent zurück. Protokollieren Sie den Ergebniscode, statt in einer Schleife erneut zu versuchen, denn eine abgelehnte Zustimmung ist eine Entscheidung des Benutzers, kein vorübergehender Fehler.

Schritt 2: Bestätigen Sie, dass die Schnittstelle zu Ihrer App gehört

Nach establish ist der ParcelFileDescriptor, den Sie erhalten, Ihr Eigentumsnachweis, und es lohnt sich, ihn vor jeder Socket-Arbeit zu prüfen. Ein Deskriptor, der null ist oder bereits geschlossen wurde, bedeutet, dass die Schnittstelle nie erstellt wurde, selbst wenn keine Ausnahme auftrat. Vergleichen Sie den Schnittstellennamen aus dem Builder mit dem, den das System für das aktive VPN meldet, indem Sie dumpsys auf einem Testgerät verwenden. Weichen sie voneinander ab, ist der von Ihnen gebaute Tunnel nicht der Tunnel, der Traffic trägt.

adb shell dumpsys connectivity

Dieselbe Diskrepanz erklärt ein verbundenes Symbol mit totem Traffic. Das System zeigt den Tunnel der anderen App, während Ihr Code einen Deskriptor für eine Schnittstelle hält, die er nicht besitzt.

Schritt 3: Rufen Sie protect vor connect auf

Sobald die Schnittstelle existiert, werden Sockets, die Ihre App öffnet, standardmäßig in den Tunnel geleitet. Der äußere Transport kann diese Regel nicht befolgen, sonst würde das gekapselte Paket in die Schnittstelle zurückschleifen, deshalb VpnService protect markiert einen Socket, um das VPN zu umgehen und das darunterliegende Netzwerk zu nutzen. Der Aufruf gibt einen boolean zurück, und ihn zu ignorieren ist der Weg zu einem Socket, der gesund aussieht und nichts überträgt.

Rufen Sie protect vor connect oder bind auf, nie danach. Der Schutz muss bestehen, bevor der Socket verbunden oder gebunden wird, denn ein verbundener Socket hat seine Route bereits gewählt, und ein späteres protect verschiebt ihn nicht zuverlässig. Gibt protect false zurück, halten Sie an und prüfen Sie das Eigentum erneut: Es schlägt fehl, wenn die Schnittstelle nicht Ihrer App gehört, und genau diesen Zustand hinterlässt ein anderes VPN. Eine false-Rückgabe ist ein Signal, zu Schritt 1 zurückzukehren, nicht denselben Socket erneut zu versuchen.

DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
    return;
}
socket.connect(serverAddress, serverPort);

Schritt 4: Binden Sie einen Prozess, wenn ein Socket unhandlich ist

Manche Transporte sind kein einzelner Socket: Eine Bibliothek öffnet vielleicht mehrere, oder Sie möchten, dass jede Verbindung in einem Hilfsprozess denselben Ausgang nutzt. ConnectivityManager.bindProcessToNetwork bindet den Prozess an ein gewähltes Netzwerk, und das ältere setProcessDefaultNetwork tat dasselbe, bevor es veraltet ist. Fordern Sie die darunterliegende Verbindung mit NET_CAPABILITY_NOT_VPN an, damit die Bindung das Netzwerk unter Ihrem Tunnel wählt und nicht den Tunnel selbst.

NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();

Einen Prozess zu binden ist kein zweites VPN. Es wählt einen Ausgang für diesen Prozess, was für Ihren Transport nützlich und für den gleichzeitigen Betrieb zweier Tunnel nutzlos ist.

Reparieren Sie das Gerät des Benutzers, nicht den Codepfad

Es gibt keine API, die zwei Apps den VPN-Platz teilen lässt, daher gehört die Lösung zum Gerätezustand. Bieten Sie dem Benutzer zwei konkrete Wege: Trennen Sie das VPN der anderen App oder löschen Sie die Einstellung für immer aktiv, damit Ihr prepare übernehmen kann, oder öffnen Sie die App-Einstellungen jener App und schließen Sie Ihre App von ihrem Tunnel aus. Die Ausnahme hält den Filter für alles andere am Laufen und lässt Ihre App die gewöhnliche Verbindung nutzen, was oft das Ergebnis ist, das der Benutzer tatsächlich will.

Schreiben Sie beide Wege in Ihre Support-Dokumentation, denn Benutzer wissen selten, welche App das VPN besitzt. Bitten Sie sie, den Namen des aktiven VPN aus den Einstellungen und die Ergebnisse von prepare, establish und protect aus Ihren Protokollen zu senden, und Sie können innerhalb einer Minute sagen, welcher Schritt fehlgeschlagen ist. Prüfen Sie dann mit echtem Traffic: eine Namensauflösung, eine TCP-Verbindung und eine Anfrage durch den Tunnel, nicht nur ein verbundenes Symbol. Ein gültiger Deskriptor und eine grüne Anzeige beweisen höchstens Eigentum, nie, dass der Transport die Routingregeln überlebt hat.

Bestehen alle vier Prüfungen und Traffic stirbt trotzdem, ist Eigentum nicht mehr Ihr Problem. Gehen Sie weiter zu Routing und Server-Erreichbarkeit und testen Sie dieselben Zugangsdaten von einem Gerät ohne installierte VPN-App.

Ähnliche Artikel