O binario auxiliar de OpenVPN falla en Android 10: permiso denegado
Android 10 négase a executar un auxiliar que a aplicación escribiu no seu propio almacenamento. Le logcat, entrega o auxiliar como biblioteca nativa e arranxa as compilacións de bundle.
A aplicación funcionaba en Android 9 e morre no mesmo teléfono tras a actualización a Android 10. O teu perfil está intacto, os certificados son os mesmos e o fallo chega antes de calquera tráfico de autenticación. Logcat mostra primeiro un punteiro nulo, e unhas liñas máis arriba está Cannot run program ...: error=13, Permission denied. Esa mensaxe trata de executar un ficheiro, non de ler a túa configuración.
Que é o que realmente rexeita o erro 13
Android 10, nivel de API 29, impediu que as aplicacións executasen ficheiros que escribían no seu propio directorio de datos. A regra apunta ao almacenamento que primeiro é escribible e despois executable, que é exactamente o que crea un lanzador que copia desde assets. As compilacións antigas do núcleo ics-openvpn descomprimen un executable openvpn do APK no primeiro arranque e iníciano con ProcessBuilder. A descompresión segue funcionando; o que se rexeita é a execución. Unha aplicación que apunta á API 29 herda a regra mesmo cando se executa nun dispositivo que saíu cunha versión anterior, e por iso unha actualización pode romper unha compilación que ninguén tocou.
Nada do rexeitamento é específico dun nome de cartafol. Un ficheiro que a aplicación creou baixo o seu directorio de datos trátase como non executable sexa cal sexa a ruta, así que getFilesDir(), getCacheDir() e un subdirectorio propio fallan todos igual.
Separa o fallo do auxiliar dun problema de perfil
Le o rexistro arredor do arranque do proceso en vez de desprazarte desde arriba. A sinatura que buscas é a liña error=13, e a traza de pila que hai enriba adoita nomear o método do lanzador que chamou a ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Se o auxiliar nunca arrancou, non verás ningunha saída do rexistro de OpenVPN: nin negociación de cifrado, nin rutas empurradas, nin configuración de DNS. Se esas liñas si aparecen e o fallo chega despois, o binario executouse e o problema está no perfil, no servidor ou na capa de encamiñamento. Esa única distinción aforra moito tempo que se iría en editar ficheiros .ovpn que nunca tiñan a culpa. Tamén che di cal dos dous arranxos de abaixo necesitas de verdade.
Comproba tamén o nivel de API no dispositivo que probas, non o da túa configuración de compilación. Unha imaxe de emulador na API 28 non reproduce nada, porque a restrición só morde a partir da 29. Comproba á vez o SDK obxectivo do teu ficheiro de compilación: subilo é o que arrastra a regra.
adb shell getprop ro.build.version.sdkPor que copiar o binario a outro sitio non axuda
A solución obvia, escribir o auxiliar noutro directorio escribible, falla polo mesmo motivo polo que fallou o orixinal. Android segue permitindo executar bibliotecas compartidas instaladas no directorio de bibliotecas nativas da aplicación, porque as empaqueta o instalador en lugar de escribirse en tempo de execución. Ese directorio é o único sitio ao que debería apuntar o lanzador. Todo o que descomprimas ti mesmo en tempo de execución, chámalle como lle chames, queda na categoría rexeitada.
O almacenamento externo é peor: está montado con noexec, e un auxiliar colocado alí non pode inicialo ningunha aplicación. Borrar os datos da aplicación tampouco restaura o comportamento antigo, porque o seguinte arranque simplemente volve crear a mesma copia non executable.
Apunta o lanzador a unha biblioteca empaquetada
Renomea o auxiliar a un nome de biblioteca compartida e entrégao baixo jniLibs para cada ABI que admitas, e logo fai que o lanzador devolva a ruta da biblioteca instalada desde a API 28 en diante:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}A biblioteca ten que descomprimirse no momento de instalar para que esa ruta conteña un ficheiro real, así que define android:extractNativeLibs="true" no elemento <application>. Se un fusionador de manifesto introduce outro valor, engade tools:replace="android:extractNativeLibs" ao lado. A referencia do elemento application documenta que controla o atributo.
Vixía unha variable local duplicada cando parcheas o lanzador a man. As fontes antigas declaran nativeAPI máis adiante no mesmo método, e unha segunda declaración é un erro de compilación, non de execución, así que a compilación falla rápido e o erro é fácil de ver. Mantén a lista de ABI do bloque de Gradle en sintonía cos cartafoles que engades.
Fai que o artefacto de versión coincida coa compilación de proba
Un APK de depuración pode levantar o túnel mentres un bundle de versión segue fallando, porque un bundle empaqueta as bibliotecas nativas doutra forma e pode servilas comprimidas desde o arquivo. Se só fallan as compilacións derivadas do bundle, engade isto a gradle.properties e volve compilar:
android.bundle.enableUncompressedNativeLibs=falseLogo confirma que o paquete instalado informa da ABI que esperas, e repite a comprobación nun dispositivo de 32 bits e outro de 64 bits se distribúes ambos. O xestor de paquetes nomea a ABI que escolleu, así que un dispositivo que caeu a 32 bits aparece aquí en lugar de nun informe de fallo:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiUnha biblioteca que falta para unha ABI reproduce o erro de permiso orixinal só nese dispositivo, e por iso un só teléfono de proba que funcione non é proba. Distribúe a marca do manifesto, o nome da biblioteca e o axuste do bundle xuntos; cambiar un e esquecer os outros devólveche ao mesmo fallo. A solución duradeira é pasar a unha versión do núcleo mantida en lugar de arrastrar o parche para sempre.