Programa veikė Android 9 ir miršta tame pačiame telefone po atnaujinimo į Android 10. Jūsų profilis nepaliestas, sertifikatai tie patys, o lūžis ateina prieš bet kokį autentifikavimo srautą. Logcat pirmiausia rodo nulinį rodyklę, o kelios eilutės aukščiau yra Cannot run program ...: error=13, Permission denied. Ta žinutė apie failo vykdymą, o ne apie jūsų konfigūracijos skaitymą.

Ką iš tikrųjų atsisako 13 klaida

Android 10, API 29 lygis, sustabdė programas vykdyti failus, kuriuos jos įrašė į savo duomenų katalogą. Taisyklė nukreipta į saugyklą, kuri pirmiausia įrašoma, o paskui vykdoma, ir būtent tai sukuria paleidiklis, kopijuojantis iš assets. Senesni ics-openvpn branduolio kompiliavimai pirmo paleidimo metu iš APK išpakuoja vykdomąjį failą openvpn ir paleidžia jį su ProcessBuilder. Išpakavimas vis dar pavyksta; atsisakoma būtent vykdymo. Programa, nukreipta į API 29, paveldi taisyklę net tada, kai veikia įrenginyje, išleistame su senesne versija, todėl atnaujinimas gali sugadinti kompiliavimą, kurio niekas nelietė.

Atsisakyme nėra nieko būdingo vienam katalogo pavadinimui. Failas, kurį programa sukūrė savo duomenų kataloge, laikomas nevykdomu nepriklausomai nuo kelio, todėl getFilesDir(), getCacheDir() ir pasirinktinis pakatalogis visi nepavyksta vienodai.

Atskirkite pagalbinio failo lūžį nuo profilio problemos

Skaitykite žurnalą aplink proceso paleidimą, o ne slinkite nuo viršaus. Parašas, kurio ieškote, yra eilutė error=13, o virš jos esantis steko pėdsakas paprastai nurodo paleidiklio metodą, kuris iškvietė ProcessBuilder:

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

Jei pagalbinis failas niekada nebuvo paleistas, OpenVPN žurnalo išvesties visai nematysite: nei šifrų derybų, nei stumiamų maršrutų, nei DNS konfigūracijos. Jei tos eilutės vis dėlto pasirodo, o gedimas ateina vėliau, dvejetainis failas veikė ir problema yra profilyje, serveryje arba maršrutizavimo sluoksnyje. Šis vienintelis skirtumas sutaupo daug laiko, kuris kitaip būtų išleistas taisant failus .ovpn, kurie niekada nebuvo kalti. Jis taip pat pasako, kurios iš dviejų pataisų žemiau jums iš tikrųjų reikia.

Patikrinkite ir API lygį įrenginyje, kurį testuojate, o ne tą, kuris yra jūsų kompiliavimo konfigūracijoje. Emuliatoriaus atvaizdas su API 28 nieko neatkuria, nes apribojimas kandžioja tik nuo 29 ir aukščiau. Tuo pačiu metu patikrinkite tikslinį SDK savo kompiliavimo faile: būtent jo pakėlimas įtraukia šią taisyklę.

adb shell getprop ro.build.version.sdk

Kodėl dvejetainio failo kopijavimas kitur nepadeda

Akivaizdus apėjimas, pagalbinio failo įrašymas į kitą įrašomą katalogą, nepavyksta dėl tos pačios priežasties kaip ir originalas. Android vis dar leidžia vykdyti bendrąsias bibliotekas, įdiegtas programos vietinių bibliotekų kataloge, nes jas supakuoja diegėjas, o ne jos įrašomos vykdymo metu. Būtent tas katalogas yra vienintelė vieta, į kurią paleidiklis turėtų rodyti. Viskas, ką patys išpakuojate vykdymo metu, kad ir kaip tai pavadintumėte, lieka atmestoje kategorijoje.

Išorinė saugykla yra blogiau: ji prijungta su noexec, o ten padėto pagalbinio failo negali paleisti jokia programa. Programos duomenų išvalymas taip pat negrąžina seno elgesio, nes kitas paleidimas tiesiog vėl sukuria tą pačią nevykdomą kopiją.

Nukreipkite paleidiklį į supakuotą biblioteką

Pervadinkite pagalbinį failą bendrosios bibliotekos vardu ir pateikite jį po jniLibs kiekvienai palaikomai ABI, tada leiskite paleidikliui nuo API 28 grąžinti įdiegtos bibliotekos kelią:

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

Biblioteka turi būti išpakuota diegimo metu, kad tame kelyje būtų tikras failas, todėl nustatykite android:extractNativeLibs="true" elementui <application>. Jei manifestų sujungėjas įtraukia kitą reikšmę, pridėkite tools:replace="android:extractNativeLibs" šalia. application elemento nuoroda dokumentuoja, ką valdo šis atributas.

Saugokitės pasikartojančio vietinio kintamojo, kai paleidiklį taisote rankomis. Senesni šaltiniai tame pačiame metode vėliau deklaruoja nativeAPI, o antroji deklaracija yra kompiliavimo, o ne vykdymo klaida, todėl kompiliavimas greitai nepavyksta ir klaidą lengva pastebėti. Laikykite Gradle bloko ABI sąrašą suderintą su katalogais, kuriuos pridedate.

Suderinkite išleidimo artefaktą su testiniu kompiliavimu

Derinimo APK gali pakelti tunelį, o išleidimo bundle vis dar lūžta, nes bundle supakuoja vietines bibliotekas kita forma ir gali jas tiekti suspaustas iš archyvo. Jei nepavyksta tik iš bundle gauti kompiliavimai, pridėkite tai į gradle.properties ir perkompiliuokite:

android.bundle.enableUncompressedNativeLibs=false

Tada patvirtinkite, kad įdiegtas paketas nurodo laukiamą ABI, ir pakartokite patikrą 32 bitų ir 64 bitų įrenginyje, jei tiekiate abu. Paketų tvarkyklė nurodo pasirinktą ABI, todėl įrenginys, nusileidęs iki 32 bitų, pasirodo čia, o ne lūžio ataskaitoje:

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

Trūkstama biblioteka vienai ABI atkartoja pirminę prieigos klaidą tik tame įrenginyje, todėl vienas sėkmingas testinis telefonas nėra įrodymas. Tiekite manifestų vėliavėlę, bibliotekos pavadinimą ir bundle nustatymą kartu; pakeitę vieną ir pamiršę kitus grįšite prie to paties lūžio. Ilgalaikis sprendimas yra pereiti prie prižiūrimo branduolio leidimo, o ne amžinai nešiotis pataisą.

Susiję straipsniai