Tətbiq Android 9-da işləyirdi və Android 10 yeniləməsindən sonra həmin telefonda ölür. Profiliniz toxunulmazdır, sertifikatlar eynidir və çökmə hər hansı autentifikasiya trafikindən əvvəl gəlir. logcat əvvəlcə null göstərici göstərir, bir neçə sətir yuxarıda isə Cannot run program ...: error=13, Permission denied. Bu mesaj faylın icrasından bəhs edir, konfiqurasiyanızın oxunmasından deyil.

13 xətası əslində nəyi rədd edir

Android 10, API səviyyəsi 29, tətbiqlərin öz data qovluğuna yazdığı faylları icra etməsini dayandırdı. Qayda yazıla bilən, sonra icra edilə bilən yaddaşa yönəlib və bu, aktivlərdən kopyalayan başladıcının yaratdığı şeyin tam özüdür. ics-openvpn nüvəsinin köhnə build-ləri ilk işə salınmada APK-dan openvpn icra faylını açır və onu ProcessBuilder ilə başladır. Açma hələ də uğurludur; rədd edilən icradır. API 29-u hədəfləyən tətbiq bu qaydanı daha köhnə buraxılışla gələn cihazda işləsə belə miras alır, buna görə heç kimin toxunmadığı build-i bir yeniləmə poza bilər.

Rəddin heç bir tərəfi bir qovluq adına xas deyil. Tətbiqin data qovluğu altında yaratdığı fayl yoldan asılı olmayaraq icra edilə bilməyən sayılır, buna görə getFilesDir(), getCacheDir() və fərdi alt qovluq eyni şəkildə uğursuz olur.

Köməkçi çökməsini profil problemindən ayırın

Prosesin başlaması ətrafındakı loqu oxuyun, yuxarıdan aşağı sürüşdürməyin. Axtardığınız imza error=13 sətridir və onun üstündəki stack trace adətən ProcessBuilder çağıran başladıcı metodunu göstərir:

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

Köməkçi heç vaxt başlamayıbsa, ümumiyyətlə OpenVPN log çıxışı görməyəcəksiniz: nə şifr razılaşması, nə göndərilən marşrutlar, nə də DNS konfiqurasiyası. Əgər o sətirlər görünürsə və uğursuzluq sonra gəlirsə, binary işləyib və problem profildə, serverdə və ya marşrutlaşdırma qatındadır. Bu tək fərq heç vaxt günahkar olmayan .ovpn fayllarını redaktə etməyə sərf olunan çox vaxta qənaət edir. Həm də aşağıdakı iki düzəlişdən hansına əslində ehtiyacınız olduğunu deyir.

Həm də test etdiyiniz cihazdakı API səviyyəsini yoxlayın, build konfiqurasiyanızdakını deyil. API 28-də emulyator obrazı heç nə təkrarlamır, çünki məhdudiyyət yalnız 29-dan yuxarı təsir edir. Eyni zamanda build faylınızdakı hədəf SDK-nı yoxlayın: qaydanı içəri çəkən onun yüksəldilməsidir.

adb shell getprop ro.build.version.sdk

Niyə binary-ni başqa yerə kopyalamaq kömək etmir

Aşkar dövrə yolu, köməkçini başqa yazıla bilən qovluğa yazmaq, orijinalın uğursuz olduğu səbəbdən uğursuz olur. Android hələ də tətbiqin yerli kitabxana qovluğuna quraşdırılmış paylaşılan kitabxanaları icra etməyə icazə verir, çünki onları quraşdırıcı paketləyir, işləmə vaxtı yazılmır. Başladıcının yönəlməli olduğu yeganə yer həmin qovluqdur. İşləmə vaxtı özünüz açdığınız hər şey, nə ad verirsinizsə verin, rədd edilən kateqoriyada qalır.

Xarici yaddaş daha pisdir: o, noexec kimi mount edilir və oraya qoyulan köməkçini heç bir tətbiq başlata bilməz. Tətbiq məlumatlarının təmizlənməsi də köhnə davranışı bərpa etmir, çünki növbəti işə salınma sadəcə eyni icra edilə bilməyən kopyanı yenidən yaradır.

Başladıcını paketlənmiş kitabxanaya yönəldin

Köməkçini paylaşılan kitabxana adına dəyişdirin və dəstəklədiyiniz hər ABI üçün jniLibs altında göndərin, sonra başladıcı API 28-dən etibarən quraşdırılmış kitabxana yolunu qaytarsın:

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

Həmin yolun real fayl saxlaması üçün kitabxana quraşdırma zamanı açılmalıdır, buna görə android:extractNativeLibs="true" atributunu <application> elementinə qoyun. Əgər manifest birləşdiricisi fərqli dəyər gətirirsə, onun yanına tools:replace="android:extractNativeLibs" əlavə edin. tətbiq elementi arayışı atributun nəyə nəzarət etdiyini sənədləşdirir.

Başladıcını əl ilə yamaqlayarkən təkrarlanan yerli dəyişəndən çəkinin. Köhnə mənbələr həmin metodun sonunda nativeAPI elan edir və ikinci elan işləmə vaxtı deyil, kompilyasiya xətasıdır, buna görə build tez uğursuz olur və səhvi görmək asandır. Gradle blokundakı ABI siyahısını əlavə etdiyiniz qovluqlarla uyğun saxlayın.

Buraxılış artefaktını test build-i ilə uyğunlaşdırın

Sazlama APK tuneli başlada bilər, halbuki buraxılış bundle-ı hələ də çökür, çünki bundle yerli kitabxanaları fərqli formada paketləyir və onları arxivdən sıxılmış şəkildə verə bilər. Yalnız bundle-dən törəyən build-lər uğursuz olursa, bunu gradle.properties faylına əlavə edin və yenidən qurun:

android.bundle.enableUncompressedNativeLibs=false

Sonra quraşdırılmış paketin gözlədiyiniz ABI-ni bildirdiyini təsdiqləyin və hər ikisini göndərirsinizsə, yoxlamanı 32 bit və 64 bit cihazda təkrarlayın. Paket meneceri seçdiyi ABI-ni adlandırır, buna görə 32 bit-ə geri düşən cihaz çökmə hesabatında deyil, burada görünür:

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

Bir ABI üçün itkin kitabxana orijinal icazə xətasını yalnız həmin cihazda təkrarlayır, buna görə bir uğurlu test telefonu sübut deyil. Manifest bayrağını, kitabxana adını və bundle parametrini birlikdə göndərin; birini dəyişib digərlərini unutmaq sizi eyni çökməyə qaytarır. Davamlı həll yamağı əbədi daşımaq əvəzinə dəstəklənən nüvə buraxılışına keçməkdir.

Əlaqəli məqalələr