Pomocný binárny súbor OpenVPN zlyháva na Androide 10: prístup zamietnutý
Android 10 odmieta spustiť pomocník, ktorý aplikácia zapísala do vlastného úložiska. Prečítajte si logcat, doručte pomocníka ako natívnu knižnicu a opravte zostavenia bundle.
Aplikácia fungovala na Androide 9 a na tom istom telefóne umiera po aktualizácii na Android 10. Váš profil je nedotknutý, certifikáty sú rovnaké a pád prichádza pred akoukoľvek autentifikačnou prevádzkou. Logcat najprv ukáže nulový ukazovateľ a o niekoľko riadkov vyššie stojí Cannot run program ...: error=13, Permission denied. Tá správa je o spúšťaní súboru, nie o čítaní vašej konfigurácie.
Čo vlastne chyba 13 odmieta
Android 10, úroveň API 29, zastavil aplikáciám spúšťanie súborov, ktoré zapísali do svojho vlastného dátového adresára. Pravidlo mieri na úložisko, ktoré je najprv zapisovateľné a potom spustiteľné, a presne to vytvára zavádzač kopírujúci z assets. Staršie zostavenia jadra ics-openvpn rozbalia pri prvom spustení z APK spustiteľný súbor openvpn a spustia ho pomocou ProcessBuilder. Rozbalenie stále prebehne; zamietnuté je práve spustenie. Aplikácia cieliaca na API 29 pravidlo dedí, aj keď beží na zariadení, ktoré vyšlo so staršou verziou, a preto môže aktualizácia pokaziť zostavenie, ktorého sa nikto nedotkol.
Na zamietnutí nie je nič špecifické pre jeden názov priečinka. Súbor, ktorý aplikácia vytvorila v svojom dátovom adresári, sa považuje za nespustiteľný bez ohľadu na cestu, takže getFilesDir(), getCacheDir() aj vlastný podadresár zlyhávajú rovnako.
Oddeľte pád pomocníka od problému s profilom
Čítajte log okolo štartu procesu namiesto posúvania od začiatku. Hľadaný podpis je riadok error=13, a výpis zásobníka nad ním zvyčajne pomenuje metódu zavádzača, ktorá zavolala ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Ak sa pomocník nikdy nespustil, neuvidíte vôbec žiadny výstup logu OpenVPN: ani vyjednávanie šifier, ani pretlačené trasy, ani konfiguráciu DNS. Ak sa tie riadky predsa objavia a zlyhanie príde neskôr, binárka bežala a problém je v profile, v serveri alebo v smerovacej vrstve. Práve tento rozdiel ušetrí veľa času stráveného úpravou súborov .ovpn, ktoré nikdy neboli vinné. Povie vám aj to, ktorú z dvoch opráv nižšie skutočne potrebujete.
Skontrolujte aj úroveň API na zariadení, ktoré testujete, nie tú vo svojej konfigurácii zostavenia. Obraz emulátora na API 28 nič nereprodukuje, pretože obmedzenie pôsobí až od 29 vyššie. Zároveň skontrolujte cieľové SDK v súbore zostavenia: práve jeho zvýšenie toto pravidlo priťahuje.
adb shell getprop ro.build.version.sdkPrečo kopírovanie binárky inam nepomáha
Zjavný obchvat, zapísanie pomocníka do iného zapisovateľného adresára, zlyháva z rovnakého dôvodu ako originál. Android stále povoľuje spúšťanie zdieľaných knižníc nainštalovaných do adresára natívnych knižníc aplikácie, pretože ich balí inštalátor a nie sú zapisované za behu. Práve na tento adresár by mal zavádzač ukazovať. Čokoľvek si za behu sami rozbalíte, nech to nazvete akokoľvek, zostáva v zamietnutej kategórii.
Externé úložisko je horšie: je pripojené s noexec a pomocníka tam nemôže spustiť žiadna aplikácia. Vymazanie dát aplikácie tiež neobnoví staré správanie, pretože ďalšie spustenie jednoducho znova vytvorí tú istú nespustiteľnú kópiu.
Nasmerujte zavádzač na zabalenú knižnicu
Premenujte pomocníka na názov zdieľanej knižnice a doručte ho pod jniLibs pre každé ABI, ktoré podporujete, a potom nechajte zavádzač vracať cestu k nainštalovanej knižnici od API 28 vyššie:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}Knižnica musí byť rozbalená pri inštalácii, aby tá cesta obsahovala skutočný súbor, takže nastavte android:extractNativeLibs="true" na prvku <application>. Ak zlučovač manifestov prinesie inú hodnotu, pridajte vedľa tools:replace="android:extractNativeLibs". referenčná príručka prvku application dokumentuje, čo tento atribút riadi.
Dávajte pozor na duplikovanú lokálnu premennú, keď zavádzač záplatujete ručne. Staršie zdroje deklarujú nativeAPI neskôr v tej istej metóde a druhá deklarácia je chyba kompilácie, nie behu, takže zostavenie rýchlo zlyhá a chybu je ľahké zbadať. Udržujte zoznam ABI v bloku Gradle v súlade s priečinkami, ktoré pridávate.
Zlaďte release artefakt s testovacím zostavením
Debug APK dokáže zdvihnúť tunel, zatiaľ čo release bundle stále padá, pretože bundle balí natívne knižnice iným tvarom a môže ich zo archívu servírovať komprimované. Ak zlyhávajú len zostavenia z bundle, pridajte toto do gradle.properties a znovu zostavte:
android.bundle.enableUncompressedNativeLibs=falsePotom potvrďte, že nainštalovaný balík hlási ABI, ktoré očakávate, a zopakujte kontrolu na 32-bitovom a 64-bitovom zariadení, ak dodávate obe. Správca balíkov uvedie zvolené ABI, takže zariadenie, ktoré spadlo na 32 bitov, sa objaví tu, a nie v správe o páde:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiChýbajúca knižnica pre jedno ABI reprodukuje pôvodnú chybu prístupu len na tom zariadení, a preto jeden úspešný testovací telefón nie je dôkaz. Dodávajte príznak manifestu, názov knižnice a nastavenie bundle spoločne; zmena jedného a zabudnutie ostatných vás vráti k tomu istému pádu. Trvalým riešením je prejsť na udržiavané vydanie jadra namiesto večného nosenia záplaty.