Aplikasi berjalan di Android 9 dan mati di ponsel yang sama setelah pembaruan ke Android 10. Profil Anda tidak tersentuh, sertifikatnya sama, dan crash datang sebelum lalu lintas autentikasi apa pun. Logcat menampilkan pointer null lebih dulu, dan beberapa baris di atasnya ada Cannot run program ...: error=13, Permission denied. Pesan itu soal menjalankan berkas, bukan soal membaca konfigurasi Anda.

Apa yang sebenarnya ditolak galat 13

Android 10, level API 29, menghentikan aplikasi menjalankan berkas yang mereka tulis ke direktori datanya sendiri. Aturan ini menyasar penyimpanan yang bisa ditulis lalu bisa dieksekusi, dan persis itulah yang dibuat peluncur yang menyalin dari assets. Build lama inti ics-openvpn mengekstrak berkas eksekusi openvpn dari APK pada jalannya pertama lalu menjalankannya dengan ProcessBuilder. Ekstraksinya tetap berhasil; yang ditolak adalah eksekusinya. Aplikasi yang menyasar API 29 mewarisi aturan tersebut bahkan saat berjalan di perangkat yang dirilis dengan versi lebih lama, karena itu pembaruan bisa merusak build yang tidak disentuh siapa pun.

Tidak ada yang khusus pada satu nama folder dalam penolakan ini. Berkas yang dibuat aplikasi di bawah direktori datanya diperlakukan sebagai tidak dapat dieksekusi apa pun jalurnya, jadi getFilesDir(), getCacheDir() dan subdirektori khusus semuanya gagal dengan cara yang sama.

Pisahkan crash pembantu dari masalah profil

Baca log di sekitar mulai proses alih-alih menggulir dari atas. Tanda yang dicari adalah baris error=13, dan jejak tumpukan di atasnya biasanya menyebut metode peluncur yang memanggil ProcessBuilder:

adb logcat -d | grep -n -i -E "cannot run program|permission denied|startOpenVPN"

Jika pembantu tidak pernah mulai, Anda tidak akan melihat keluaran log OpenVPN sama sekali: tidak ada negosiasi cipher, tidak ada rute yang didorong, tidak ada konfigurasi DNS. Jika baris itu muncul dan kegagalan datang belakangan, binernya berjalan dan masalahnya ada di profil, server, atau lapisan perutean. Satu pembedaan itu menghemat banyak waktu yang biasanya habis untuk menyunting berkas .ovpn yang tidak pernah bersalah. Itu juga memberi tahu Anda mana dari dua perbaikan di bawah yang benar-benar Anda butuhkan.

Periksa juga level API pada perangkat yang Anda uji, bukan yang ada di konfigurasi build. Citra emulator pada API 28 tidak mereproduksi apa pun, karena batasan ini hanya menggigit mulai 29 ke atas. Periksa SDK target di berkas build Anda pada saat yang sama: menaikkannya itulah yang menarik aturan ini masuk.

adb shell getprop ro.build.version.sdk

Mengapa menyalin biner ke tempat lain tidak menolong

Akal-akalan yang jelas, menulis pembantu ke direktori lain yang bisa ditulis, gagal karena alasan yang sama dengan yang asli. Android masih mengizinkan eksekusi pustaka bersama yang dipasang ke direktori pustaka native aplikasi, karena pemasang yang mengemasnya, bukan ditulis saat runtime. Direktori itulah satu-satunya tempat yang seharusnya dituju peluncur. Apa pun yang Anda ekstrak sendiri saat runtime, apa pun namanya, tetap berada di kategori yang ditolak.

Penyimpanan eksternal lebih buruk: dipasang dengan noexec, dan pembantu yang diletakkan di sana tidak bisa dijalankan aplikasi mana pun. Menghapus data aplikasi juga tidak memulihkan perilaku lama, karena peluncuran berikutnya hanya membuat ulang salinan yang sama dan tidak dapat dieksekusi.

Arahkan peluncur ke pustaka yang dikemas

Ganti nama pembantu menjadi nama pustaka bersama dan kirimkan di bawah jniLibs untuk setiap ABI yang Anda dukung, lalu buat peluncur mengembalikan jalur pustaka terpasang mulai API 28:

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

Pustaka harus diekstrak saat pemasangan agar jalur itu berisi berkas sungguhan, jadi setel android:extractNativeLibs="true" pada elemen <application>. Jika penggabung manifes membawa nilai lain, tambahkan tools:replace="android:extractNativeLibs" di sebelahnya. referensi elemen application mendokumentasikan apa yang dikendalikan atribut itu.

Awas variabel lokal ganda saat menambal peluncur dengan tangan. Sumber lama mendeklarasikan nativeAPI di kemudian hari dalam metode yang sama, dan deklarasi kedua adalah galat kompilasi bukan galat runtime, jadi build gagal cepat dan kesalahannya mudah terlihat. Jaga daftar ABI di blok Gradle selaras dengan folder yang Anda tambahkan.

Buat artefak rilis cocok dengan build uji

APK debug bisa menaikkan terowongan sementara bundle rilis masih crash, karena bundle mengemas pustaka native dalam bentuk berbeda dan bisa menyajikannya terkompresi dari arsip. Jika hanya build turunan bundle yang gagal, tambahkan ini ke gradle.properties lalu bangun ulang:

android.bundle.enableUncompressedNativeLibs=false

Lalu pastikan paket terpasang melaporkan ABI yang Anda harapkan, dan ulangi pemeriksaan pada perangkat 32-bit dan 64-bit jika Anda mengirim keduanya. Manajer paket menyebut ABI yang dipilihnya, jadi perangkat yang mundur ke 32 bit muncul di sini bukan di laporan crash:

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

Pustaka yang hilang untuk satu ABI mereproduksi galat izin asli hanya pada perangkat itu, karena itu satu ponsel uji yang berhasil bukan bukti. Kirim flag manifes, nama pustaka, dan pengaturan bundle bersama-sama; mengubah satu dan melupakan yang lain membuat Anda kembali ke crash yang sama. Perbaikan yang bertahan adalah beralih ke rilis inti yang dirawat ketimbang membawa tambalan itu selamanya.

Artikel terkait