Tinatawag ng app mo ang VpnService.prepare, nakakatanggap ng null, gumagawa ng tunnel, at mukhang valid ang descriptor, pero walang nakakarating sa server. O kaya ay nagbabalik ang prepare ng Intent, inaprubahan ito ng user, at nabibigo pa rin ang establish. Karaniwang pareho ang ibig sabihin ng dalawang resulta: may app nang nagmamay-ari ng VPN interface para sa user na ito, at minamarkahan ng mga protect call mo ang mga socket na hindi magagamit ng tunnel mo. Dumaan sa mga pagsusuri sa ibaba nang sunod-sunod at huminto sa unang nabibigo, dahil hindi kayang ayusin ng susunod na hakbang ang nauna.

Siguraduhin ang puwesto ng VPN bago hawakan ang protect

Binibigyan ng Android ang bawat user ng eksaktong isang aktibong VPN interface. Ito ay tuntunin ng platform, hindi pahintulot na mahihiling ng app mo, kaya dalawang client na parehong gustong maging tunnel ay hindi maaaring magkasabay.

Ang laging aktibong VPN na itinakda para sa ibang app ang pinakamahirap na kaso, dahil pinananatiling konektado ng system ang app na iyon at tinatanggihan ang pagkuha ng kontrol. Ang filter ng trapiko tulad ng AdGuard, corporate client o consumer VPN ay maaaring humawak sa puwesto habang nakalimutan ng user na naka-install ito. Hilingin sa user na buksan ang Settings at basahin ang entry na nakalista sa ilalim ng VPN; ang pangalan doon ang may-ari na kinakalaban mo. Hangga't hindi nadidiskonekta ang entry na iyon o nabura ang setting na laging aktibo, hindi maaaring maging interface ang VpnService mo.

Hakbang 1: basahin ang ibinabalik ng prepare

Sinasagot ng VpnService.prepare ang isang tanong: maaari bang maging VPN ang app na ito? Nagbabalik ito ng null kapag may pahintulot na, at Intent kapag kailangan pa itong aprubahan ng user, na dapat mong ilunsad gamit ang startActivityForResult. Ituring ang pagbabalik ng null bilang pahintulot na magpatuloy, hindi bilang patunay na magtatagumpay ang establish.

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

Kung bumalik ang daan ng Intent at tumanggi ang user, patuloy na magbabalik ng Intent ang prepare sa susunod na pagtatangka. I-log ang result code sa halip na ulitin sa loop, dahil desisyon ng user ang tinanggihang pahintulot, hindi pansamantalang error.

Hakbang 2: tiyakin na pag-aari ng app mo ang interface

Pagkatapos ng establish, ang ParcelFileDescriptor na natatanggap mo ang patunay ng pagmamay-ari, at sulit itong suriin bago ang anumang gawain sa socket. Ang descriptor na null o sarado na ay nangangahulugang hindi kailanman nagawa ang interface, kahit walang exception na lumitaw. Ihambing ang pangalan ng interface mula sa builder sa inirereport ng system para sa aktibong VPN, gamit ang dumpsys sa test device. Kung magkaiba, ang tunnel na ginawa mo ay hindi ang tunnel na nagdadala ng trapiko.

adb shell dumpsys connectivity

Ang parehong pagkakaiba ang nagpapaliwanag ng konektadong icon na patay ang trapiko. Ipinapakita ng system ang tunnel ng ibang app, habang hawak ng code mo ang descriptor ng interface na hindi mo pag-aari.

Hakbang 3: tawagin ang protect bago ang connect

Kapag umiiral ang interface, ang mga socket na binubuksan ng app mo ay iruruta sa tunnel bilang default. Hindi masusunod ng panlabas na transport ang tuntuning iyon kung hindi ay babalik sa interface ang naka-encapsulate na packet, kaya VpnService protect ay nagmamarka ng socket para laktawan ang VPN at gamitin ang nasa ilalim na network. Nagbabalik ng boolean ang tawag, at ang balewalain ito ang paraan para mapunta ka sa socket na mukhang malusog at walang dinadala.

Tawagin ang protect bago ang connect o bind, hindi kailanman pagkatapos. Dapat nasa puwesto ang proteksyon bago ikonekta o itali ang socket, dahil ang konektadong socket ay pumili na ng ruta nito at hindi ito gagalawin nang mapagkakatiwalaan ng protect na darating mamaya. Kung nagbabalik ng false ang protect, huminto at suriin muli ang pagmamay-ari: nabibigo ito kapag hindi pag-aari ng app mo ang interface, at iyon mismo ang kalagayang iniiwan ng ibang VPN. Ang pagbabalik ng false ay hudyat na bumalik sa Hakbang 1, hindi para ulitin ang parehong socket.

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

Hakbang 4: itali ang isang proseso kapag mahirap ang socket

Ang ilang transport ay hindi isang socket: maaaring magbukas ng marami ang isang library, o gusto mong gamitin ng bawat koneksyon sa helper process ang parehong labasan. Itinatali ng ConnectivityManager.bindProcessToNetwork ang proseso sa piniling network, at ginawa rin ito ng mas lumang setProcessDefaultNetwork bago ito na-deprecate. Hilingin ang nasa ilalim na koneksyon gamit ang NET_CAPABILITY_NOT_VPN upang piliin ng pagtali ang network sa ilalim ng tunnel mo at hindi ang tunnel mismo.

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

Ang pagtali ng proseso ay hindi pangalawang VPN. Pumipili ito ng labasan para sa prosesong iyon, na kapaki-pakinabang sa transport mo at walang silbi sa pagpapatakbo ng dalawang tunnel nang sabay.

Ayusin ang device ng user, hindi ang daan ng code

Walang API na nagpapahintulot sa dalawang app na magbahagi ng puwesto ng VPN, kaya kabilang sa kalagayan ng device ang pag-aayos. Mag-alok sa user ng dalawang konkretong daan: idiskonekta o burahin ang laging aktibo ng ibang app upang makapasok ang prepare mo, o buksan ang settings kada app ng app na iyon at ibukod ang app mo sa tunnel nito. Pinapanatili ng pagbubukod na tumatakbo ang filter para sa lahat ng iba at hinahayaan ang app mo na gamitin ang ordinaryong koneksyon, na kadalasan ang resulta na talagang gusto ng user.

Isulat ang dalawang daan sa dokumentasyon ng suporta, dahil bihirang malaman ng mga user kung aling app ang may-ari ng VPN. Hilingin sa kanila na ipadala ang pangalan ng aktibong VPN mula sa Settings at ang mga resulta ng prepare, establish at protect mula sa mga log mo, at sa isang minuto masasabi mo kung aling hakbang ang nabigo. Pagkatapos i-verify gamit ang tunay na trapiko: paghahanap ng pangalan, koneksyong TCP at isang kahilingan sa tunnel, hindi lamang konektadong icon. Ang valid na descriptor at berdeng tagapagpahiwatig ay nagpapatunay ng pagmamay-ari sa pinakamabuti, hindi kailanman na nakaligtas ang transport sa mga tuntunin ng routing.

Kung pumasa ang apat na pagsusuri at namamatay pa rin ang trapiko, hindi na problema mo ang pagmamay-ari. Lumipat sa routing at abot ng server, at subukan ang parehong kredensyal mula sa device na walang naka-install na app na VPN.

Kaugnay na mga artikulo