თქვენი აპლიკაცია იძახებს VpnService.prepare-ს, იღებს null-ს, აშენებს გვირაბს და დესკრიპტორი ვალიდურად გამოიყურება, მაგრამ სერვერამდე ვერაფერი აღწევს. ან prepare აბრუნებს Intent-ს, მომხმარებელი ადასტურებს მას და establish მაინც ვერ მუშაობს. ორივე შედეგი ჩვეულებრივ ერთს ნიშნავს: სხვა აპლიკაცია უკვე ფლობს ამ მომხმარებლის VPN ინტერფეისს და თქვენი protect გამოძახებები ნიშნავს სოკეტებს, რომლებსაც თქვენი გვირაბი ვერ გამოიყენებს. გაიარეთ ქვემოთ მოცემული შემოწმებები თანმიმდევრობით და გაჩერდით პირველზე, რომელიც ვერ მუშაობს, რადგან შემდგომი ნაბიჯი წინას ვერ გამოასწორებს.

შეამოწმეთ VPN-ის ადგილი, სანამ protect-ს შეეხებით

Android თითოეულ მომხმარებელს აძლევს ზუსტად ერთ აქტიურ VPN ინტერფეისს. ეს პლატფორმის წესია და არა ნებართვა, რომელიც თქვენს აპლიკაციას შეუძლია მოითხოვოს, ამიტომ ორი კლიენტი, რომლებსაც ორივეს სურს გვირაბი იყოს, ერთად ვერ იარსებებს.

სხვა აპლიკაციისთვის დაყენებული მუდამ ჩართული VPN ყველაზე რთული შემთხვევაა, რადგან სისტემა ინარჩუნებს ამ აპლიკაციას დაკავშირებულს და უარყოფს ჩაბარებას. ტრაფიკის ფილტრს, როგორიცაა AdGuard, კორპორატიულ კლიენტს ან სამომხმარებლო VPN-ს, შეუძლია დაიკავოს ადგილი, სანამ მომხმარებელს დაავიწყდა, რომ ის დაინსტალირებულია. სთხოვეთ მომხმარებელს გახსნას პარამეტრები და წაიკითხოს VPN-ის ქვეშ მითითებული ჩანაწერი; იქ მოცემული სახელი არის ის მფლობელი, რომელთანაც კონკურენციას აწარმოებთ. სანამ ის ჩანაწერი არ გაითიშება ან მუდამ ჩართულის პარამეტრი არ წაიშლება, თქვენი VpnService ვერ გახდება ინტერფეისი.

ნაბიჯი 1: წაიკითხეთ, რას აბრუნებს prepare

VpnService.prepare პასუხობს ერთ კითხვას: შეიძლება ეს აპლიკაცია გახდეს VPN? ის აბრუნებს null-ს, როცა თანხმობა უკვე არსებობს, და Intent-ს, როცა მომხმარებელმა ჯერ კიდევ უნდა დაამტკიცოს ის, რაც უნდა გაუშვათ startActivityForResult-ით. null-ის დაბრუნებას მოეკიდეთ როგორც გაგრძელების ნებართვას და არა როგორც მტკიცებას, რომ establish წარმატებით დასრულდება.

Intent intent = VpnService.prepare(context);
if (intent != null) {
    startActivityForResult(intent, REQUEST_VPN);
}

თუ Intent-ის გზა ბრუნდება და მომხმარებელმა უარი თქვა, prepare შემდეგ ცდაზე კვლავ Intent-ს დააბრუნებს. ჩაწერეთ შედეგის კოდი ციკლში ხელახლა ცდის ნაცვლად, რადგან უარყოფილი თანხმობა მომხმარებლის გადაწყვეტილებაა და არა დროებითი შეცდომა.

ნაბიჯი 2: დაადასტურეთ, რომ ინტერფეისი თქვენს აპლიკაციას ეკუთვნის

establish-ის შემდეგ მიღებული ParcelFileDescriptor თქვენი საკუთრების მტკიცებულებაა და ღირს მისი შემოწმება ნებისმიერ სოკეტურ სამუშაომდე. null ან უკვე დახურული დესკრიპტორი ნიშნავს, რომ ინტერფეისი არასოდეს შექმნილა, მაშინაც კი, თუ გამონაკლისი არ გამოჩენილა. შეადარეთ builder-ის ინტერფეისის სახელი იმ სახელს, რომელსაც სისტემა აქტიური VPN-ისთვის აცხადებს, dumpsys-ის გამოყენებით სატესტო მოწყობილობაზე. თუ ისინი განსხვავდება, თქვენ მიერ აშენებული გვირაბი არ არის ის გვირაბი, რომელიც ტრაფიკს ატარებს.

adb shell dumpsys connectivity

იგივე შეუსაბამობა ხსნის დაკავშირებულ ხატულას მკვდარი ტრაფიკით. სისტემა აჩვენებს სხვა აპლიკაციის გვირაბს, ხოლო თქვენი კოდი ინახავს დესკრიპტორს ინტერფეისისა, რომელსაც არ ფლობს.

ნაბიჯი 3: გამოიძახეთ protect connect-ამდე

