Appen virkede på Android 9 og dør på den samme telefon efter opdateringen til Android 10. Din profil er urørt, certifikaterne er de samme, og nedbruddet kommer før enhver godkendelsestrafik. Logcat viser først en nul-pointer, og et par linjer over den står Cannot run program ...: error=13, Permission denied. Den besked handler om at køre en fil, ikke om at læse din konfiguration.

Hvad fejl 13 egentlig afviser

Android 10, API-niveau 29, stoppede apps med at køre filer, som de havde skrevet i deres egen datamappe. Reglen rammer lager, der først kan skrives til og derefter køres, hvilket er præcis det, en launcher der kopierer ud af assets opretter. Ældre builds af ics-openvpn-kernen pakker en kørbar fil openvpn ud af APK'en ved første kørsel og starter den med ProcessBuilder. Udpakningen lykkes stadig; det er kørslen, der afvises. En app, der retter sig mod API 29, arver reglen, selv når den kører på en enhed, der blev leveret med en ældre version, og derfor kan en opdatering ødelægge et build, som ingen har rørt.

Intet ved afvisningen er specifikt for et bestemt mappenavn. En fil, som appen har oprettet under sin datamappe, behandles som ikke-kørbar uanset stien, så getFilesDir(), getCacheDir() og en brugerdefineret undermappe fejler alle på samme måde.

Adskil hjælperens nedbrud fra et profilproblem

Læs loggen omkring processtarten i stedet for at rulle fra toppen. Signaturen, du skal søge efter, er linjen error=13, og stacktracen ovenover nævner normalt den launchemetode, der kaldte ProcessBuilder:

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

Hvis hjælperen aldrig startede, ser du slet ingen OpenVPN-logudskrift: ingen cipher-forhandling, ingen push-ruter, ingen DNS-konfiguration. Hvis de linjer derimod dukker op, og fejlen kommer senere, kørte binæren, og problemet ligger i profilen, serveren eller routinglaget. Den ene forskel sparer meget tid, der ellers ville gå med at redigere filer .ovpn, som aldrig var skyldige. Den fortæller dig også, hvilken af de to rettelser nedenfor du faktisk har brug for.

Tjek også API-niveauet på den enhed, du tester, og ikke det i din build-konfiguration. Et emulatorbillede på API 28 genskaber intet, fordi begrænsningen først bider fra 29 og opefter. Tjek samtidig mål-SDK'en i din build-fil: det er hævningen af den, der trækker reglen ind.

adb shell getprop ro.build.version.sdk

Hvorfor det ikke hjælper at kopiere binæren et andet sted hen

Den oplagte omvej, at skrive hjælperen til en anden skrivbar mappe, fejler af samme grund som originalen. Android tillader stadig at køre delte biblioteker, der er installeret i appens native-bibliotekmappe, fordi installationsprogrammet pakker dem i stedet for at skrive dem ved kørsel. Den mappe er det ene sted, launcheren bør pege på. Alt, du selv pakker ud ved kørsel, uanset hvad du kalder det, forbliver i den afviste kategori.

Eksternt lager er værre: det er monteret med noexec, og en hjælper placeret der kan ikke startes af nogen app. Rydning af appdata genskaber heller ikke den gamle adfærd, fordi næste kørsel bare genskaber den samme ikke-kørbare kopi.

Ret launcheren mod et pakket bibliotek

Omdøb hjælperen til et delt-biblioteknavn og lever den under jniLibs for hver ABI, du understøtter, og lad derefter launcheren returnere stien til det installerede bibliotek fra API 28 og frem:

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

Biblioteket skal pakkes ud ved installation, for at den sti skal indeholde en rigtig fil, så sæt android:extractNativeLibs="true" på elementet <application>. Hvis en manifest-sammenlægning trækker en anden værdi ind, tilføj tools:replace="android:extractNativeLibs" ved siden af. referencen for application-elementet dokumenterer, hvad attributten styrer.

Pas på en duplikeret lokal variabel, når du patcher launcheren i hånden. Ældre kilder erklærer nativeAPI senere i samme metode, og en anden erklæring er en kompileringsfejl snarere end en kørselsfejl, så buildet fejler hurtigt, og fejlen er let at se. Hold ABI-listen i Gradle-blokken i takt med de mapper, du tilføjer.

Få release-artefakten til at matche testbuildet

En debug-APK kan starte tunnelen, mens en release-bundle stadig crasher, fordi en bundle pakker native-biblioteker i en anden form og kan levere dem komprimerede fra arkivet. Hvis kun bundle-afledte builds fejler, tilføj dette til gradle.properties og genopbyg:

android.bundle.enableUncompressedNativeLibs=false

Bekræft derefter, at den installerede pakke rapporterer den ABI, du forventer, og gentag tjekket på en 32-bit og en 64-bit enhed, hvis du leverer begge. Pakkehåndteringen angiver den ABI, den valgte, så en enhed, der faldt tilbage til 32-bit, dukker op her i stedet for i en nedbrudsrapport:

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

Et manglende bibliotek til én ABI genskaber den oprindelige tilladelsesfejl på den enhed alene, og derfor er én vellykket testtelefon ikke bevis. Lever manifestflaget, biblioteknavnet og bundle-indstillingen sammen; ændrer du én og glemmer de andre, er du tilbage ved samme nedbrud. Den varige løsning er at skifte til en vedligeholdt kernel-udgivelse i stedet for at slæbe på patchen for evigt.

Relaterede artikler