Az OpenVPN segédbinárisa nem fut Android 10-en: hozzáférés megtagadva
Az Android 10 megtagadja egy olyan segédprogram futtatását, amelyet az alkalmazás a saját tárhelyére írt. Olvasd a logcatet, szállítsd a segédet natív könyvtárként, és javítsd a bundle build-eket.
Az alkalmazás Android 9-en működött, és ugyanezen a telefonon hal meg az Android 10-re frissítés után. A profilod érintetlen, a tanúsítványok ugyanazok, és az összeomlás minden hitelesítési forgalom előtt jön. A logcat először egy null mutatót mutat, és néhány sorral fölötte ott áll Cannot run program ...: error=13, Permission denied. Az az üzenet fájl futtatásáról szól, nem a konfigurációd olvasásáról.
Mit utasít el valójában a 13-as hiba
Az Android 10, a 29-es API-szint, megakadályozta, hogy az alkalmazások futtassák azokat a fájlokat, amelyeket a saját adatkönyvtárukba írtak. A szabály az olyan tárhelyet célozza, amely előbb írható, majd futtatható, és pontosan ezt hozza létre az az indító, amely az assets mappából másol ki. Az ics-openvpn mag régebbi buildjei az első futáskor kicsomagolnak egy futtatható openvpn fájlt az APK-ból, és ezzel indítják: ProcessBuilder. A kicsomagolás továbbra is sikerül, a futtatás az, amit megtagadnak. Egy API 29-re célzó alkalmazás akkor is örökli a szabályt, ha olyan eszközön fut, amely régebbi kiadással érkezett, ezért egy frissítés eltörhet egy buildet, amelyhez senki nem nyúlt.
A megtagadásban semmi sem jellemző egyetlen mappanévre. Az alkalmazás adatkönyvtára alatt létrehozott fájlt az útvonaltól függetlenül nem futtathatónak tekinti a rendszer, így a getFilesDir(), a getCacheDir() és egy egyéni alkönyvtár is ugyanúgy elbukik.
Válaszd szét a segéd összeomlását és a profilhibát
A folyamat indítása körüli naplót olvasd, ne fentről görgess. A keresendő aláírás a error=13 sor, és a fölötte lévő veremnyom általában megnevezi azt az indítómetódust, amely meghívta a ProcessBuilder-t:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Ha a segéd soha nem indult el, egyáltalán nem látsz OpenVPN-naplókimenetet: sem titkosítási egyeztetést, sem leküldött útvonalakat, sem DNS-konfigurációt. Ha viszont azok a sorok megjelennek, és a hiba később jön, a bináris futott, és a probléma a profilban, a szerverben vagy az útválasztási rétegben van. Ez az egyetlen különbség sok időt spórol, amely különben olyan .ovpn fájlok szerkesztésére menne el, amelyek soha nem voltak hibásak. Azt is megmondja, melyikre van valójában szükséged az alábbi két javítás közül.
Ellenőrizd a tesztelt eszköz API-szintjét is, ne a build-konfigurációdban lévőt. Egy API 28-as emulátorkép semmit sem reprodukál, mert a korlátozás csak 29-től felfelé harap. Ugyanakkor ellenőrizd a build-fájlod cél-SDK-ját: annak emelése hozza be a szabályt.
adb shell getprop ro.build.version.sdkMiért nem segít a bináris máshová másolása
A kézenfekvő kerülőút, hogy a segédet egy másik írható könyvtárba írod, ugyanazért bukik el, amiért az eredeti. Az Android továbbra is engedi az alkalmazás natív könyvtárkönyvtárába telepített megosztott könyvtárak futtatását, mert azokat a telepítő csomagolja, és nem futásidőben íródnak. Ez az a könyvtár, amelyre az indítónak mutatnia kellene. Minden, amit futásidőben magad csomagolsz ki, bárminek is nevezed, a megtagadott kategóriában marad.
A külső tárhely rosszabb: noexec módben van csatolva, és az oda helyezett segédet egyetlen alkalmazás sem tudja elindítani. Az alkalmazásadatok törlése sem állítja vissza a régi viselkedést, mert a következő indítás egyszerűen újra létrehozza ugyanazt a nem futtatható másolatot.
Irányítsd az indítót egy csomagolt könyvtárra
Nevezd át a segédet megosztott könyvtárnévre, és szállítsd a jniLibs alatt minden általad támogatott ABI-hoz, majd az indító adjon vissza a telepített könyvtár útvonalát API 28-tól kezdve:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}A könyvtárat telepítéskor ki kell csomagolni ahhoz, hogy az az útvonal valódi fájlt tartalmazzon, ezért állítsd be a android:extractNativeLibs="true" beállítást a <application> elemen. Ha egy manifest-egyesítő más értéket hoz be, tedd mellé a tools:replace="android:extractNativeLibs" beállítást. Az application elem referenciája dokumentálja, mit vezérel ez az attribútum.
Figyelj egy duplikált helyi változóra, amikor kézzel foltozod az indítót. A régebbi források ugyanabban a metódusban később deklarálják a nativeAPI-t, és a második deklaráció fordítási hiba, nem futásidejű, ezért a build gyorsan elbukik, és a hiba könnyen észrevehető. Tartsd a Gradle-blokk ABI-listáját összhangban a hozzáadott mappákkal.
Egyeztesd a kiadási artefaktumot a tesztbuilddel
Egy hibakereső APK fel tudja hozni az alagutat, miközben egy kiadási bundle még mindig összeomlik, mert a bundle más formában csomagolja a natív könyvtárakat, és tömörítve szolgálhatja ki őket az archívumból. Ha csak a bundle-ből származó buildek buknak el, add hozzá ezt a gradle.properties-hoz, és építsd újra:
android.bundle.enableUncompressedNativeLibs=falseEzután igazold, hogy a telepített csomag a várt ABI-t jelenti, és ismételd meg az ellenőrzést egy 32 bites és egy 64 bites eszközön, ha mindkettőt szállítod. A csomagkezelő megnevezi a választott ABI-t, így a 32 bitre visszaesett eszköz itt jelenik meg, nem összeomlási jelentésben:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiEgy ABI-hoz hiányzó könyvtár csak azon az eszközön reprodukálja az eredeti engedélyhibát, ezért egyetlen sikeres teszttelefon nem bizonyíték. Szállítsd együtt a manifest jelzőt, a könyvtár nevét és a bundle beállítást; ha egyet megváltoztatsz, a többiről megfeledkezve ugyanahhoz az összeomláshoz jutsz vissza. A tartós megoldás az, hogy áttérsz egy karbantartott magkiadásra, ahelyett hogy örökké cipelnéd a foltot.