Appen din kallar VpnService.prepare, får null attende, byggjer ein tunnel, og deskriptoren ser gyldig ut, men ingenting når fram til tenaren. Eller prepare returnerer ein Intent, brukaren godkjenner han, og establish sviktar framleis. Begge utfalla tyder vanlegvis det same: ein annan app eig alt VPN-grensesnittet for denne brukaren, og protect-kalla dine markerer sockets som tunnelen din ikkje kan bruke. Gå gjennom kontrollane nedanfor i rekkjefølgje og stopp ved den første som sviktar, fordi eit seinare steg ikkje kan reparere eit tidlegare.

Sjekk VPN-plassen før du rører protect

Android gjev kvar brukar nøyaktig eitt aktivt VPN-grensesnitt. Det er ein plattformregel, ikkje ei løyve appen din kan be om, så to klientar som båe vil vere tunnelen kan ikkje eksistere samstundes.

Ein alltid på VPN sett opp for ein annan app er det vanskelegaste tilfellet, fordi systemet held den appen tilkopla og avviser ei overtaking. Eit trafikkfilter som AdGuard, ein bedriftsklient eller ein forbrukar-VPN kan halde plassen medan brukaren har gløymt at han er installert. Be brukaren opne Innstillingar og lese oppføringa som står under VPN; namnet der er eigaren du konkurrerer med. Til den oppføringa er kopla frå eller innstillinga alltid på er sletta, kan ikkje VpnService bli grensesnittet.

Steg 1: les kva prepare returnerer

VpnService.prepare svarar på eitt spørsmål: kan denne appen bli VPN-en? Han returnerer null når samtykke alt finst, og ein Intent når brukaren framleis må godkjenne han, som du må starte med startActivityForResult. Handsame ein null-retur som løyve til å halde fram, ikkje som prov på at establish vil lukkast.

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

Viss Intent-vegen kjem attende og brukaren avslo, vil prepare halde fram med å returnere ein Intent ved neste forsøk. Logg resultatkoden i staden for å prøve igjen i ei løkke, fordi eit avslått samtykke er ei brukaravgjerd, ikkje ein forbigåande feil.

Steg 2: stadfest at grensesnittet høyrer til appen din

Etter establish er ParcelFileDescriptor du mottek provet ditt på eigarskap, og det er verdt å sjekke før noko socket-arbeid. Ein deskriptor som er null eller alt lukka tyder at grensesnittet aldri vart oppretta, sjølv om ingen unntak dukka opp. Samanlikn grensesnittnamnet frå buildaren med det systemet rapporterer for den aktive VPN-en, ved hjelp av dumpsys på ei testande eining. Viss dei er ulike, er tunnelen du bygde ikkje tunnelen som ber trafikk.

adb shell dumpsys connectivity

Den same uoverensstemminga forklarer eit tilkopla ikon med død trafikk. Systemet viser tunnelen til den andre appen, medan koden din held ein deskriptor for eit grensesnitt han ikkje eig.

Steg 3: kall protect før connect

Når grensesnittet finst, blir sockets appen din opnar ruta inn i tunnelen som standard. Den ytre transporten kan ikkje følgje den regelen, elles ville den innkapsla pakken gå attende i grensesnittet, difor VpnService protect markerer ein socket for å omgå VPN-en og bruke det underliggjande nettverket. Kallet returnerer ein boolean, og å ignorere han er måten du endar opp med ein socket som ser sunn ut og ikkje ber noko.

Kall protect før connect eller bind, aldri etter. Vernet må vere på plass før socketen blir kopla til eller bunden, fordi ein tilkopla socket alt har valt ruta si, og ein seinare protect flyttar han ikkje påliteleg. Viss protect returnerer false, stopp og sjekk eigarskapet på nytt: han sviktar når grensesnittet ikkje høyrer til appen din, som er nøyaktig tilstanden ein annan VPN etterlèt. Ein false-retur er eit signal om å gå attende til Steg 1, ikkje om å prøve same socket igjen.

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

Steg 4: bind ein prosess når ein socket er vanskeleg

Nokre transportar er ikkje éin socket: eit bibliotek kan opne fleire, eller du vil kanskje at kvar tilkopling i ein hjelpeprosess skal bruke same utgang. ConnectivityManager.bindProcessToNetwork bind prosessen til eit valt nettverk, og den eldre setProcessDefaultNetwork gjorde det same før han vart utdatert. Be om den underliggjande tilkoplinga med NET_CAPABILITY_NOT_VPN slik at bindinga vel nettverket under tunnelen din og ikkje sjølve tunnelen.

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

Å binde ein prosess er ikkje ein andre VPN. Det vel ein utgang for den prosessen, noko som er nyttig for transporten din og ubrukeleg for å køyre to tunnelar samstundes.

Fiks eininga til brukaren, ikkje kodevegen

Det finst ingen API som lèt to apper dele VPN-plassen, så rettinga høyrer til tilstanden til eininga. Tilby brukaren to konkrete vegar: kople frå eller slett alltid på for den andre appen slik at prepare kan ta over, eller opne innstillingane per app for den appen og utelat appen din frå tunnelen hans. Unntaket held filteret i gang for alt anna og lèt appen din bruke den vanlege tilkoplinga, som ofte er resultatet brukaren faktisk ønskjer.

Skriv begge vegane i støttedokumentasjonen din, fordi brukarar sjeldan veit kva app som eig VPN-en. Be dei sende namnet på den aktive VPN-en frå Innstillingar og resultata av prepare, establish og protect frå loggane dine, og du kan seie innan eitt minutt kva steg som svikta. Stadfest deretter med ekte trafikk: eit namneoppslag, ei TCP-tilkopling og éi førespurnad gjennom tunnelen, ikkje berre eit tilkopla ikon. Ein gyldig deskriptor og ein grøn indikator provar i beste fall eigarskap, aldri at transporten overlevde rutereglane.

Viss alle fire kontrollane består og trafikken framleis døyr, er eigarskapet ikkje lenger problemet ditt. Gå vidare til ruting og tenartilgjenge, og test dei same påloggingsdetaljane frå ei eining utan VPN-app installert.

Relaterte artiklar