Ինչպես կոդում ստուգել՝ արդյոք VPN-ը միացած է Android-ում
Android-ում VPN միացման getter չկա։ Հարցրեք ցանցի հնարավորություններին, հետևեք transport-ի փոփոխություններին և առանձին պահեք հավելվածի համաձայնությունը, սարքի վիճակը և սերվերի ապացույցը։
Դուք չեք կարող հարցնել Android-ին՝ արդյոք VPN-ը միացած է։ VpnService-ը չի բացահայտում միացման getter, իսկ կարգավիճակի սանդղակի բանալու պատկերակը նկարում է համակարգը, ոչ թե ինչ-որ բան, որ ձեր հավելվածը կարող է կարդալ։ Ինչ կարող եք անել՝ միացվածության կույտին ավելի նեղ հարց տալն է՝ որ ցանցն է օգտագործում այս հավելվածի թրաֆիկը, և արդյոք այդ ցանցը կրում է VPN transport-ը։ Այս ուղեցույցն անցնում է ստուգումները կեղծ դրական արդյունքներից խուսափող հերթականությամբ՝ սարքի վրա shell հրամանից մինչև հավելվածում աշխատող callback-ը։
Ստուգեք սարքը shell-ից՝ նախքան հավելվածի կոդին դիպչելը
Նախ ստացեք հիմնային գիծ։ adb shell dumpsys connectivity-ը տպում է համակարգին հայտնի յուրաքանչյուր ցանց, և յուրաքանչյուր բլոկ թվարկում է իր transport-ները, այնպես որ VPN ցանցը հայտնվում է այդ ցանկում VPN-ով։ Կարդացեք լռելյայն ցանցի բլոկը ամեն ինչից առաջ, ապա փնտրեք նույն transport-ը կրող այլ ցանցեր։
Երկու ելք պատասխանում են տարբեր հարցերի։ VPN ցանցի գոյությունը նույնը չէ, ինչ ձեր հավելվածի կողմից դրա օգտագործումը, և աշխատանքային պրոֆիլով կամ երկրորդ օգտատիրոջով սարքում երկուսն էլ կարող են ճշմարիտ լինել միաժամանակ, մինչ ձեր պրոցեսը ուղղակիորեն խոսում է ինտերնետի հետ։ Ակտիվ ցանցի տողն է, որ որոշում է, թե ինչ են անում ձեր socket-ները։
Հարցրեք այն ցանցին, որն օգտագործում է հավելվածը
Կոդում հնարավորությունների թեստը չորս տող է։ Հայցեք ACCESS_NETWORK_STATE-ը manifest-ում, ապա համեմատեք ակտիվ ցանցի հնարավորությունները VPN transport-ի հետ։
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-ին որպես «թրաֆիկը դուրս է գալիս ինչ-որ թունելով» և ձեր սեփական հաշվառումը պահեք առանձին։ Boolean-ը, որը լուռ նշանակում է երեք բան միաժամանակ, այն է, ինչի պատճառով կարգավորումների էկրանը ի վերջո հայտնում է VPN-ը որպես անջատված, մինչ օգտատերը զննում է դրա միջով։
Երբ փոխարենը պետք է իմանաք՝ արդյոք որևէ VPN ցանց գոյություն ունի, կրկնեք cm.getAllNetworks()-ի վրա և փորձարկեք յուրաքանչյուր հնարավորությունների հավաքածու։ Երկու պատասխանները տարբերվում են այն սարքում, որը գործարկում է յուրաքանչյուր հավելվածի VPN, որը ծածկում է այլ հավելվածներ, բայց ոչ ձերը, և այդ տարբերությունը սկզբնաղբյուրն է այն սխալի հաշվետվությունների մեծամասնության, որոնք ասում են, թե հավելվածը չի տեսնում ակնհայտորեն միացված VPN-ը։
Հետևեք փոփոխություններին՝ հարցումների փոխարեն
Ժամաչափով հարցումները սխալ ձև է այս հարցի համար, քանի որ վիճակի փոփոխությունները գալիս են որպես իրադարձություններ։ Գրանցեք հարցում, որը համապատասխանում է VPN transport-ին, և թողեք framework-ը ձեզ ասի, թե երբ համապատասխան ցանց է հայտնվում, անհետանում կամ փոխվում։
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 |
Երրորդ տողն այն է, ինչ մարդիկ փորձում են պատասխանել client-ում, և այնտեղ հնարավոր չէ պատասխանել։ Տվեք VPN client-ներին իրենց սեփական հասցեների լողավազանը և կարդացեք աղբյուրի հասցեն սերվերում. դա միակ ապացույցն է, որ դիմանում է, երբ օգտատերը կես նստաշրջանում փոխում է թունելը։
Ստուգման հերթականություն, որը բացառում է կեղծ դրական արդյունքները
Շրջանառության մեջ դեռ գտնվող հին օրինակելի կոդը կոչում է getNetworkInfo(ConnectivityManager.TYPE_VPN)։ Այդ կանչը հնացած է, և մի քանի թողարկումներում այն վերադարձնում էր null նույնիսկ միացված VPN-ի դեպքում, դրա համար էլ շատ պատճենված հատվածներ կոտրված են թվում աշխատող սարքում։ Հնարավորությունների ստուգումները խուսափում են ծուղակից և նույն կերպ են վարվում Android 5.0-ից սկսած։ Եթե պահպանում եք կոդային բազա, որը դեռ կանչում է այն, փոխարինեք կանչը փոխարենը այն fallback-ով փաթաթելու, քանի որ fallback-ն էր, որ սկզբում արտադրեց սխալ պատասխանը։
Մեկ կարճ ուղի արժանի է զգուշացման։ Արտաքին ծառայության հաղորդած հանրային հասցեն VPN-ի ելքի հասցեի հետ համեմատելը ինչ-որ բան ապացուցում է ուղու մասին, բայց ամեն ստուգման ավելացնում է երրորդ կողմի հարցում, կախված է այդ ծառայության աշխատունակությունից և կարող է քեշավորվել կամ արգելափակվել։ Պահեք այն որպես ձեռքով ախտորոշում, երբեք որպես հավելվածի ներսի վիճակի ստուգում։
Կատարեք ստուգումները այս հերթականությամբ և կանգ առեք առաջինի վրա, որը չի համընկնում օգտատիրոջ հաշվետվության հետ՝ transport-ների ցանկը shell-ում, ակտիվ ցանցի հնարավորությունները, callback իրադարձությունները, մինչ միացնում և անջատում եք թունելը, ապա աղբյուրի հասցեն սերվերի կողմում։ Եթե բոլոր չորսը համընկնում են, և հավելվածը դեռ սխալ է վարվում, սխալը ձեր սեփական վիճակի մեքենայում է, ոչ թե Android-ի ցանցի ընկալման մեջ։