Android-এ কোডে VPN সংযুক্ত আছে কিনা যাচাই করবেন কীভাবে
Android-এ VPN সংযোগের কোনো getter নেই। নেটওয়ার্কের সক্ষমতা জিজ্ঞাসা করুন, ট্রান্সপোর্ট পরিবর্তন লক্ষ্য করুন এবং অ্যাপের সম্মতি, ডিভাইসের অবস্থা ও সার্ভারের প্রমাণ আলাদা রাখুন।
আপনি Android-কে জিজ্ঞাসা করতে পারেন না 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()-এর উপর ইটারেট করুন এবং প্রতিটি সক্ষমতা সেট পরীক্ষা করুন। দুটি উত্তর আলাদা হয় এমন ডিভাইসে যা per-app 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 ঝুলিয়ে রাখবেন না। দৃশ্যমান উপাদানহীন অ্যাপ স্থায়ী পর্যবেক্ষকের জন্য খারাপ আয়োজক, আর যে callbacks ভুলে যান সেগুলোই কনফিগারেশন পরিবর্তনের পরে ক্র্যাশ করে।
তিনটি প্রশ্ন আলাদা রাখুন
এই ক্ষেত্রের বেশিরভাগ বিভ্রান্তি আসে তিনটি ভিন্ন পরীক্ষাকে একটি হিসেবে ধরার থেকে। তারা ভিন্ন 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) ডাকে। ওই কলটি বাতিল করা হয়েছে, আর কয়েকটি রিলিজে VPN সংযুক্ত থাকা সত্ত্বেও এটি null ফেরত দিয়েছিল, তাই অনেক কপি করা স্নিপেট কাজ করা ডিভাইসে ভাঙা দেখায়। সক্ষমতা পরীক্ষা ফাঁদটি এড়ায় এবং Android 5.0 থেকে একই আচরণ করে। যদি এমন কোডবেস রক্ষণাবেক্ষণ করেন যা এখনও এটি ডাকে, কলটিকে ফলব্যাকে মুড়ে না দিয়ে প্রতিস্থাপন করুন, কারণ ফলব্যাকই প্রথমে ভুল উত্তরটি তৈরি করেছিল।
একটি শর্টকাট সতর্কতার যোগ্য। বহিঃস্থ সেবা যে পাবলিক ঠিকানা জানায় তা VPN প্রস্থান ঠিকানার সাথে তুলনা করা পথ সম্পর্কে কিছু প্রমাণ করে, কিন্তু প্রতিটি পরীক্ষায় তৃতীয় পক্ষের অনুরোধ যোগ করে, ওই সেবা চালু থাকার উপর নির্ভর করে, এবং ক্যাশে বা ব্লক হতে পারে। এটি হাতে করা ডায়াগনস্টিক হিসেবে রাখুন, কখনো অ্যাপের ভেতরের অবস্থা পরীক্ষা হিসেবে নয়।
এই ক্রমে পরীক্ষাগুলো চালান এবং ব্যবহারকারীর প্রতিবেদনের সাথে মিলছে না এমন প্রথমটিতে থামুন: shell-এ ট্রান্সপোর্ট তালিকা, সক্রিয় নেটওয়ার্কের সক্ষমতা, টানেল চালু-বন্ধ করার সময় callback ঘটনা, তারপর সার্ভার পাশে সোর্স ঠিকানা। যদি চারটিই মিলে যায় আর অ্যাপ তবুও খারাপ আচরণ করে, বাগটি Android-এর নেটওয়ার্ক দৃষ্টিভঙ্গিতে নয়, আপনার নিজের অবস্থা মেশিনে।