Не, апликација VpnService не може да укључи VPN телефона
Апликација поседује један тунел и његов животни циклус, а не прекидач VPN-а на телефону. Сагласност се може повући, истовремено ради само један VPN, а пакете ви рашчлањујете.
Погрешна претпоставка је да апликација VpnService окрене системски прекидач и да је телефон од тада заштићен. Оно што се стварно догађа уже је: апликација тражи сагласност, гради интерфејс, чита и пише пакете кроз дескриптор датотеке и враћа интерфејс када се заустави. Истовремено може бити жив само један такав тунел, а систем га може одузети без питања. Већина збуњујућег понашања које људи приписују протоколу долази управо из тог модела власништва.
Третирајте сваки прелаз животног циклуса као део дизајна, а не као рубни случај.
Мит: апликација укључује VPN телефона
Ток почиње са VpnService.prepare(), који враћа Intent који корисник мора да прихвати, и наставља се са establish(), који након тога и даље може пасти због разлога које апликација не контролише. Успостављање другог тунела деактивира први интерфејс и замењује системску обавест. Нигде у том низу апликација не поседује прекидач на нивоу уређаја; поседује дескриптор који јој је додељен и пакете који на њега слете. Чак и трајна обавест припада систему, а не дизајну апликације.
Пошто се интерфејс додељује, а не отима, други клијент може заузети место у сваком тренутку. Апликација то сазнаје кроз повратни позив, а не кроз повратну вредност.
Присилно заустављање апликације видљиво је у панелу обавести из истог разлога: систем извештава шта је доделио, а не шта је апликација одлучила да прикаже.
Мит: сагласност се даје једном
Када систем преда тунел другом клијенту, шаље трансакцију опозива на binder који базна класа враћа из onBind(). Апликација која тај binder замени својим мора сама да проследи трансакцију опозива, иначе VpnService.onRevoke() никада неће бити позван и сервис наставља да пише у дескриптор који више не постоји. Одобрење корисник такође може да повуче, а систем може да заустави сервис током рестарта или поновног повезивања у режиму стално укљученог.
Бавити се тиме је конкретан посао, а не линија у логу: престаните да читате дескриптор, затворите га, очистите заставицу повезаности и реците кориснику у којем је стању. Затим одлучите да ли апликација треба поново да успостави, поново да пита или да чека, јер у конфигурацији стално укљученог систем може сам да врати сервис.
Апликација која опозив третира као изузетак, а не као стање, извештаваће да је повезана док се сваки пакет који запише одбацује, а то је најгори могући начин отказа за алат за приватност.
Мит: интерфејс филтрира саобраћај уместо вас
Оно што стиже је сирови IP. Хватање или филтрирање значи рашчлањивање IP, TCP, UDP и IPv6 заглавља, праћење токова и писање одговора натраг у дескриптор, што апликацију за хватање чини малим мрежним стеком, а не системском могућношћу. protect() постоји да би сопствени транспортни socket апликације заобишао њен тунел уместо да хвата сам себе, и враћа логичку вредност коју треба проверити на сваком socket-у створеном након што је тунел подигнут.
Листе дозвољених и забрањених апликација у градитељу одлучују чији саобраћај улази у интерфејс. Оне не дају приступ садржају изван IP пакета које интерфејс носи, и ниједан API не открива тунел другог клијента нити наводи системске VPN профиле. Мрежне могућности могу рећи да је нека мрежа VPN транспорт, али не и чији је тунел.
Зато питања о стању других апликација добијају противречне одговоре: информација једноставно није изложена вашем процесу.
Шта животни циклус заиста захтева
Пратите стања која контролишете: припремљено, успостављено, опозвано, заустављено. Поновно повезивање треба да буде растављање па успостављање, са једним власником дескриптора и транспортног socket-а, јер ће се две компоненте које се утркују да успоставе везу борити за једно доступно место. Покретања у позадини су ограничена на модерном 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
Покрените тунел, потврдите обавест и интерфејс, затим повежите други клијент и гледајте да ли се повратни позив опозива извршава и да ли се стање чисти. Присилно зауставите апликацију и проверите да ли нешто још извештава о живом дескриптору.
Поновите след након рестарта у режиму стално укљученог, јер се тај пут разликује од кориснички покренутог повезивања. Оно што тражите није зелена икона, него доказ да је апликација престала да користи дескриптор који више није поседовала.