Како у коду проверити да ли је VPN повезан на Андроиду
Андроид нема getter за стање VPN везе. Питајте могућности мреже, пратите промене транспорта и држите одвојено сагласност апликације, стање уређаја и доказ са сервера.
Не можете питати Андроид да ли је VPN повезан. VpnService не излаже getter стања везе, а икону кључа у статусној траци црта систем, а не нешто што ваша апликација може прочитати. Оно што можете јесте да стеку повезивања поставите уже питање: коју мрежу користи саобраћај ове апликације и преноси ли та мрежа VPN транспорт. Овај водич пролази кроз провере редоследом који избегава лажно позитивне резултате, од shell команде на уређају до callback-а који остављате да ради у апликацији.
Проверите уређај из shell-а пре него што дирнете код апликације
Прво узмите основно стање. adb shell dumpsys connectivity исписује сваку мрежу коју систем зна, а сваки блок наводи своје транспорте, па се VPN мрежа појављује са VPN на том списку. Прво прочитајте блок подразумеване мреже, затим тражите друге мреже које преносе исти транспорт.
Два излаза одговарају на различита питања. Постојање VPN мреже није исто што и то да је ваша апликација користи, а на уређају са радним профилом или другим корисником обоје може бити тачно истовремено док ваш процес разговара директно са интернетом. Линија активне мреже је она која одлучује шта раде ваши сокети.
Питајте коју мрежу апликација користи
У коду је тест могућности четири линије. Затражите ACCESS_NETWORK_STATE у манифесту, а затим упоредите могућности активне мреже са VPN транспортом:
ConnectivityManager cm = (ConnectivityManager)
context.getSystemService(Context.CONNECTIVITY_SERVICE);
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean vpnInUse = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Одговор је намерно узак: да ли је подразумевана мрежа апликације тренутно VPN. Не говори ништа о томе који VPN, да ли је ваш или да ли тунел стиже до другог краја. Третирајте true као „саобраћај излази кроз неки тунел“ и водите своју евиденцију одвојено. Логичка вредност која тихо значи три ствари истовремено разлог је зашто екран подешавања пријављује VPN као искључен док корисник прегледа кроз њега.
Када, насупрот томе, треба да знате постоји ли икаква VPN мрежа, итерирајте cm.getAllNetworks() и тестирајте сваки скуп могућности. Два одговора се разилазе на уређају који покреће VPN за појединачне апликације који покрива друге апликације али не вашу, и то разилажење извор је већине пријава грешака које кажу да апликација не види очигледно укључен VPN.
Пратите промене уместо анкетирања
Анкетирање по тајмеру погрешан је облик за ово питање, јер промене стања долазе као догађаји. Региструјте захтев који одговара VPN транспорту и пустите оквир да вам каже када се одговарајућа мрежа појави, нестане или промени:
NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(request, callback);
VPN мрежа никада не преноси NET_CAPABILITY_NOT_VPN, па експлицитно уклањање документује намеру. Без тога захтев се и даље поклапа, али следећа особа која чита код мора то сама схватити.
Регистрација припада опсегу коме је потребно посматрање, обично екрану или услузи у првом плану. Одјавите у onStop или onDestroy и не остављајте callback прикачен за активност које више нема. Апликација без видљиве компоненте лош је домаћин за сталног посматрача, а заборављени callback-и су они који падају после промене конфигурације.
Држите три питања одвојено
Већина забуне у овој области долази од третирања три различите провере као једне. Оне користе различите API-је и свака може бити тачна док су остале нетачне.
| Question | How you answer it | What a yes means |
|---|---|---|
| Did the user grant VPN consent to my app? | VpnService.prepare() returns null | Consent exists; the tunnel may still be down |
| Is the device sending default traffic through a VPN? | TRANSPORT_VPN on the active network | Something is tunneling, not necessarily your app |
| Did this request arrive through the VPN? | Source address seen by the server | Proof for that connection, nothing wider |
Трећи ред је онај на који људи покушавају да одговоре на клијенту и ту се не може одговорити. Дајте VPN клијентима сопствени базен адреса и читајте изворну адресу на серверу; то је једини доказ који преживи промену тунела усред сесије.
Редослед провере који искључује лажно позитивне резултате
Старији пример кода који још циркулише позива getNetworkInfo(ConnectivityManager.TYPE_VPN). Тај позив је застарео и на неколико издања враћао је null чак и док је VPN био повезан, због чега многи копирани исечци изгледају покварени на уређају који ради. Провере могућности избегавају ту замку и понашају се исто од Андроида 5.0. Ако одржавате базу кода која га још позива, замените позив уместо да га умотате у резервно решење, јер је управо резервно решење произвело погрешан одговор.
Један пречица заслужује упозорење. Поређење јавне адресе коју пријављује спољна услуга са излазном адресом VPN-а доказује нешто о путу, али додаје захтев треће стране у сваку проверу, зависи од тога да та услуга остане доступна и може бити кеширано или блокирано. Држите то као ручну дијагностику, никада као проверу стања унутар апликације.
Извршите провере овим редоследом и зауставите се на првој која се не слаже са извештајем корисника: списак транспорта из shell-а, могућности активне мреже, callback догађаји док укључујете и искључујете тунел, затим изворна адреса на страни сервера. Ако се сва четири слажу и апликација се и даље понаша погрешно, грешка је у вашој сопственој машини стања, а не у Андроидовом виђењу мреже.