आप Android से नहीं पूछ सकते कि VPN कनेक्ट है या नहीं। VpnService कोई कनेक्शन getter प्रकट नहीं करता, और स्टेटस बार में चाबी का आइकन सिस्टम बनाता है, ऐसा कुछ नहीं जो आपका ऐप पढ़ सके। आप जो कर सकते हैं वह यह है कि कनेक्टिविटी स्टैक से एक सँकरा सवाल पूछें: इस ऐप का ट्रैफ़िक कौन-से नेटवर्क का उपयोग कर रहा है, और क्या वह नेटवर्क VPN ट्रांसपोर्ट ले जाता है। यह गाइड जाँचों को उसी क्रम में ले जाती है जो झूठे धनात्मक परिणामों से बचता है, डिवाइस पर shell कमांड से लेकर ऐप में चलता छोड़े गए callback तक।

ऐप का कोड छूने से पहले shell से डिवाइस जाँचें

पहले आधार-रेखा लें। adb shell dumpsys connectivity सिस्टम के जाने-पहचाने हर नेटवर्क को छापता है, और हर ब्लॉक अपने ट्रांसपोर्ट सूचीबद्ध करता है, इसलिए VPN नेटवर्क उस सूची में VPN के साथ दिखता है। बाकी सब से पहले डिफ़ॉल्ट नेटवर्क का ब्लॉक पढ़ें, फिर वही ट्रांसपोर्ट ले जाने वाले अन्य नेटवर्क खोजें।

दो आउटपुट दो अलग सवालों के जवाब देते हैं। VPN नेटवर्क का होना यह नहीं बताता कि आपका ऐप उसका उपयोग कर रहा है, और कार्य-प्रोफ़ाइल या दूसरे उपयोगकर्ता वाले डिवाइस पर दोनों एक साथ सही हो सकते हैं जबकि आपकी प्रक्रिया सीधे इंटरनेट से बात कर रही हो। सक्रिय नेटवर्क की पंक्ति ही तय करती है कि आपके सॉकेट क्या करेंगे।

वह नेटवर्क माँगें जो ऐप उपयोग कर रहा है

कोड में क्षमता जाँच चार पंक्तियाँ है। manifest में 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 ट्रांसपोर्ट से मेल खाने वाला अनुरोध पंजीकृत करें और 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 लटका न छोड़ें। दृश्य घटक के बिना ऐप स्थायी पर्यवेक्षक के लिए खराब मेज़बान है, और जो callbacks आप भूल जाते हैं वही कॉन्फ़िगरेशन बदलाव के बाद क्रैश होते हैं।

तीन सवालों को अलग रखें

इस क्षेत्र की अधिकांश उलझन तीन अलग जाँचों को एक मानने से आती है। वे अलग API उपयोग करती हैं, और हर एक सही हो सकती है जबकि बाकी गलत हों।

QuestionHow you answer itWhat a yes means
Did the user grant VPN consent to my app?VpnService.prepare() returns nullConsent exists; the tunnel may still be down
Is the device sending default traffic through a VPN?TRANSPORT_VPN on the active networkSomething is tunneling, not necessarily your app
Did this request arrive through the VPN?Source address seen by the serverProof for that connection, nothing wider

तीसरी पंक्ति वही है जिसे लोग क्लाइंट पर उत्तर देने की कोशिश करते हैं, और वहाँ उसका उत्तर नहीं दिया जा सकता। VPN क्लाइंट को उनका अपना पता-समूह दें और सर्वर पर स्रोत पता पढ़ें; यही एकमात्र सबूत है जो उपयोगकर्ता द्वारा सत्र के बीच में सुरंग बदलने पर भी टिकता है।

झूठे धनात्मक परिणामों को बाहर करने वाला सत्यापन क्रम

अभी भी प्रचलन में पुराना नमूना कोड getNetworkInfo(ConnectivityManager.TYPE_VPN) को कॉल करता है। वह कॉल अप्रचलित है, और कई रिलीज़ में उसने null लौटाया तब भी जब VPN कनेक्ट था, इसीलिए बहुत से कॉपी किए गए स्निपेट काम करते डिवाइस पर टूटे दिखते हैं। क्षमता जाँचें इस जाल से बचती हैं और Android 5.0 से आगे एक जैसा व्यवहार करती हैं। अगर आप ऐसा कोडबेस बनाए रखते हैं जो अब भी उसे कॉल करता है, तो कॉल को fallback में लपेटने के बजाय बदलें, क्योंकि fallback ही मूल रूप से गलत जवाब पैदा करता था।

एक शॉर्टकट चेतावनी के योग्य है। किसी बाहरी सेवा द्वारा बताए गए सार्वजनिक पते की तुलना VPN के निकास पते से करना पथ के बारे में कुछ साबित तो करता है, पर यह हर जाँच में तीसरे पक्ष का अनुरोध जोड़ता है, उस सेवा के चालू रहने पर निर्भर करता है, और कैश या ब्लॉक हो सकता है। इसे हाथ से किया जाने वाला निदान रखें, कभी ऐप के भीतर स्थिति जाँच के रूप में नहीं।

जाँचों को इस क्रम में चलाएँ और उपयोगकर्ता की रिपोर्ट से न मिलने वाली पहली जाँच पर रुकें: shell में ट्रांसपोर्ट सूची, सक्रिय नेटवर्क की क्षमताएँ, सुरंग चालू-बंद करते समय callback घटनाएँ, फिर सर्वर पक्ष पर स्रोत पता। अगर चारों मेल खाते हैं और ऐप फिर भी गलत व्यवहार करता है, तो बग Android के नेटवर्क-दृष्टिकोण में नहीं, आपकी अपनी स्थिति-मशीन में है।

संबंधित लेख