Nej, en VpnService-app kan ikke tænde telefonens VPN
Appen ejer én tunnel og dens livscyklus, ikke telefonens VPN-kontakt. Samtykke kan trækkes tilbage, kun én VPN kører ad gangen, og pakkerne er dine at parse.
Den forkerte antagelse er, at en VpnService-app slår en systemkontakt om, og at telefonen derefter er beskyttet. Det, der faktisk sker, er snævrere: appen beder om samtykke, bygger et interface, læser og skriver pakker gennem en filbeskrivelse og giver interfacet tilbage, når den stopper. Der kan kun være én sådan tunnel levende ad gangen, og systemet kan tage den uden at spørge. Det meste af den forvirrende adfærd, folk giver protokollen skylden for, kommer fra den ejerskabsmodel.
Behandl hver livscyklusovergang som en del af designet og ikke som et randtilfælde.
Myte: appen tænder telefonens VPN
Forløbet begynder med VpnService.prepare(), som returnerer et Intent, brugeren skal acceptere, og fortsætter med establish(), som derefter stadig kan fejle af grunde, appen ikke styrer. Oprettelse af en anden tunnel deaktiverer det første interface og erstatter systemnotifikationen. Ingen steder i det forløb ejer appen en kontakt for hele enheden; den ejer den beskrivelse, den har fået, og de pakker, der lander på den. Selv den vedvarende notifikation tilhører systemet og ikke appens design.
Fordi interfacet gives i stedet for at blive erobret, kan en anden klient tage pladsen når som helst. Appen får det at vide gennem et callback, ikke gennem en returværdi.
Tvangslukning af appen ses i notifikationspanelet af samme grund: systemet rapporterer, hvad det har givet, ikke hvad appen har valgt at vise.
Myte: samtykke gives én gang
Når systemet overdrager tunnelen til en anden klient, sender det en tilbagekaldelsestransaktion på den binder, som basisklassen returnerer fra onBind(). En app, der erstatter den binder med sin egen, skal selv videresende tilbagekaldelsestransaktionen, ellers udløses VpnService.onRevoke() aldrig, og tjenesten bliver ved med at skrive til en beskrivelse, der ikke findes længere. Godkendelsen kan også trækkes tilbage af brugeren, og systemet kan stoppe tjenesten under en genstart eller en altid-til-genopkobling.
At håndtere det er konkret arbejde og ikke en loglinje: stop med at læse beskrivelsen, luk den, ryd forbundet-flaget og fortæl brugeren, hvilken tilstand den er i. Beslut derefter, om appen skal genoprette, spørge igen eller vente, for i en altid-til-konfiguration kan systemet selv bringe tjenesten tilbage.
En app, der behandler tilbagekaldelse som en undtagelse og ikke som en tilstand, vil rapportere forbundet, mens hver pakke, den skriver, kasseres, og det er den værste fejltilstand for et privatlivsværktøj.
Myte: interfacet filtrerer trafik for dig
Det, der ankommer, er rå IP. At opsnappe eller filtrere betyder at parse IP-, TCP-, UDP- og IPv6-headere, følge flows og skrive svar tilbage til beskrivelsen, hvilket gør en opsnapningsapp til en lille netværksstak i stedet for en systemfacilitet. protect() findes, så appens egen transportsocket omgår sin tunnel i stedet for at opsnappe sig selv, og den returnerer en boolsk værdi, der fortjener et tjek på hver socket, der oprettes, efter tunnelen er oppe.
Byggerens lister over tilladte og forbudte applikationer afgør, hvis trafik der kommer ind i interfacet. De giver ikke adgang til nyttelast ud over de IP-pakker, interfacet bærer, og ingen API blotlægger en anden klients tunnel eller lister systemets VPN-profiler. Netværksfunktioner kan sige, at et netværk er VPN-transport, men ikke hvis tunnel det er.
Derfor får statusspørgsmål om andre apps modstridende svar: informationen bliver simpelthen ikke eksponeret til din proces.
Hvad livscyklussen faktisk kræver
Følg de tilstande, du styrer: forberedt, oprettet, tilbagekaldt, stoppet. Genopkobling bør være nedrivning efterfulgt af oprettelse, med én ejer af beskrivelsen og transportsocketten, fordi to komponenter, der kappes om at oprette, vil slås om den ene ledige plads. Baggrundsstarter er begrænsede på moderne Android, så et boot-broadcast er ikke nogen garanti; brug en forgrundstjeneste med dens notifikation, hvor API-niveauet tillader det.
Log overgangen og ikke kun sluttilstanden, og hold tilstandsmaskinen lille nok til at teste hver vej. En tilstand, du ikke kan nå i en test, er en tilstand, du møder i marken uden en log.
Test livscyklussen på en enhed
Emulatorer kan efterligne forbindelse, men ikke samtykke- og tilbagekaldelsesforløbet overbevisende, så kør disse på en rigtig enhed med en anden VPN-klient installeret til at stjæle pladsen.
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
Start tunnelen, bekræft notifikationen og interfacet, og tilslut derefter den anden klient og se, om tilbagekaldelses-callbacket kører, og om tilstanden ryddes. Tvangsluk appen, og kontroller, om noget stadig rapporterer en levende beskrivelse.
Gentag sekvensen efter en altid-til-genstart, fordi den vej adskiller sig fra en brugerstartet forbindelse. Det, du leder efter, er ikke et grønt ikon, men bevis for, at appen holdt op med at bruge en beskrivelse, den ikke længere ejede.