Nu, o aplicație VpnService nu poate porni VPN-ul telefonului
Aplicația deține un singur tunel și ciclul lui de viață, nu întrerupătorul VPN al telefonului. Consimțământul poate fi retras, rulează un singur VPN o dată, iar pachetele rămân ale dumneavoastră de analizat.
Presupunerea greșită este că o aplicație VpnService acționează un întrerupător de sistem și că, de atunci, telefonul este protejat. Ce se întâmplă de fapt este mai îngust: aplicația cere consimțământ, construiește o interfață, citește și scrie pachete printr-un descriptor de fișier și returnează interfața când se oprește. Un singur astfel de tunel poate fi viu o dată, iar sistemul îl poate lua fără să întrebe. Cea mai mare parte a comportamentului confuz pe care oamenii îl pun pe seama protocolului vine din acel model de proprietate.
Tratați fiecare tranziție a ciclului de viață ca parte a proiectului și nu ca un caz limită.
Mit: aplicația pornește VPN-ul telefonului
Fluxul începe cu VpnService.prepare(), care returnează un Intent pe care utilizatorul trebuie să îl accepte, și continuă cu establish(), care poate eșua în continuare din motive pe care aplicația nu le controlează. Stabilirea unui al doilea tunel dezactivează prima interfață și înlocuiește notificarea de sistem. Nicăieri în acea secvență aplicația nu deține un întrerupător pentru întregul dispozitiv; deține descriptorul care i-a fost acordat și pachetele care ajung pe el. Chiar și notificarea persistentă aparține sistemului, nu proiectului aplicației.
Deoarece interfața este acordată și nu capturată, un alt client poate ocupa locul oricând. Aplicația află asta printr-un apel invers, nu printr-o valoare returnată.
Oprirea forțată a aplicației se vede în panoul de notificări din același motiv: sistemul raportează ce a acordat, nu ce a ales aplicația să afișeze.
Mit: consimțământul se dă o singură dată
Când sistemul predă tunelul altui client, trimite o tranzacție de revocare pe binderul pe care clasa de bază îl returnează din onBind(). O aplicație care înlocuiește acel binder cu al ei trebuie să transmită ea însăși tranzacția de revocare, altfel VpnService.onRevoke() nu se declanșează niciodată și serviciul continuă să scrie într-un descriptor care nu mai există. Aprobarea poate fi retrasă și de utilizator, iar sistemul poate opri serviciul în timpul unei reporniri sau al unei reconectări mereu activă.
Gestionarea ei este muncă concretă și nu o linie de jurnal: opriți citirea descriptorului, închideți-l, ștergeți indicatorul de conectat și spuneți utilizatorului în ce stare se află. Apoi decideți dacă aplicația trebuie să restabilească, să întrebe din nou sau să aștepte, pentru că într-o configurație mereu activă sistemul poate readuce serviciul de unul singur.
O aplicație care tratează revocarea ca excepție și nu ca stare va raporta conectat în timp ce fiecare pachet pe care îl scrie este aruncat, iar acesta este cel mai rău mod de eșec posibil pentru un instrument de confidențialitate.
Mit: interfața filtrează traficul în locul dumneavoastră
Ce sosește este IP brut. Capturarea sau filtrarea înseamnă analiza antetelor IP, TCP, UDP și IPv6, urmărirea fluxurilor și scrierea răspunsurilor înapoi în descriptor, ceea ce face dintr-o aplicație de captură un mic stiv de rețea și nu o facilitate de sistem. protect() există pentru ca socketul de transport al aplicației să ocolească tunelul ei în loc să se captureze pe sine, și returnează o valoare booleană care merită verificată pe fiecare socket creat după ce tunelul este ridicat.
Listele de aplicații permise și interzise din constructor decid al cui trafic intră în interfață. Ele nu dau acces la payload dincolo de pachetele IP pe care interfața le transportă, și niciun API nu expune tunelul altui client sau listează profilurile VPN ale sistemului. Capacitățile de rețea pot spune că o rețea este transport VPN, dar nu al cui este tunelul.
De aceea întrebările despre starea altor aplicații primesc răspunsuri contradictorii: informația pur și simplu nu este expusă procesului dumneavoastră.
Ce cere de fapt ciclul de viață
Urmăriți stările pe care le controlați: pregătit, stabilit, revocat, oprit. Reconectarea ar trebui să fie demontare urmată de stabilire, cu un singur proprietar al descriptorului și al socketului de transport, pentru că două componente care concurează să stabilească se vor certa pe singurul loc disponibil. Pornirile în fundal sunt restricționate pe Android modern, așa că o difuzare la boot nu este o garanție; folosiți un serviciu de prim-plan cu notificarea lui acolo unde nivelul API permite.
Înregistrați tranziția și nu doar starea finală, și țineți mașina de stări suficient de mică încât să testați fiecare cale. O stare la care nu puteți ajunge într-un test este o stare pe care o veți întâlni în teren fără jurnal.
Testați ciclul de viață pe un dispozitiv
Emulatoarele pot falsifica conectivitatea, dar nu convingător calea de consimțământ și revocare, așa că rulați aceste teste pe un dispozitiv real cu un al doilea client VPN instalat care să fure locul.
adb shell dumpsys connectivity | grep -i -A3 vpn
adb shell dumpsys activity services | grep -i -A2 vpnservice
adb logcat -s VpnService:V AndroidRuntime:E
adb shell am force-stop com.example.vpnapp
Porniți tunelul, confirmați notificarea și interfața, apoi conectați celălalt client și urmăriți dacă apelul invers de revocare rulează și dacă starea se curăță. Opriți forțat aplicația și verificați dacă ceva mai raportează un descriptor viu.
Repetați secvența după o repornire mereu activă, pentru că acea cale diferă de o conexiune inițiată de utilizator. Nu căutați o pictogramă verde, ci dovada că aplicația a încetat să folosească un descriptor pe care nu îl mai deținea.