Appen virket på Android 9 og dør på samme telefon etter oppdateringen til Android 10. Profilen din er urørt, sertifikatene er de samme, og krasjet kommer før noen autentiseringstrafikk. Logcat viser først en null-peker, og noen linjer over den står Cannot run program ...: error=13, Permission denied. Den meldingen handler om å kjøre en fil, ikke om å lese konfigurasjonen din.

Hva feil 13 egentlig avviser

Android 10, API-nivå 29, stoppet apper fra å kjøre filer de hadde skrevet i sin egen datamappe. Regelen rammer lager som først er skrivbart og deretter kjørbart, og det er nøyaktig det en oppstarter som kopierer ut av assets lager. Eldre bygg av ics-openvpn-kjernen pakker ut en kjørbar fil openvpn fra APK-en ved første kjøring og starter den med ProcessBuilder. Uttpakkingen lykkes fortsatt; det er kjøringen som avvises. En app som retter seg mot API 29 arver regelen selv når den kjører på en enhet som kom med en eldre versjon, og derfor kan en oppdatering ødelegge et bygg som ingen har rørt.

Ingenting ved avvisningen er spesifikt for ett mappenavn. En fil appen har opprettet under datamappen sin behandles som ikke kjørbar uansett sti, så getFilesDir(), getCacheDir() og en egen undermappe feiler alle på samme måte.

Skill hjelperens krasj fra et profilproblem

Les loggen rundt prosessstarten i stedet for å rulle fra toppen. Signaturen du leter etter er linjen error=13, og stakksporet over den navngir vanligvis oppstartermetoden som kalte ProcessBuilder:

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

Hvis hjelperen aldri startet, ser du ingen OpenVPN-logutskrift i det hele tatt: ingen chiffer-forhandling, ingen dyttede ruter, ingen DNS-konfigurasjon. Hvis de linjene likevel dukker opp og feilen kommer senere, kjørte binæren, og problemet ligger i profilen, serveren eller rutingslaget. Den ene forskjellen sparer mye tid som ellers ville gått med til å redigere filer .ovpn som aldri var skyldige. Den forteller deg også hvilken av de to rettelsene nedenfor du faktisk trenger.

Sjekk også API-nivået på enheten du tester, ikke det i byggekonfigurasjonen din. Et emulatorbilde på API 28 gjenskaper ingenting, fordi begrensningen først biter fra 29 og oppover. Sjekk samtidig mål-SDK-en i byggefilen din: det er hevingen av den som drar regelen inn.

adb shell getprop ro.build.version.sdk

Hvorfor det ikke hjelper å kopiere binæren et annet sted

Den åpenbare omveien, å skrive hjelperen til en annen skrivbar mappe, feiler av samme grunn som originalen. Android tillater fortsatt å kjøre delte biblioteker som er installert i appens innebygde bibliotekmappe, fordi installasjonsprogrammet pakker dem i stedet for at de skrives ved kjøring. Den mappen er det ene stedet oppstarteren bør peke på. Alt du selv pakker ut ved kjøring, uansett hva du kaller det, blir i den avviste kategorien.

Eksternt lager er verre: det er montert med noexec, og en hjelper som ligger der kan ikke startes av noen app. Å tømme appdata gjenoppretter heller ikke den gamle oppførselen, fordi neste kjøring bare lager den samme ikke-kjørbare kopien på nytt.

Rett oppstarteren mot et pakket bibliotek

Gi hjelperen nytt navn til et delt-biblioteknavn og lever den under jniLibs for hver ABI du støtter, og la deretter oppstarteren returnere stien til det installerte biblioteket fra API 28 og oppover:

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

Biblioteket må pakkes ut ved installasjon for at den stien skal inneholde en ekte fil, så sett android:extractNativeLibs="true" på elementet <application>. Hvis en manifest-sammenføyning drar inn en annen verdi, legg til tools:replace="android:extractNativeLibs" ved siden av. referansen for application-elementet dokumenterer hva attributtet styrer.

Pass på en duplisert lokal variabel når du patcher oppstarteren for hånd. Eldre kilder deklarerer nativeAPI senere i samme metode, og en andre deklarasjon er en kompileringsfeil og ikke en kjørefeil, så bygget feiler raskt og feilen er lett å se. Hold ABI-listen i Gradle-blokken i takt med mappene du legger til.

Få utgivelsesartefakten til å matche testbygget

En debug-APK kan reise tunnelen mens en utgivelses-bundle fortsatt krasjer, fordi en bundle pakker innebygde biblioteker i en annen form og kan levere dem komprimert fra arkivet. Hvis bare bundle-avledede bygg feiler, legg til dette i gradle.properties og bygg på nytt:

android.bundle.enableUncompressedNativeLibs=false

Bekreft deretter at den installerte pakken rapporterer ABI-en du forventer, og gjenta kontrollen på en 32-biters og en 64-biters enhet hvis du leverer begge. Pakkebehandleren navngir ABI-en den valgte, så en enhet som falt tilbake til 32 bit dukker opp her i stedet for i en krasjrapport:

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

Et manglende bibliotek for én ABI gjenskaper den opprinnelige tilgangsfeilen bare på den enheten, og derfor er én vellykket testtelefon ikke bevis. Lever manifestflagget, biblioteknavnet og bundle-innstillingen sammen; endrer du én og glemmer de andre, er du tilbake ved samme krasj. Den varige løsningen er å gå over til en vedlikeholdt kjerneutgivelse i stedet for å bære patchen for alltid.

Relaterte artikler