OpenVPN:n apubinaari epäonnistuu Android 10:ssä: käyttö estetty
Android 10 kieltäytyy suorittamasta apuohjelmaa, jonka sovellus kirjoitti omaan tallennustilaansa. Lue logcat, toimita apuohjelma natiivina kirjastona ja korjaa bundle-koonnit.
Sovellus toimi Android 9:ssä ja kuolee samassa puhelimessa Android 10 -päivityksen jälkeen. Profiilisi on koskematon, varmenteet ovat samat ja kaatuminen tulee ennen mitään todennusliikennettä. Logcat näyttää ensin nollaviittimen, ja muutamaa riviä ylempänä on Cannot run program ...: error=13, Permission denied. Se viesti koskee tiedoston suorittamista, ei asetustesi lukemista.
Mitä virhe 13 oikeastaan kieltäytyy tekemästä
Android 10, API-taso 29, esti sovelluksia suorittamasta tiedostoja, jotka ne kirjoittivat omaan datakansioonsa. Sääntö kohdistuu tallennustilaan, joka on ensin kirjoitettavaa ja sitten suoritettavaa, ja juuri sellaista luo käynnistin, joka kopioi assetseista. ics-openvpn-ytimen vanhemmat koönnit purkavat APK:sta suoritettavan tiedoston openvpn ensimmäisellä käynnillä ja käynnistävät sen komennolla ProcessBuilder. Purkaminen onnistuu edelleen, suorittaminen on se, mikä evätään. API 29:ään kohdistuva sovellus perii säännön, vaikka se toimisi laitteessa, joka tuli markkinoille vanhemmalla versiolla, minkä vuoksi päivitys voi rikkoa koönnin, jota kukaan ei koskenut.
Mikään kieltäytymisessä ei ole ominaista yhdelle kansiolle. Tiedostoa, jonka sovellus loi datakansionsa alle, kohdellaan ei-suoritettavana polusta riippumatta, joten getFilesDir(), getCacheDir() ja mukautettu alikansio epäonnistuvat kaikki samalla tavalla.
Erota apuohjelman kaatuminen profiiliongelmasta
Lue loki prosessin käynnistymisen ympäriltä sen sijaan, että vierittäisit alusta. Etsittävä allekirjoitus on rivi error=13, ja sen yläpuolella oleva pinon jäljitys nimeää yleensä käynnistinmenetelmän, joka kutsui ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Jos apuohjelma ei koskaan käynnistynyt, et näe lainkaan OpenVPN:n lokitulostetta: ei salausneuvottelua, ei työnnettyjä reittejä, ei DNS-asetuksia. Jos ne rivit kuitenkin ilmestyvät ja epäonnistuminen tulee myöhemmin, binaari toimi ja ongelma on profiilissa, palvelimessa tai reitityskerroksessa. Se yksi ero säästää paljon aikaa, joka muuten kuluisi sellaisten .ovpn-tiedostojen muokkaamiseen, jotka eivät koskaan olleet syyllisiä. Se kertoo myös, kumpaa alla olevista kahdesta korjauksesta todella tarvitset.
Tarkista myös API-taso laitteesta, jota testaat, et sitä, joka on koönnin asetuksissasi. API 28:n emulaattorikuva ei toista mitään, koska rajoitus puree vasta 29:stä ylöspäin. Tarkista samalla koontitiedostosi kohde-SDK: sen nostaminen on se, mikä vetää säännön mukaan.
adb shell getprop ro.build.version.sdkMiksi binaarin kopioiminen muualle ei auta
Ilmeinen kiertotie, apuohjelman kirjoittaminen toiseen kirjoitettavaan kansioon, epäonnistuu samasta syystä kuin alkuperäinen. Android sallii edelleen sellaisten jaettujen kirjastojen suorittamisen, jotka on asennettu sovelluksen natiivien kirjastojen kansioon, koska asentaja pakkaa ne eikä niitä kirjoiteta ajonaikana. Juuri se kansio on ainoa paikka, johon käynnistimen pitäisi osoittaa. Kaikki, mitä itse purat ajonaikana, miksi ikinä sitä kutsutkin, pysyy evätyssä kategoriassa.
Ulkoinen tallennustila on pahempi: se on liitetty noexec-vaihtoehdolla, eikä sinne sijoitettua apuohjelmaa voi käynnistää mikään sovellus. Sovelluksen tietojen tyhjentäminen ei sekään palauta vanhaa käytöstä, koska seuraava käynnistys yksinkertaisesti luo saman ei-suoritettavan kopion uudelleen.
Osoita käynnistin pakattuun kirjastoon
Nimeä apuohjelma jaetun kirjaston nimeksi ja toimita se jniLibs-kansion alle jokaiselle tukemallesi ABI:lle, ja anna sitten käynnistimen palauttaa asennetun kirjaston polku API 28:sta alkaen:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}Kirjasto on purettava asennuksen yhteydessä, jotta polku sisältäisi todellisen tiedoston, joten aseta android:extractNativeLibs="true" elementtiin <application>. Jos manifestien yhdistäjä tuo toisen arvon, lisää tools:replace="android:extractNativeLibs" sen viereen. application-elementin viite kertoo, mitä attribuutti ohjaa.
Varo kahdennettua paikallista muuttujaa, kun paikkaat käynnistintä käsin. Vanhemmat lähteet julistavat nativeAPI myöhemmin samassa menetelmässä, ja toinen julistus on käännösvirhe eikä ajonaikainen virhe, joten koonti epäonnistuu nopeasti ja virhe on helppo huomata. Pidä Gradle-lohkon ABI-luettelo synkronissa lisäämiesi kansioiden kanssa.
Sovita julkaisuartefakti testikoontiin
Debug-APK voi nostaa tunnelin, kun julkaisubundle kaatuu edelleen, koska bundle pakkaa natiivit kirjastot eri muotoon ja voi tarjoilla ne pakattuina arkistosta. Jos vain bundle-pohjaiset koönnit epäonnistuvat, lisää tämä gradle.properties-tiedostoon ja kokoa uudelleen:
android.bundle.enableUncompressedNativeLibs=falseVahvista sitten, että asennettu paketti ilmoittaa odottamasi ABI:n, ja toista tarkistus 32- ja 64-bittisellä laitteella, jos toimitat molemmat. Paketinhallinta nimeää valitsemansa ABI:n, joten 32 bittiin pudonnut laite näkyy täällä eikä kaatumisraportissa:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiYhdelle ABI:lle puuttuva kirjasto toistaa alkuperäisen käyttöoikeusvirheen vain sillä laitteella, minkä vuoksi yksi onnistunut testipuhelin ei ole todiste. Toimita manifestin lippu, kirjaston nimi ja bundle-asetus yhdessä, jos muutat yhtä ja unohdat muut, olet taas samassa kaatumisessa. Kestävä ratkaisu on siirtyä ylläpidettyyn ydinjulkaisuun sen sijaan, että kantaisit paikkausta ikuisesti.