Ne, VpnService aplikacija ne može uključiti VPN telefona
Aplikacija posjeduje jedan tunel i njegov životni ciklus, a ne prekidač VPN-a na telefonu. Privola se može povući, istovremeno radi samo jedan VPN, a pakete vi raščlanjujete.
Pogrešna pretpostavka je da VpnService aplikacija okrene sistemski prekidač i da je telefon od tada zaštićen. Ono što se stvarno događa uže je: aplikacija traži privolu, gradi sučelje, čita i piše pakete kroz deskriptor datoteke i vraća sučelje kada se zaustavi. Istovremeno može biti živ samo jedan takav tunel, a sustav ga može oduzeti bez pitanja. Većina zbunjujućeg ponašanja koje ljudi pripisuju protokolu dolazi upravo iz tog modela vlasništva.
Tretirajte svaki prijelaz životnog ciklusa kao dio dizajna, a ne kao rubni slučaj.
Mit: aplikacija uključuje VPN telefona
Tok počinje s VpnService.prepare(), koji vraća Intent koji korisnik mora prihvatiti, i nastavlja se s establish(), koji nakon toga još može pasti zbog razloga koje aplikacija ne kontrolira. Uspostavljanje drugog tunela deaktivira prvo sučelje i zamjenjuje sistemsku obavijest. Nigdje u tom nizu aplikacija ne posjeduje prekidač na razini uređaja; posjeduje deskriptor koji joj je dodijeljen i pakete koji na njega slijeću. Čak i trajna obavijest pripada sustavu, a ne dizajnu aplikacije.
Budući da se sučelje dodjeljuje, a ne otima, drugi klijent može zauzeti mjesto u svakom trenutku. Aplikacija to saznaje kroz povratni poziv, a ne kroz povratnu vrijednost.
Prisilno zaustavljanje aplikacije vidljivo je u ploči obavijesti iz istog razloga: sustav izvještava što je dodijelio, a ne što je aplikacija odlučila prikazati.
Mit: privola se daje jednom
Kada sustav preda tunel drugom klijentu, šalje transakciju opoziva na binder koji osnovna klasa vraća iz onBind(). Aplikacija koja taj binder zamijeni svojim mora sama proslijediti transakciju opoziva, inače VpnService.onRevoke() nikada neće biti pozvan i servis nastavlja pisati u deskriptor koji više ne postoji. Odobrenje može povući i korisnik, a sustav može zaustaviti servis tijekom restarta ili ponovnog povezivanja u načinu stalno uključenog.
Baviti se time konkretan je posao, a ne linija u logu: prestanite čitati deskriptor, zatvorite ga, očistite zastavicu povezanosti i recite korisniku u kojem se stanju nalazi. Zatim odlučite treba li aplikacija ponovno uspostaviti, ponovno pitati ili čekati, jer u konfiguraciji stalno uključenog sustav može sam vratiti servis.
Aplikacija koja opoziv tretira kao iznimku, a ne kao stanje, izvještavat će da je povezana dok se svaki paket koji zapiše odbacuje, a to je najgori mogući način kvara za alat za privatnost.
Mit: sučelje filtrira promet umjesto vas
Ono što stiže jest sirovi IP. Hvatanje ili filtriranje znači raščlanjivanje IP, TCP, UDP i IPv6 zaglavlja, praćenje tokova i pisanje odgovora natrag u deskriptor, što aplikaciju za hvatanje čini malim mrežnim stogom, a ne sistemskom mogućnošću. protect() postoji da bi vlastiti transportni socket aplikacije zaobišao njezin tunel umjesto da hvata sam sebe, i vraća logičku vrijednost koju treba provjeriti na svakom socketu stvorenom nakon što je tunel podignut.
Popisi dopuštenih i zabranjenih aplikacija u graditelju odlučuju čiji promet ulazi u sučelje. Oni ne daju pristup sadržaju izvan IP paketa koje sučelje nosi, i nijedan API ne otkriva tunel drugog klijenta niti navodi sistemske VPN profile. Mrežne mogućnosti mogu reći da je neka mreža VPN transport, ali ne i čiji je tunel.
Zato pitanja o stanju drugih aplikacija dobivaju proturječne odgovore: informacija jednostavno nije izložena vašem procesu.
Što životni ciklus zapravo zahtijeva
Pratite stanja koja kontrolirate: pripremljeno, uspostavljeno, opozvano, zaustavljeno. Ponovno povezivanje treba biti rastavljanje pa uspostavljanje, s jednim vlasnikom deskriptora i transportnog socketa, jer će se dvije komponente koje se utrkuju da uspostave vezu boriti za jedno dostupno mjesto. Pozadinska pokretanja ograničena su na modernom Androidu, pa emitiranje pri podizanju nije garancija; koristite uslugu u prvom planu s njezinom obavijesti gdje razina API-ja to dopušta.
Bilježite prijelaz, a ne samo konačno stanje, i držite stroj stanja dovoljno malim da testirate svaki put. Stanje u koje ne možete doći u testu je stanje koje ćete sresti na terenu bez loga.
Testirajte životni ciklus na uređaju
Emulatori mogu lažirati povezanost, ali ne i put privole i opoziva uvjerljivo, pa ovo pokrenite na stvarnom uređaju s instaliranim drugim VPN klijentom koji će zauzeti mjesto.
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
Pokrenite tunel, potvrdite obavijest i sučelje, zatim povežite drugi klijent i gledajte izvršava li se povratni poziv opoziva i čisti li se stanje. Prisilno zaustavite aplikaciju i provjerite izvještava li nešto još o živom deskriptoru.
Ponovite slijed nakon restarta u načinu stalno uključenog, jer se taj put razlikuje od korisnički pokrenutog povezivanja. Ono što tražite nije zelena ikona, nego dokaz da je aplikacija prestala koristiti deskriptor koji više nije posjedovala.