Հավելվածն աշխատում էր Android 9-ում և մահանում է նույն հեռախոսում Android 10-ի թարմացումից հետո։ Ձեր պրոֆիլը ձեռք չի տրվել, վկայականները նույնն են, և վթարը գալիս է նախքան որևէ նույնականացման երթևեկություն։ logcat-ը նախ ցույց է տալիս զրոյական ցուցիչ, և դրանից մի քանի տող վերև կա 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-ի կարգավորումը միասին․ մեկը փոխելով և մնացածը մոռանալով դուք վերադառնում եք նույն վթարին։ Կայուն լուծումը պահպանվող միջուկի թողարկմանն անցնելն է, այլ ոչ թե կարկատանը հավերժ կրելը։

Առնչվող հոդվածներ