Апп 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 аппын native сангийн хавтаст суулгасан хуваалцсан сангуудыг ажиллуулахыг зөвшөөрсөн хэвээр байгаа, учир нь тэдгээрийг суулгагч багцалдаг, ажиллах үед бичдэггүй. Яг тэр хавтас бол эхлүүлэгчийн заах ёстой цорын ганц газар. Ажиллах үед та өөрөө задалж буй бүхэн, ямар ч нэр өгсөн, татгалзсан ангилалд үлддэг.

Гадаад сан нь улам дор: тэр noexec-ээр холбогдсон, тэнд тавьсан туслахыг ямар ч апп эхлүүлж чадахгүй. Аппын өгөгдлийг цэвэрлэх нь хуучин зан төлөвийг сэргээхгүй, учир нь дараагийн ажиллуулалт яг тэр ажиллагдахгүй хуулбарыг дахин үүсгэдэг.

Эхлүүлэгчийг багцлагдсан сан руу чиглүүл

Туслахыг хуваалцсан сангийн нэр болгон өөрчилж, дэмждэг ABI бүрд нь jniLibs-ийн дор нийлүүл, дараа нь эхлүүлэгч API 28-аас эхлэн суулгасан сангийн замыг буцаадаг болго:

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

Тэр замд бодит файл байхын тулд санг суулгах үед задлах ёстой, тиймээс android:extractNativeLibs="true"-ыг <application> элемент дээр тохируул. Хэрэв manifest нэгтгэгч өөр утга авчирвал түүний хажууд tools:replace="android:extractNativeLibs" нэм. application элементийн лавлагаа энэ шинж чанар юуг удирддагийг баримтжуулдаг.

Эхлүүлэгчийг гараар нөхөхдөө давхардсан локал хувьсагчаас болгоомжил. Хуучин эх сурвалжууд ижил аргад дараа нь nativeAPI гэж зарладаг, хоёр дахь зарлал нь ажиллах үеийн биш, эмхэтгэлийн алдаа, тиймээс бүтээц хурдан бүтэлгүйтэж, алдааг амархан анзаарна. Gradle блок дахь ABI жагсаалтыг нэмж буй хавтастуудтайгаа нийцүүлэн байлга.

Гаргалын артефактыг туршилтын бүтээцтэй нийцүүл

Дибаг APK нь туннелийг өргөж чадах боловч гаргалын bundle одоо ч краш болдог, учир нь bundle нь native сангуудыг өөр хэлбэрээр багцалж, архиваас шахсан байдлаар өгч болно. Зөвхөн bundle-ээс гаралтай бүтээцүүд бүтэлгүйтвэл үүнийг gradle.properties-д нэмээд дахин бүтээ:

android.bundle.enableUncompressedNativeLibs=false

Дараа нь суулгасан багц хүлээж буй ABI-г мэдээлж байгаа эсэхийг баталгаажуул, хоёуланг нь нийлүүлбэл 32 бит ба 64 бит төхөөрөмж дээр шалгалтыг давт. Багцын менежер сонгосон ABI-г нэрлэдэг, тиймээс 32 бит рүү буцсан төхөөрөмж крашийн тайланд биш, энд харагдана:

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

Нэг ABI-д сан дутагдах нь анхны хандалтын алдааг зөвхөн тэр төхөөрөмж дээр дахин үүсгэдэг, тиймээс нэг амжилттай туршилтын утас бол нотолгоо биш. Manifest-ийн туг, сангийн нэр, bundle-ийн тохиргоог хамт нийлүүл; нэгийг өөрчилж бусдыг мартвал ижил краш руу буцна. Байнгын шийдэл нь нөхөөсийг үүрд үүрэх биш, дэмжигддэг цөмийн хувилбар руу шилжих явдал юм.

Холбоотой нийтлэлүүд