აპი მუშაობდა Android 9-ზე და კვდება იმავე ტელეფონზე Android 10-ზე განახლების შემდეგ. თქვენი პროფილი ხელუხლებელია, სერტიფიკატები იგივეა და კრახი ნებისმიერ ავთენტიფიკაციის ტრაფიკამდე მოდის. logcat ჯერ null მაჩვენებელს აჩვენებს, და რამდენიმე სტრიქონით ზემოთ არის Cannot run program ...: error=13, Permission denied. ეს შეტყობინება ფაილის გაშვებას ეხება, არა თქვენი კონფიგურაციის წაკითხვას.

რას უარყოფს სინამდვილეში შეცდომა 13

Android 10-მა, API დონე 29-მა, შეწყვიტა აპების მიერ საკუთარ მონაცემთა დირექტორიაში დაწერილი ფაილების გაშვება. წესი მიმართულია ისეთ საცავზე, რომელიც ჯერ ჩასაწერია და შემდეგ გაშვებადი, და ზუსტად ამას ქმნის assets-იდან კოპირებადი გამშვები. ics-openvpn ბირთვის ძველი აწყობები პირველ გაშვებაზე APK-იდან ხსნიან გაშვებად openvpn ფაილს და იწყებენ მას ProcessBuilder-ის მეშვეობით. ამოღება კვლავ წარმატებულია; უარყოფილია სწორედ გაშვება. API 29-ზე მიმართული აპი ამ წესს მემკვიდრეობით იღებს მაშინაც, როცა უფრო ძველი გამოშვებით გამოსულ მოწყობილობაზე მუშაობს, ამიტომ განახლებამ შეიძლება გატეხოს აწყობა, რომელსაც არავინ შეხებია.

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

გამოყავით დამხმარის კრახი პროფილის პრობლემისგან

წაიკითხეთ ლოგი პროცესის დაწყების ირგვლივ, ნაცვლად ზემოდან სქროლვისა. მოსაძებნი ხელმოწერაა ხაზი error=13, და მის ზემოთ არსებული სტეკის კვალი ჩვეულებრივ ასახელებს გამშვების იმ მეთოდს, რომელმაც გამოიძახა ProcessBuilder:

adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"

თუ დამხმარე არასოდეს გაშვებულა, OpenVPN-ის ლოგის გამოტანას საერთოდ ვერ ნახავთ: არც შიფრის მოლაპარაკება, არც გაგზავნილი მარშრუტები, არც DNS კონფიგურაცია. თუ ეს სტრიქონები მაინც გამოჩნდა და მარცხი მოგვიანებით მოვიდა, ბინარი მუშაობდა და პრობლემა პროფილში, სერვერში ან მარშრუტიზაციის შრეშია. ეს ერთი განსხვავება ბევრ დროს ზოგავს, რომელიც სხვაგვარად .ovpn ფაილების რედაქტირებას დაეხარჯა, რომლებიც არასოდეს ყოფილა დამნაშავე. ის ასევე გეტყვით, ქვემოთ მოცემული ორი შესწორებიდან რომელი გჭირდებათ სინამდვილეში.

შეამოწმეთ API დონე იმ მოწყობილობაზე, რომელსაც ამოწმებთ, და არა თქვენს აწყობის კონფიგურაციაში არსებული. API 28-ზე ემულატორის იმიჯი არაფერს ამრავლებს, რადგან შეზღუდვა მხოლოდ 29-იდან ზემოთ კბენს. ამავდროულად შეამოწმეთ სამიზნე SDK თქვენს აწყობის ფაილში: სწორედ მისი აწევა შემოიყვანს ამ წესს.

adb shell getprop ro.build.version.sdk

რატომ არ ეხმარება ბინარის სხვაგან კოპირება

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