როგორც კი ინტერფეისი არსებობს, თქვენი აპლიკაციის მიერ გახსნილი სოკეტები ნაგულისხმევად გვირაბში მიემართება. გარე ტრანსპორტი ვერ მიჰყვება ამ წესს, თორემ ინკაფსულირებული პაკეტი ინტერფეისში დაბრუნდებოდა, ამიტომ VpnService protect ნიშნავს სოკეტს VPN-ის გვერდის ავლით და ქვედა ქსელის გამოყენებისთვის. გამოძახება აბრუნებს boolean-ს და მისი იგნორირება ის გზაა, რომლითაც ხვდებით ჯანსაღად გამომყურე, მაგრამ არაფრის მატარებელ სოკეტს.

გამოიძახეთ protect connect-ამდე ან bind-ამდე, არასოდეს შემდეგ. დაცვა უნდა იყოს ადგილზე მანამ, სანამ სოკეტი დაუკავშირდება ან ჩაიკეტება, რადგან დაკავშირებულმა სოკეტმა უკვე აირჩია თავისი მარშრუტი და გვიანი protect მას საიმედოდ არ გადაადგილებს. თუ protect აბრუნებს false-ს, გაჩერდით და თავიდან შეამოწმეთ საკუთრება: ის ვერ მუშაობს, როცა ინტერფეისი თქვენს აპლიკაციას არ ეკუთვნის, რაც ზუსტად ის მდგომარეობაა, რომელსაც სხვა VPN ტოვებს. false-ის დაბრუნება ნიშანია დაუბრუნდეთ ნაბიჯ 1-ს და არა იგივე სოკეტი ხელახლა სცადოთ.

DatagramSocket socket = new DatagramSocket();
if (!vpnService.protect(socket)) {
    return;
}
socket.connect(serverAddress, serverPort);

ნაბიჯი 4: მიაბით პროცესი, როცა სოკეტი მოუხერხებელია

ზოგიერთი ტრანსპორტი ერთი სოკეტი არ არის: ბიბლიოთეკამ შეიძლება რამდენიმე გახსნას, ან გსურთ, რომ დამხმარე პროცესში ყველა კავშირმა იგივე გასასვლელი გამოიყენოს. ConnectivityManager.bindProcessToNetwork აბამს პროცესს არჩეულ ქსელზე და ძველი setProcessDefaultNetwork იგივეს აკეთებდა, სანამ მოძველებულად გამოცხადდებოდა. მოითხოვეთ ქვედა კავშირი NET_CAPABILITY_NOT_VPN-ით, რათა მიბმამ აირჩიოს თქვენი გვირაბის ქვეშ არსებული ქსელი და არა თავად გვირაბი.

NetworkRequest request = new NetworkRequest.Builder()
    .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
    .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();

პროცესის მიბმა მეორე VPN არ არის. ის ირჩევს გასასვლელს ამ პროცესისთვის, რაც სასარგებლოა თქვენი ტრანსპორტისთვის და უსარგებლოა ორი გვირაბის ერთდროულად გასაშვებად.

შეაკეთეთ მომხმარებლის მოწყობილობა და არა კოდის გზა

არ არსებობს API, რომელიც ორ აპლიკაციას VPN-ის ადგილის გაზიარების საშუალებას მისცემს, ამიტომ გასწორება მოწყობილობის მდგომარეობას ეხება. შესთავაზეთ მომხმარებელს ორი კონკრეტული გზა: გათიშეთ ან წაშალეთ სხვა აპლიკაციის მუდამ ჩართული, რათა თქვენმა prepare-მა ჩაიბაროს, ან გახსენით იმ აპლიკაციის აპლიკაციით პარამეტრები და გამორიცხეთ თქვენი აპლიკაცია მისი გვირაბიდან. გამორიცხვა ფილტრს მუშაობაში ინარჩუნებს ყველა დანარჩენისთვის და თქვენს აპლიკაციას აძლევს ჩვეულებრივი კავშირის გამოყენების საშუალებას, რაც ხშირად ის შედეგია, რომელიც მომხმარებელს სინამდვილეში სურს.

ჩაწერეთ ორივე გზა თქვენს მხარდაჭერის დოკუმენტაციაში, რადგან მომხმარებლებმა იშვიათად იციან, რომელი აპლიკაცია ფლობს VPN-ს. სთხოვეთ მათ გამოგზავნონ აქტიური VPN-ის სახელი პარამეტრებიდან და prepare, establish და protect-ის შედეგები თქვენი ჟურნალებიდან, და ერთ წუთში შეძლებთ თქვათ, რომელი ნაბიჯი ვერ შესრულდა. შემდეგ გადაამოწმეთ რეალური ტრაფიკით: სახელის ძიება, TCP კავშირი და ერთი მოთხოვნა გვირაბის გავლით და არა მხოლოდ დაკავშირებული ხატულა. ვალიდური დესკრიპტორი და მწვანე მაჩვენებელი საუკეთესო შემთხვევაში საკუთრებას ამტკიცებს და არასოდეს იმას, რომ ტრანსპორტმა მარშრუტიზაციის წესები გადაიტანა.

თუ ოთხივე შემოწმება გაივლის და ტრაფიკი მაინც კვდება, საკუთრება აღარ არის თქვენი პრობლემა. გადადით მარშრუტიზაციასა და სერვერის ხელმისაწვდომობაზე და შეამოწმეთ იგივე მონაცემები მოწყობილობიდან, სადაც VPN აპლიკაცია არ არის დაინსტალირებული.

დაკავშირებული სტატიები