როგორ შევამოწმოთ კოდით, დაკავშირებულია თუ არა VPN Android-ზე
Android-ს VPN-ის დაკავშირების მიმღები არ აქვს. ჰკითხეთ ქსელის შესაძლებლობებს, დააკვირდით ტრანსპორტის ცვლილებებს და ცალ-ცალკე შეინახეთ აპის თანხმობა, მოწყობილობის მდგომარეობა და სერვერის მტკიცებულება.
Android-ს ვერ ჰკითხავთ, დაკავშირებულია თუ არა VPN. VpnService არ ამჟღავნებს დაკავშირების მიმღებს და სტატუსის ზოლში გასაღების ხატულა სისტემას ხატავს და არა ის, რასაც თქვენი აპი წაიკითხავს. რაც შეგიძლიათ გააკეთოთ, უფრო ვიწრო შეკითხვა დაუსვათ კავშირის სტეკს: რომელ ქსელს იყენებს ამ აპის ტრაფიკი და ატარებს თუ არა ეს ქსელი 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 უკვე გაქრალ activity-ზე. აპი ხილული კომპონენტის გარეშე ცუდი მასპინძელია მუდმივი დამკვირვებლისთვის, და დავიწყებული 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 დაკავშირებული იყო, ამიტომ უამრავი კოპირებული ფრაგმენტი მომუშავე მოწყობილობაზე გატეხილად გამოიყურება. შესაძლებლობების შემოწმებები თავს არიდებს ამ ხაფანგს და ერთნაირად იქცევა Android 5.0-დან მოყოლებული. თუ ისევ იმ კოდბაზას ინახავთ, რომელიც მას იძახებს, ჩაანაცვლეთ თავად გამოძახება და ნუ შემოახვევთ fallback-ში, რადგან სწორედ fallback-მა წარმოშვა არასწორი პასუხი.
ერთი მოკლე გზა გაფრთხილებას იმსახურებს. გარე სერვისის მიერ მოხსენებული საჯარო მისამართის VPN-ის გასასვლელი მისამართთან შედარება რაღაცას ამტკიცებს გზაზე, მაგრამ ყოველ შემოწმებას მესამე მხარის მოთხოვნას უმატებს, დამოკიდებულია იმაზე, რომ ის სერვისი მუშაობდეს, და შეიძლება დაქეშდეს ან დაიბლოკოს. შეინახეთ ის ხელით დიაგნოსტიკად, არასოდეს როგორც აპის შიგნით მდგომარეობის შემოწმება.
შეასრულეთ შემოწმებები ამ რიგით და გაჩერდით პირველზე, რომელიც მომხმარებლის ცნობას არ ეთანხმება: ტრანსპორტების სია shell-იდან, აქტიური ქსელის შესაძლებლობები, callback მოვლენები გვირაბის ჩართვა-გამორთვისას, შემდეგ სერვერის მხრიდან წყაროს მისამართი. თუ ოთხივე ემთხვევა და აპი მაინც არასწორად იქცევა, ბაგი თქვენს მდგომარეობის მანქანაშია და არა Android-ის ქსელის ხედვაში.