გარე საცავი უარესია: ის noexec-ითაა მიმაგრებული, და იქ მოთავსებული დამხმარის გაშვებას ვერც ერთი აპი ვერ შეძლებს. აპის მონაცემების გასუფთავებაც არ აბრუნებს ძველ ქცევას, რადგან შემდეგი გაშვება უბრალოდ ხელახლა ქმნის იმავე არაგაშვებად ასლს.

მიმართეთ გამშვები შეფუთულ ბიბლიოთეკაზე

გადაარქვით დამხმარეს საერთო ბიბლიოთეკის სახელი და მიაწოდეთ ის jniLibs-ის ქვეშ ყოველი ABI-სთვის, რომელსაც მხარს უჭერთ, შემდეგ გამშვებმა API 28-იდან დაიწყოს დაყენებული ბიბლიოთეკის ბილიკის დაბრუნება:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    return new File(context.getApplicationInfo().nativeLibraryDir,
                    "libovpnexec.so").getPath();
}

ბიბლიოთეკა ინსტალაციის დროს უნდა გაიხსნას, რომ ამ ბილიკმა რეალური ფაილი შეიცავდეს, ამიტომ დააყენეთ android:extractNativeLibs="true" <application> ელემენტზე. თუ manifest-ის გამაერთიანებელი სხვა მნიშვნელობას შემოიტანს, დაამატეთ tools:replace="android:extractNativeLibs" მის გვერდით. application ელემენტის ცნობარი აღწერს, რას მართავს ეს ატრიბუტი.

ყურადღება მიაქციეთ გაორმაგებულ ლოკალურ ცვლადს, როცა გამშვებს ხელით აპაჩებთ. ძველი წყაროები იმავე მეთოდში მოგვიანებით აცხადებენ nativeAPI-ს, და მეორე განაცხადი კომპილაციის შეცდომაა და არა გაშვების, ამიტომ აწყობა სწრაფად მარცხდება და შეცდომა ადვილად შესამჩნევია. Gradle ბლოკში ABI-ების სია შეესაბამებოდეს თქვენს დამატებულ საქაღალდეებს.

შეესაბამეთ გამოშვების არტეფაქტი სატესტო აწყობას

გამართვის APK-მ შეიძლება გახსნას გვირაბი, სანამ გამოშვების bundle ჯერ კიდევ იშლება, რადგან bundle ნატიურ ბიბლიოთეკებს სხვა ფორმით აფუთავს და შეიძლება ისინი არქივიდან შეკუმშული მიაწოდოს. თუ მხოლოდ bundle-იდან მიღებული აწყობები მარცხდება, დაამატეთ ეს gradle.properties-ში და ხელახლა ააწყვეთ:

android.bundle.enableUncompressedNativeLibs=false

შემდეგ დაადასტურეთ, რომ დაყენებული პაკეტი აცხადებს თქვენ მიერ მოსალოდნელ ABI-ს, და გაიმეორეთ შემოწმება 32-ბიტიან და 64-ბიტიან მოწყობილობაზე, თუ ორივეს აწვდით. პაკეტების მენეჯერი ასახელებს არჩეულ ABI-ს, ამიტომ 32 ბიტზე დაბრუნებული მოწყობილობა აქ ჩნდება და არა კრახის ანგარიშში:

adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbi

ერთი ABI-სთვის დაკარგული ბიბლიოთეკა თავდაპირველ წვდომის შეცდომას მხოლოდ იმ მოწყობილობაზე ამრავლებს, ამიტომ ერთი წარმატებული სატესტო ტელეფონი მტკიცებულება არ არის. მიაწოდეთ manifest-ის დროშა, ბიბლიოთეკის სახელი და bundle-ის პარამეტრი ერთად; ერთის შეცვლა და დანარჩენების დავიწყება იმავე კრახს დაგაბრუნებთ. გამძლე გადაწყვეტა არის გადასვლა მხარდაჭერილ ბირთვის გამოშვებაზე, და არა პაჩის სამუდამოდ ტარება.

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