Uygulama Android 9'da çalışıyordu ve Android 10 güncellemesinden sonra aynı telefonda ölüyor. Profiliniz el değmemiş, sertifikalar aynı ve çökme herhangi bir kimlik doğrulama trafiğinden önce geliyor. logcat önce bir boş gösterici gösteriyor, birkaç satır yukarısında ise Cannot run program ...: error=13, Permission denied. Bu mesaj bir dosyayı çalıştırmayla ilgili, yapılandırmanızı okumayla değil.

13 numaralı hata gerçekte neyi reddediyor

Android 10, API seviyesi 29, uygulamaların kendi veri dizinlerine yazdıkları dosyaları çalıştırmasını durdurdu. Kural önce yazılabilir sonra çalıştırılabilir depolamayı hedefliyor ve varlıklardan kopyalayan bir başlatıcının yarattığı tam olarak budur. ics-openvpn çekirdeğinin eski derlemeleri ilk çalıştırmada APK'den çalıştırılabilir bir dosyayı openvpn çıkarır ve onu ProcessBuilder ile başlatır. Çıkarma hâlâ başarılı; reddedilen şey çalıştırma. API 29'u hedefleyen bir uygulama, daha eski bir sürümle gelen bir cihazda çalışsa bile kuralı devralır, bu yüzden bir güncelleme kimsenin dokunmadığı bir derlemeyi bozabilir.

Reddetmede tek bir klasör adına özgü hiçbir şey yok. Uygulamanın veri dizini altında oluşturduğu bir dosya, yoldan bağımsız olarak çalıştırılamaz sayılır, bu yüzden getFilesDir(), getCacheDir() ve özel bir alt dizin hepsi aynı şekilde başarısız olur.

Yardımcı çökmesini profil sorunundan ayırın

Baştan kaydırmak yerine süreç başlangıcının etrafındaki günlüğü okuyun. Aradığınız imza şu satır: error=13, ve üstündeki yığın izi genellikle ProcessBuilder çağıran başlatıcı yöntemini adlandırır:

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

Yardımcı hiç başlamadıysa, hiç OpenVPN günlük çıktısı görmezsiniz: ne şifre görüşmesi, ne itilen yollar, ne DNS yapılandırması. O satırlar yine de görünürse ve hata sonra gelirse, ikili çalıştı ve sorun profilde, sunucuda veya yönlendirme katmanında. Bu tek ayrım, aksi hâlde hiç suçlu olmayan .ovpn dosyalarını düzenlemeye gidecek çok zamanı kurtarır. Ayrıca aşağıdaki iki düzeltmeden hangisine gerçekten ihtiyacınız olduğunu söyler.

Test ettiğiniz cihazdaki API seviyesini de kontrol edin, derleme yapılandırmanızdakini değil. API 28'deki bir emülatör imajı hiçbir şeyi yeniden üretmez, çünkü kısıtlama yalnızca 29 ve üstünde ısırıyor. Aynı anda derleme dosyanızdaki hedef SDK'yı kontrol edin: kuralı içeri çeken şey onu yükseltmektir.

adb shell getprop ro.build.version.sdk

İkiliyi başka bir yere kopyalamak neden yardımcı olmuyor

Bariz geçici çözüm, yardımcıyı başka bir yazılabilir dizine yazmak, orijinalin başarısız olduğu aynı nedenden başarısız olur. Android hâlâ uygulamanın yerel kitaplık dizinine kurulan paylaşılan kitaplıkları çalıştırmaya izin veriyor, çünkü onları yükleyici paketliyor, çalışma zamanında yazılmıyorlar. Tam olarak o dizin başlatıcının işaret etmesi gereken tek yer. Çalışma zamanında kendiniz çıkardığınız her şey, adı ne olursa olsun, reddedilen kategoride kalır.

Dış depolama daha kötü: noexec ile bağlanmış ve oraya konan bir yardımcı hiçbir uygulama tarafından başlatılamaz. Uygulama verilerini temizlemek de eski davranışı geri getirmez, çünkü sonraki çalıştırma yalnızca aynı çalıştırılamaz kopyayı yeniden oluşturur.

Başlatıcıyı paketlenmiş bir kitaplığa yönlendirin

Yardımcıyı paylaşılan kitaplık adına çevirin ve desteklediğiniz her ABI için jniLibs altında gönderin, sonra başlatıcı API 28'den itibaren kurulu kitaplığın yolunu döndürsün:

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

O yolun gerçek bir dosya içermesi için kitaplık kurulum sırasında çıkarılmalı, bu yüzden şu ayarı android:extractNativeLibs="true" öğesine yapın: <application>. Bir manifest birleştirici farklı bir değer getirirse yanına tools:replace="android:extractNativeLibs" ekleyin. application öğesi başvurusu bu özniteliğin neyi denetlediğini belgeler.

Başlatıcıyı elle yamalarken yinelenen yerel değişkene dikkat edin. Eski kaynaklar aynı yöntemde daha sonra nativeAPI bildirir ve ikinci bildirim çalışma zamanı değil derleme hatasıdır, bu yüzden derleme hızla başarısız olur ve hatayı görmek kolaydır. Gradle bloğundaki ABI listesini eklediğiniz klasörlerle uyumlu tutun.

Sürüm artefaktını test derlemesiyle eşleştirin

Hata ayıklama APK'sı tüneli kaldırabilirken sürüm bundle'ı hâlâ çökebilir, çünkü bundle yerel kitaplıkları farklı bir biçimde paketler ve onları arşivden sıkıştırılmış olarak sunabilir. Yalnızca bundle'dan türeyen derlemeler başarısız oluyorsa bunu gradle.properties dosyasına ekleyin ve yeniden derleyin:

android.bundle.enableUncompressedNativeLibs=false

Sonra kurulu paketin beklediğiniz ABI'yi bildirdiğini doğrulayın ve ikisini de gönderiyorsanız 32 bit ve 64 bit cihazda kontrolü yineleyin. Paket yöneticisi seçtiği ABI'yi adlandırır, bu yüzden 32 bite geri düşen bir cihaz çökme raporunda değil burada görünür:

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

Bir ABI için eksik kitaplık orijinal izin hatasını yalnızca o cihazda yeniden üretir, bu yüzden tek bir başarılı test telefonu kanıt değildir. Manifest bayrağını, kitaplık adını ve bundle ayarını birlikte gönderin; birini değiştirip diğerlerini unutmak sizi aynı çökmeye geri getirir. Kalıcı çözüm, yamayı sonsuza dek taşımak yerine bakımı yapılan bir çekirdek sürümüne geçmektir.

İlgili makaleler