Binari ndihmës i OpenVPN dështon në Android 10: leja u refuzua
Android 10 refuzon të ekzekutojë një ndihmës që aplikacioni shkroi në ruajtësen e vet. Lexo logcat, dërgo ndihmësin si bibliotekë amtare dhe rregullo ndërtimet bundle.
Aplikacioni punonte në Android 9 dhe vdes në të njëjtin telefon pas përditësimit në Android 10. Profili yt është i paprekur, certifikatat janë të njëjtat, dhe rrëzimi vjen para çdo trafiku vërtetimi. Logcat shfaq fillimisht një tregues null, dhe disa rreshta sipër tij qëndron Cannot run program ...: error=13, Permission denied. Ai mesazh ka të bëjë me ekzekutimin e një skedari, jo me leximin e konfigurimit tënd.
Çfarë refuzon në të vërtetë gabimi 13
Android 10, niveli API 29, i ndaloi aplikacionet të ekzekutojnë skedarë që i shkruanin në drejtorinë e tyre të të dhënave. Rregulli synon ruajtësen që së pari është e shkrueshme dhe më pas e ekzekutueshme, dhe pikërisht këtë krijon një nisës që kopjon nga assets. Ndërtimet e vjetra të bërthamës ics-openvpn shpaketojnë në nisjen e parë nga APK një skedar të ekzekutueshëm openvpn dhe e nisin me ProcessBuilder. Shpaketimi ende ka sukses; ajo që refuzohet është ekzekutimi. Një aplikacion që synon API 29 e trashëgon rregullin edhe kur punon në një pajisje që doli me një version më të vjetër, prandaj një përditësim mund të thyejë një ndërtim që askush nuk e preku.
Asgjë në refuzim nuk është specifike për një emër dosjeje. Një skedar që aplikacioni krijoi nën drejtorinë e të dhënave trajtohet si i paekzekutueshëm pavarësisht shtegut, kështu që getFilesDir(), getCacheDir() dhe një nëndrejtori e personalizuar të gjitha dështojnë në të njëjtën mënyrë.
Ndaj rrëzimin e ndihmësit nga një problem profili
Lexo regjistrin rreth nisjes së procesit në vend që të lëvizësh nga maja. Nënshkrimi që kërkon është rreshti error=13, dhe gjurmja e stivit sipër tij zakonisht emërton metodën e nisësit që thirri ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Nëse ndihmësi nuk u nis kurrë, nuk do të shohësh fare dalje regjistri nga OpenVPN: as negocim shifrash, as rrugë të shtyra, as konfigurim DNS. Nëse ato rreshta shfaqen dhe dështimi vjen më vonë, binari punoi dhe problemi është në profil, në server ose në shtresën e rrugëzimit. Ai dallim i vetëm kursen shumë kohë që përndryshe do të shkonte për përpunimin e skedarëve .ovpn që nuk ishin kurrë fajtorë. Të tregon gjithashtu cilën nga dy rregullimet më poshtë të duhet vërtet.
Kontrollo edhe nivelin API në pajisjen që teston, jo atë në konfigurimin e ndërtimit. Një imazh emulatori në API 28 nuk riprodhon asgjë, sepse kufizimi kafshon vetëm nga 29 e lart. Në të njëjtën kohë kontrollo SDK-në e synuar në skedarin e ndërtimit: ngritja e tij është ajo që e tërheq këtë rregull.
adb shell getprop ro.build.version.sdkPse kopjimi i binarit diku tjetër nuk ndihmon
Zgjidhja e dukshme, shkrimi i ndihmësit në një drejtori tjetër të shkrueshme, dështon për të njëjtën arsye si origjinali. Android ende lejon ekzekutimin e bibliotekave të përbashkëta të instaluara në drejtorinë e bibliotekave amtare të aplikacionit, sepse instaluesi i paketon dhe ato nuk shkruhen në kohë ekzekutimi. Ajo drejtori është i vetmi vend ku nisësi duhet të tregojë. Çdo gjë që shpaketon vetë në kohë ekzekutimi, si ta quash, mbetet në kategorinë e refuzuar.
Ruajtësja e jashtme është më keq: është montuar me noexec, dhe një ndihmës i vendosur atje nuk mund të niset nga asnjë aplikacion. Fshirja e të dhënave të aplikacionit gjithashtu nuk rikthen sjelljen e vjetër, sepse nisja tjetër thjesht rikrijon të njëjtën kopje të paekzekutueshme.
Drejto nisësin te një bibliotekë e paketuar
Riemërto ndihmësin në një emër biblioteke të përbashkët dhe dërgoje nën jniLibs për çdo ABI që mbështet, pastaj bëj që nisësi të kthejë shtegun e bibliotekës së instaluar nga API 28 e tutje:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}Biblioteka duhet të shpaketohet në instalim që ai shteg të përmbajë një skedar të vërtetë, prandaj vendos android:extractNativeLibs="true" te elementi <application>. Nëse një bashkues manifesti sjell një vlerë tjetër, shto tools:replace="android:extractNativeLibs" pranë tij. referenca e elementit application dokumenton çfarë kontrollon ky atribut.
Ki kujdes për një ndryshore lokale të dyfishuar kur e rregullon nisësin me dorë. Burimet e vjetra deklarojnë nativeAPI më vonë në të njëjtën metodë, dhe një deklarim i dytë është gabim kompilimi dhe jo gabim ekzekutimi, prandaj ndërtimi dështon shpejt dhe gabimi vihet re lehtë. Mbaj listën e ABI në bllokun Gradle në përputhje me dosjet që shton.
Bëj që artefakti i lëshimit të përputhet me ndërtimin e testit
Një APK debug mund të ngrejë tunelin ndërsa një bundle lëshimi ende rrëzohet, sepse bundle paketon bibliotekat amtare në një formë tjetër dhe mund t'i shërbejë të kompresuara nga arkivi. Nëse dështojnë vetëm ndërtimet që vijnë nga bundle, shto këtë në gradle.properties dhe rindërto:
android.bundle.enableUncompressedNativeLibs=falsePastaj konfirmo që paketa e instaluar raporton ABI-në që pret, dhe përsërit kontrollin në një pajisje 32-bit dhe 64-bit nëse i dërgon të dyja. Menaxheri i paketave emërton ABI-në që zgjodhi, prandaj një pajisje që ra përsëri në 32 bit shfaqet këtu dhe jo në një raport rrëzimi:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiNjë bibliotekë që mungon për një ABI riprodhon gabimin origjinal të lejes vetëm në atë pajisje, prandaj një telefon testimi që funksionon nuk është provë. Dërgo flamurin e manifestit, emrin e bibliotekës dhe cilësimin e bundle së bashku; ndrysho një dhe harro të tjerat dhe kthehesh te i njëjti rrëzim. Rregullimi i qëndrueshëm është kalimi në një lëshim bërthame të mirëmbajtur, në vend që ta mbash arnën përgjithmonë.