Pomocný binární soubor OpenVPN na Androidu 10 selhává: přístup odepřen
Android 10 odmítá spustit pomocný soubor, který aplikace zapsala do vlastního úložiště. Přečtěte si logcat, dodejte pomocník jako nativní knihovnu a opravte sestavení bundle.
Aplikace fungovala na Androidu 9 a na stejném telefonu umírá po aktualizaci na Android 10. Váš profil je nedotčený, certifikáty jsou stejné a pád přichází před jakýmkoli autentizačním provozem. Logcat nejprve ukáže nulový ukazatel a o několik řádků výš stojí Cannot run program ...: error=13, Permission denied. Ta zpráva je o spuštění souboru, ne o čtení vaší konfigurace.
Co vlastně chyba 13 odmítá
Android 10, úroveň API 29, zastavil aplikacím spouštění souborů, které zapsaly do svého vlastního datového adresáře. Pravidlo cílí na úložiště, které je nejprve zapisovatelné a potom spustitelné, což je přesně to, co vytváří zavaděč kopírující z assets. Starší sestavení jádra ics-openvpn rozbalí z APK při prvním spuštění spustitelný soubor openvpn a spustí ho pomocí ProcessBuilder. Rozbalení stále proběhne; odmítnuto je právě spuštění. Aplikace cílící na API 29 pravidlo dědí, i když běží na zařízení, které vyšlo se starší verzí, a proto může aktualizace rozbít sestavení, kterého se nikdo nedotkl.
Na odmítnutí není nic specifické pro jeden název složky. Soubor, který aplikace vytvořila ve svém datovém adresáři, se považuje za nespustitelný bez ohledu na cestu, takže getFilesDir(), getCacheDir() i vlastní podadresář selhávají stejně.
Oddělte pád pomocníka od problému s profilem
Čtěte log kolem spuštění procesu, místo abyste rolováním začínali od začátku. Hledaný podpis je řádek error=13 a výpis zásobníku nad ním obvykle pojmenuje metodu zavaděče, která zavolala ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Pokud se pomocník nikdy nespustil, neuvidíte vůbec žádný výstup logu OpenVPN: žádné vyjednávání šifer, žádné protlačené trasy, žádnou konfiguraci DNS. Pokud se ty řádky objeví a selhání přijde později, binárka běžela a problém je v profilu, serveru nebo vrstvě směrování. Právě tento rozdíl ušetří hodně času stráveného úpravami souborů .ovpn, které nikdy nebyly vinny. Také vám řekne, kterou ze dvou oprav níže skutečně potřebujete.
Zkontrolujte také úroveň API na zařízení, které testujete, ne tu ve své konfiguraci sestavení. Obraz emulátoru na API 28 nic nereprodukuje, protože omezení působí až od 29 výš. Zároveň zkontrolujte cílové SDK ve svém souboru sestavení: právě jeho zvýšení toto pravidlo přitahuje.
adb shell getprop ro.build.version.sdkProč kopírování binárky jinam nepomáhá
Zjevný obchvat, zapsání pomocníka do jiného zapisovatelného adresáře, selhává ze stejného důvodu jako originál. Android stále povoluje spouštění sdílených knihoven nainstalovaných do adresáře nativních knihoven aplikace, protože je zabaluje instalátor, a ne že by se zapisovaly za běhu. Právě na tento adresář by měl zavaděč ukazovat. Cokoli si za běhu rozbalíte sami, ať to nazvete jakkoli, zůstává v odmítnuté kategorii.
Externí úložiště je horší: je připojeno s noexec a pomocníka tam nemůže spustit žádná aplikace. Vymazání dat aplikace také neobnoví staré chování, protože příští spuštění prostě znovu vytvoří stejnou nespustitelnou kopii.
Nasměrujte zavaděč na zabalenou knihovnu
Přejmenujte pomocníka na název sdílené knihovny a dodejte ho pod jniLibs pro každé ABI, které podporujete, a pak nechte zavaděč vracet cestu k nainstalované knihovně od API 28 výš:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}Knihovna musí být rozbalena při instalaci, aby ta cesta obsahovala skutečný soubor, takže nastavte android:extractNativeLibs="true" na prvku <application>. Pokud slučovač manifestů přinese jinou hodnotu, přidejte vedle tools:replace="android:extractNativeLibs". referenční příručka prvku application dokumentuje, co tento atribut řídí.
Dejte pozor na duplikovanou lokální proměnnou, když zavaděč záplatujete ručně. Starší zdroje deklarují nativeAPI později ve stejné metodě a druhá deklarace je chyba kompilace, nikoli běhu, takže sestavení rychle selže a chybu je snadné odhalit. Udržujte seznam ABI v bloku Gradle v souladu se složkami, které přidáváte.
Slaďte release artefakt s testovacím sestavením
Ladicí APK dokáže zvednout tunel, zatímco release bundle stále padá, protože bundle balí nativní knihovny v jiném tvaru a může je z archivu servírovat komprimované. Pokud selhávají jen sestavení z bundle, přidejte toto do gradle.properties a znovu sestavte:
android.bundle.enableUncompressedNativeLibs=falsePotom potvrďte, že nainstalovaný balíček hlásí ABI, které očekáváte, a opakujte kontrolu na 32bitovém a 64bitovém zařízení, pokud dodáváte obě. Správce balíčků uvede ABI, které vybral, takže zařízení, které spadlo na 32 bitů, se objeví tady, a ne ve zprávě o pádu:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiChybějící knihovna pro jedno ABI reprodukuje původní chybu přístupu jen na tom zařízení, a proto jeden úspěšný testovací telefon není důkaz. Dodávejte příznak manifestu, název knihovny i nastavení bundle společně; změna jednoho a zapomenutí ostatních vás vrátí ke stejnému pádu. Trvalým řešením je přejít na udržované vydání jádra, místo abyste záplatu nosili navěky.