Погрешната претпоставка е дека апликација VpnService превртува системски прекинувач и од тој момент телефонот е заштитен. Она што навистина се случува е потесно: апликацијата бара согласност, гради интерфејс, чита и пишува пакети преку датотечен дескриптор и го враќа интерфејсот кога ќе запре. Истовремено може да биде жив само еден таков тунел, а системот може да го одземе без да праша. Најголемиот дел од збунувачкото однесување што луѓето му го припишуваат на протоколот доаѓа токму од тој модел на сопственост.

Третирајте го секој премин на животниот циклус како дел од дизајнот, а не како рабен случај.

Мит: апликацијата го вклучува VPN-от на телефонот

Текот започнува со VpnService.prepare(), кој враќа Intent што корисникот мора да го прифати, и продолжува со establish(), кој потоа сѐ уште може да не успее од причини што апликацијата не ги контролира. Поставувањето втор тунел го деактивира првиот интерфејс и го заменува системското известување. Никаде во таа низа апликацијата не поседува прекинувач за целиот уред; таа го поседува дескрипторот што ѝ е даден и пакетите што слетуваат на него. Дури и постојаното известување му припаѓа на системот, а не на дизајнот на апликацијата.

Бидејќи интерфејсот се дава, а не се граба, друг клиент може да го заземе местото во секое време. Апликацијата тоа го дознава преку повратен повик, а не преку повратна вредност.

Присилното запирање на апликацијата се гледа во панелот со известувања од истата причина: системот известува што дал, а не што апликацијата избрала да прикаже.

Мит: согласноста се дава еднаш

Кога системот го предава тунелот на друг клиент, испраќа трансакција за отповикување на binder-от што базната класа го враќа од onBind(). Апликација што го заменува тој binder со свој мора сама да ја проследи трансакцијата за отповикување, инаку VpnService.onRevoke() никогаш не се активира и услугата продолжува да пишува во дескриптор што повеќе не постои. Одобрувањето може да го повлече и корисникот, а системот може да ја запре услугата при рестартирање или при повторно поврзување во режим секогаш вклучен.

Справувањето со тоа е конкретна работа, а не ред во дневник: престанете да го читате дескрипторот, затворете го, исчистете го знамето за поврзаност и кажете му на корисникот во кое состојба се наоѓа. Потоа одлучете дали апликацијата треба повторно да воспостави, повторно да праша или да чека, бидејќи во конфигурација секогаш вклучена системот може сам да ја врати услугата.

Апликација што отповикувањето го третира како исклучок, а не како состојба, ќе известува дека е поврзана додека секој пакет што ќе го напише се отфрла, а тоа е најлошиот можен режим на отказ за алатка за приватност.

Мит: интерфејсот го филтрира сообраќајот наместо вас

Она што пристигнува е суров IP. Фаќањето или филтрирањето значи разложување на IP, TCP, UDP и IPv6 заглавија, следење на текови и пишување одговори назад во дескрипторот, што ја прави апликацијата за фаќање мал мрежен стек, а не системска можност. protect() постои за да може сопствениот транспортен сокет на апликацијата да го заобиколи нејзиниот тунел наместо да се фати себеси, и враќа логичка вредност што заслужува проверка на секој сокет создаден откако тунелот е подигнат.

Списоците на дозволени и забранети апликации во градителот одлучуваат чиј сообраќај влегува во интерфејсот. Тие не даваат пристап до товарот надвор од IP пакетите што ги носи интерфејсот, и ниту еден API не го открива тунелот на друг клиент или ги набројува VPN профилите на системот. Мрежните можности можат да кажат дека мрежата е VPN транспорт, но не и чиј е тунелот.

Затоа прашањата за состојбата на други апликации добиваат противречни одговори: информацијата едноставно не е изложена на вашиот процес.

Што всушност бара животниот циклус

Следете ги состојбите што ги контролирате: подготвена, воспоставена, отповикана, запре. Повторното поврзување треба да биде расклопување по што следи воспоставување, со еден сопственик на дескрипторот и транспортниот сокет, бидејќи две компоненти што се натпреваруваат да воспостават ќе се караат за единственото слободно место. Позадинските стартувања се ограничени на современиот Android, затоа емитувањето при подигање не е гаранција; користете услуга во прв план со нејзиното известување каде што нивото на API го дозволува.

Запишете го преминот, а не само конечната состојба, и држете ја машината на состојби доволно мала за да го тестирате секој пат. Состојба до која не можете да стигнете во тест е состојба што ќе ја сретнете на терен без дневник.

Тестирајте го животниот циклус на уред

Емулаторите можат да излажат поврзаност, но не и патеката на согласноста и отповикувањето убедливо, затоа изведете ги овие на вистински уред со инсталиран втор VPN клиент што ќе го украде местото.

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

Стартувајте го тунелот, потврдете го известувањето и интерфејсот, потоа поврзете го другиот клиент и гледајте дали се извршува повратниот повик за отповикување и дали состојбата се чисти. Присилно запрете ја апликацијата и проверете дали нешто сѐ уште известува за жив дескриптор.

Повторете ја низата по рестартирање во режим секогаш вклучен, бидејќи таа патека се разликува од поврзување што го започнува корисникот. Она што го барате не е зелена икона, туку доказ дека апликацијата престанала да користи дескриптор што повеќе не го поседувала.

Поврзани статии