Teie rakendus kutsub VpnService.prepare, saab null, ehitab tunneli, ja kirjeldaja tundub kehtiv, kuid serverini ei jõua midagi. Või prepare tagastab Intent’i, kasutaja kinnitab selle ja establish ebaõnnestub ikkagi. Mõlemad tulemused tähendavad tavaliselt sama: mõni teine rakendus omab juba selle kasutaja VPN-liidest ja teie protect-kutsed märgistavad sokette, mida teie tunnel kasutada ei saa. Käige allolevad kontrollid järjekorras läbi ja peatuge esimesel, mis ebaõnnestub, sest hilisem samm ei saa varasemat parandada.

Kontrollige VPN-kohta enne, kui protect’i puutute

Android annab igale kasutajale täpselt ühe aktiivse VPN-liidese. See on platvormi reegel, mitte luba, mida teie rakendus saab taotleda, seega kaks klienti, kes mõlemad tahavad tunnel olla, ei saa kõrvuti eksisteerida.

Teise rakenduse jaoks seatud alati sisse lülitatud VPN on kõige raskem juhtum, sest süsteem hoiab seda rakendust ühendatud ja keeldub ülevõtmisest. Liiklusfilter nagu AdGuard, ettevõtteklient või tarbijate VPN võib kohta hoida, samal ajal kui kasutaja on unustanud, et see on paigaldatud. Paluge kasutajal avada Seaded ja lugeda VPN-i all loetletud kirjet; seal olev nimi on omanik, kellega te võistlete. Kuni seda kirjet ei ole lahti ühendatud või alati sisse lülitatud seadet ei ole kustutatud, ei saa teie VpnService liideseks.

Samm 1: lugege, mida prepare tagastab

VpnService.prepare vastab ühele küsimusele: kas see rakendus tohib VPN-iks saada? See tagastab nulli, kui nõusolek on juba olemas, ja Intent’i, kui kasutaja peab selle veel kinnitama, mille peate käivitama funktsiooniga startActivityForResult. Käsitlege nulli tagastust loana jätkata, mitte tõendina, et establish õnnestub.

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

Kui Intent’i tee naaseb ja kasutaja keeldus, jätkab prepare järgmisel katsel Intent’i tagastamist. Logige tulemuse kood, selle asemel et tsüklis uuesti proovida, sest keeldutud nõusolek on kasutaja otsus, mitte mööduv viga.

Samm 2: kinnitage, et liides kuulub teie rakendusele

Pärast establish’i on saadud ParcelFileDescriptor teie omanditõend ja seda tasub kontrollida enne igasugust soketitööd. Kirjeldaja, mis on null või juba suletud, tähendab, et liidest ei loodud kunagi, isegi kui erandit ei ilmnenud. Võrrelge ehitaja liidese nime sellega, mida süsteem aktiivse VPN-i kohta teatab, kasutades testseadmes dumpsys’i. Kui need erinevad, ei ole teie ehitatud tunnel see tunnel, mis liiklust kannab.

adb shell dumpsys connectivity

Sama lahknevus selgitab ühendatud ikooni surnud liiklusega. Süsteem näitab teise rakenduse tunnelit, samal ajal kui teie kood hoiab kirjeldajat liidese kohta, mida see ei oma.

Samm 3: kutsuge protect enne connect’i

Kui liides on olemas, suunatakse teie rakenduse avatud soketid vaikimisi tunnelisse. Väline transport ei saa seda reeglit järgida, muidu pöörduks kapseldatud pakett liidesesse tagasi, seetõttu VpnService protect märgistab soketi VPN-ist möödumiseks ja alusvõrgu kasutamiseks. Kutse tagastab boolean’i ja selle eiramine on viis, kuidas jõuda soketini, mis näeb terve välja ega kanna midagi.

Kutsuge protect enne connect’i või bind’i, mitte kunagi pärast. Kaitse peab olema paigas enne, kui soket ühendatakse või seotakse, sest ühendatud soket on oma marsruudi juba valinud ja hilisem protect ei liiguta seda usaldusväärselt. Kui protect tagastab false, peatuge ja kontrollige omandust uuesti: see ebaõnnestub, kui liides ei kuulu teie rakendusele, mis on täpselt see olek, mille teine VPN maha jätab. False’i tagastus on signaal naasta sammu 1 juurde, mitte sama soketit uuesti proovida.

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

Samm 4: siduge protsess, kui soket on kohmakas

Mõned transpordid ei ole üks soket: teek võib avada mitu või võite soovida, et iga ühendus abiprotsessis kasutaks sama väljapääsu. ConnectivityManager.bindProcessToNetwork seob protsessi valitud võrguga ja vanem setProcessDefaultNetwork tegi sama tööd enne, kui see aegunuks tunnistati. Taotlege alusühendust väärtusega NET_CAPABILITY_NOT_VPN, et sidumine valiks võrgu teie tunneli all, mitte tunneli enda.

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

Protsessi sidumine ei ole teine VPN. See valib selle protsessi jaoks väljapääsu, mis on kasulik teie transpordule ja kasutu kahe tunneli korraga käitamiseks.

Parandage kasutaja seadet, mitte koodi teed

Puudub API, mis lubaks kahel rakendusel VPN-kohta jagada, seega kuulub parandus seadme olekusse. Pakkuge kasutajale kahte konkreetset teed: ühendage lahti või kustutage teise rakenduse alati sisse lülitatud valik, et teie prepare saaks üle võtta, või avage selle rakenduse rakendusepõhised seaded ja välistage oma rakendus selle tunnelist. Välistamine hoiab filtri kõige muu jaoks töös ja lubab teie rakendusel kasutada tavalist ühendust, mis on sageli tulemus, mida kasutaja tegelikult soovib.

Kirjutage mõlemad teed oma tugidokumentatsiooni, sest kasutajad teavad harva, milline rakendus VPN-i omab. Paluge neil saata seadetest aktiivse VPN-i nimi ja oma logidest prepare’i, establish’i ja protect’i tulemused, ja te saate minutiga öelda, milline samm ebaõnnestus. Seejärel kontrollige tegeliku liiklusega: nimepäring, TCP-ühendus ja üks päring läbi tunneli, mitte ainult ühendatud ikoon. Kehtiv kirjeldaja ja roheline indikaator tõestavad parimal juhul omandust, mitte kunagi seda, et transport marsruutimisreeglid üle elas.

Kui kõik neli kontrolli läbivad ja liiklus sureb endiselt, ei ole omandus enam teie probleem. Liikuge edasi marsruutimise ja serveri kättesaadavuse juurde ning testige samu mandaate seadmest, kus VPN-rakendust ei ole paigaldatud.

Seotud artiklid