Помоћни бинарни програм OpenVPN не ради на Android-у 10: приступ одбијен
Android 10 одбија да изврши помоћни програм који је апликација уписала у сопствено складиште. Прочитајте logcat, доставите помоћник као нативну библиотеку и поправите bundle-изградње.
Апликација је радила на Android-у 9 и умире на истом телефону након надоградње на Android 10. Ваш профил је нетакнут, сертификати су исти, а пад долази пре било каквог саобраћаја аутентификације. Logcat прво приказује нулти показивач, а неколико редова изнад стоји Cannot run program ...: error=13, Permission denied. Та порука је о извршавању датотеке, не о читању ваше конфигурације.
Шта грешка 13 заправо одбија
Android 10, ниво API-ја 29, зауставио је апликације у извршавању датотека које су уписале у сопствени директоријум података. Правило циља на складиште које је прво уписиво, а затим извршиво, и управо то ствара покретач који копира из assets. Старије изградње језгра ics-openvpn распакују при првом покретању из APK-а извршну датотеку openvpn и покрећу је помоћу ProcessBuilder. Распакивање и даље успева; одбијено је управо извршавање. Апликација која циља API 29 наслеђује правило чак и када ради на уређају који је изашао са старијом верзијом, зато једна надоградња може да поквари изградњу коју нико није дотакао.
Ништа у одбијању није специфично за једно име фасцикле. Датотека коју је апликација направила у свом директоријуму података третира се као неизвршива без обзира на путању, па зато getFilesDir(), getCacheDir() и прилагођени поддиректоријум сви не успевају на исти начин.
Одвојите пад помоћника од проблема са профилом
Читајте дневник око покретања процеса уместо да скролујете од врха. Потпис који тражите је ред error=13, а траг стека изнад њега обично именује методу покретача која је позвала ProcessBuilder:
adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"Ако се помоћник никада није покренуо, нећете видети никакав излаз дневника OpenVPN-а: ни преговарање о шифрама, ни послате руте, ни DNS конфигурацију. Ако се ти редови ипак појаве и неуспех дође касније, бинарни програм је радио и проблем је у профилу, серверу или слоју рутирања. Та једна разлика штеди много времена које би иначе отишло на уређивање датотека .ovpn које никада нису биле криве. Она вам такође каже која од две исправке испод вам је заиста потребна.
Проверите и ниво API-ја на уређају који тестирате, а не онај у вашој конфигурацији изградње. Слика емулатора на API 28 не репродукује ништа, јер ограничење делује само од 29 навише. Истовремено проверите циљни SDK у датотеци изградње: управо његово подизање уводи ово правило.
adb shell getprop ro.build.version.sdkЗашто копирање бинарног програма на друго место не помаже
Очигледно заобилажење, уписивање помоћника у други уписив директоријум, не успева из истог разлога као оригинал. Android још увек дозвољава извршавање дељених библиотека инсталираних у директоријум нативних библиотека апликације, јер их пакује инсталатер, а не уписују се у време извршавања. Управо тај директоријум је једино место на које покретач треба да показује. Све што сами распакујете у време извршавања, како год то назвали, остаје у одбијеној категорији.
Спољашње складиште је горе: монтирано је са noexec, а помоћник постављен тамо не може да покрене ниједна апликација. Брисање података апликације такође не враћа старо понашање, јер следеће покретање једноставно поново ствара исту неизвршиву копију.
Усмерите покретач на спаковану библиотеку
Преименујте помоћник у име дељене библиотеке и доставите га под jniLibs за сваки ABI који подржавате, а затим нека покретач од API 28 надаље враћа путању инсталиране библиотеке:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return new File(context.getApplicationInfo().nativeLibraryDir,
"libovpnexec.so").getPath();
}Библиотека мора да се распакује при инсталацији да би та путања садржала стварну датотеку, зато поставите android:extractNativeLibs="true" на елементу <application>. Ако спајач манифеста унесе другу вредност, додајте tools:replace="android:extractNativeLibs" поред ње. референца елемента application документује шта контролише тај атрибут.
Пазите на дуплирану локалну променљиву када покретач крпите ручно. Старији извори декларишу nativeAPI касније у истој методи, а друга декларација је грешка при компајлирању, не при извршавању, тако да изградња брзо не успева и грешку је лако приметити. Држите листу ABI-ја у Gradle блоку усклађену са фасциклама које додајете.
Ускладите артефакт издања са тестном изградњом
Debug APK може да подигне тунел док release bundle још увек пада, јер bundle пакује нативне библиотеке у другачијем облику и може да их служи компресоване из архиве. Ако не успевају само изградње настале из bundle-а, додајте ово у gradle.properties и поново изградите:
android.bundle.enableUncompressedNativeLibs=falseЗатим потврдите да инсталирани пакет пријављује ABI који очекујете, и поновите проверу на 32-битном и 64-битном уређају ако достављате оба. Менаџер пакета наводи изабрани ABI, па се уређај који је пао на 32 бита појављује овде, а не у извештају о паду:
adb shell dumpsys package com.example.openvpn | grep -i primaryCpuAbiБиблиотека која недостаје за један ABI репродукује оригиналну грешку приступа само на том уређају, зато један успешан тест телефон није доказ. Доставите заставицу манифеста, име библиотеке и поставку bundle-а заједно; промена једног и заборављање осталих враћа вас на исти пад. Трајно решење је прелазак на одржавано издање језгра, уместо да закрпу носите заувек.