Android 9 Pie ve Android 10, uyarılar veriyor veya gizli API'lere erişimi doğrudan engelliyor. Geliştiricilerin gizli API kısıtlamalarını nasıl aşabilecekleri aşağıda açıklanmıştır.
Bir yıldan fazla bir süre öncesine geri dönüyoruz ve hepimiz Android P beta sürümlerinde neler olacağını görmekten heyecan duyuyoruz. Kullanıcılar yeni özellikleri sabırsızlıkla beklerken, geliştiriciler de uygulamalarını daha iyi hale getirecek bazı yeni araçları sabırsızlıkla bekliyor. Ne yazık ki bu geliştiricilerin bazıları için, ilk Android P beta biraz kötü bir sürprizle geldi: gizli API kısıtlamaları. Bunun tam olarak ne anlama geldiğine dalmadan önce, bağlamı hakkında biraz açıklama yapmama izin verin.
Bütün bunlar ne hakkında?
Android uygulama geliştiricilerinin bir uygulama yaparken sıfırdan başlamasına gerek yok. Google, uygulama geliştirmeyi daha kolay ve daha az tekrarlı hale getirecek araçlar sağlar. Bu araçlardan biri de Android SDK'dır. SDK aslında geliştiricilerin uygulamalarında güvenle kullanabileceği tüm işlevlere bir referanstır. Bu işlevler, Google'ın onayladığı tüm Android sürümlerinde standart olarak gelir. Ancak SDK kapsamlı değildir. Android'in çerçevesinin SDK'nın parçası olmayan pek çok "gizli" kısmı var.
Bu "gizli" parçalar, daha hileli veya düşük seviyeli şeyler için inanılmaz derecede faydalı olabilir. Mesela benim Widget Çekmecesi uygulaması Bir kullanıcının bir widget'tan bir uygulamayı başlattığını algılamak ve böylece çekmecenin otomatik olarak kapanabilmesi için SDK olmayan bir işlevi kullanır. Şunu düşünüyor olabilirsiniz: "Neden SDK olmayan bu işlevleri SDK'nın parçası yapmıyorsunuz?" Sorun şu ki operasyonları tam olarak öngörülebilir değil. Google, çerçevenin her bir parçasının onayladığı her cihazda çalıştığından emin olamaz, bu nedenle doğrulanmak üzere daha önemli yöntemler seçilir. Google, çerçevenin geri kalanının tutarlı kalacağını garanti etmez. Üreticiler bu gizli fonksiyonları değiştirebilir veya tamamen kaldırabilir. AOSP'nin farklı sürümlerinde bile, gizli bir işlevin hâlâ var olup olmayacağını veya eskisi gibi çalışıp çalışmayacağını asla bilemezsiniz.
Neden "gizli" kelimesini kullandığımı merak ediyorsanız bunun nedeni, bu işlevlerin SDK'nın bir parçası bile olmamasıdır. Normalde, bir uygulamada gizli bir yöntem veya sınıf kullanmaya çalışırsanız derleme başarısız olur. Bunları kullanmak gerektirir refleks veya değiştirilmiş bir SDK.
Google, Android P ile bunları yalnızca gizlemenin yeterli olmadığına karar verdi. İlk beta yayınlandığında öyle açıklandı gizli işlevlerin çoğu (hepsi değil) artık uygulama geliştiricilerin kullanımına açık değildi. İlk beta, uygulamanız kara listeye alınmış bir işlevi kullandığında sizi uyarıyordu. Sonraki betalar uygulamanızın çökmesine neden oldu. En azından benim için bu kara liste oldukça can sıkıcıydı. Sadece biraz kırmakla kalmadı Gezinme Hareketleri, ancak gizli işlevler varsayılan olarak kara listeye alındığından (Google'ın sürüm başına bazılarını manuel olarak beyaz listeye alması gerekir), Widget Drawer'ı çalıştırırken çok fazla sorun yaşadım.
Artık kara listeyi aşmanın birkaç yolu vardı. Kara liste yalnızca en son API'yi hedefleyen uygulamalara uygulandığından, ilki uygulamanızın API 27'yi (Android 8.1) hedeflemesini sağlamaktı. Ne yazık ki, Google'ın minimum API gereksinimleri Play Store'da yayınlamak için API 27'yi ancak bu kadar uzun süre hedeflemek mümkün olacaktır. 1 Kasım 2019 itibarıylaPlay Store'daki tüm uygulama güncellemeleri API 28 veya üstünü hedeflemelidir.
İkinci geçici çözüm aslında Google'ın Android'e yerleştirdiği bir şeydir. Kara listeyi tamamen devre dışı bırakmak için bir ADB komutunu çalıştırmak mümkündür. Bu, kişisel kullanım ve testler için harikadır, ancak size ilk elden şunu söyleyebilirim ki, son kullanıcıların ADB'yi kurup çalıştırmasını sağlamaya çalışmak bir kabus.
Peki bu bizi nereye bırakıyor? Play Store'a yükleme yapmak istiyorsak artık API 27'yi hedefleyemeyiz ve ADB yöntemi geçerli değildir. Ancak bu, seçeneklerimizin tükendiği anlamına gelmiyor.
Gizli API kara listesi yalnızca beyaz listede olmayan kullanıcı uygulamaları için geçerlidir. Sistem uygulamaları, platform imzasıyla imzalanan uygulamalar ve yapılandırma dosyasında belirtilen uygulamaların tümü kara listeden muaftır. İşin komik yanı, Google Play Hizmetleri paketinin tamamı bu yapılandırma dosyasında belirtiliyor. Sanırım Google hepimizden daha iyi.
Neyse kara listeden bahsetmeye devam edelim. Bugün bizi ilgilendiren kısmı ise sistem başvurularının muaf olması. Bu, örneğin Sistem Kullanıcı Arayüzü uygulamasının, sistem bölümünde olduğundan istediği tüm gizli işlevleri kullanabileceği anlamına gelir. Açıkçası, bir uygulamayı sistem bölümüne aktaramayız. Bunun için root, iyi bir dosya yöneticisi ve izin bilgisi gerekiyor... ADB'yi kullanmak daha kolay olurdu. En azından gizli API kara listesi söz konusu olduğunda, bir sistem uygulaması olmamızın tek yolu bu değil.
Aklınızı yedi paragraf önce, yansımadan bahsettiğim zamana götürün. Yansımanın ne olduğunu bilmiyorsanız okumanızı tavsiye ederim bu sayfa, ama işte kısa bir özet. Java'da yansıma, normalde erişilemeyen sınıflara, yöntemlere ve alanlara erişmenin bir yoludur. İnanılmaz derecede güçlü bir araçtır. Bu paragrafta söylediğim gibi, yansıma eskiden SDK dışı işlevlere erişmenin bir yoluydu. API kara listesi yansıma kullanımını engeller ancak kullanımını engellemez. çift-refleks.
İşte burada işler biraz tuhaflaşıyor. Normalde gizli bir yöntemi çağırmak istiyorsanız şöyle bir şey yapardınız (bu Kotlin'dedir, ancak Java da benzerdir):
val someHiddenClass = Class.forName("android.some.hidden.Class")
val someHiddenMethod = someHiddenClass.getMethod("someHiddenMethod", String::class.java)
someHiddenMethod.invoke(null, "some important string")
Ancak API kara listesi sayesinde yalnızca bir ClassNotFoundException elde edersiniz. Ancak iki kez düşünürseniz, iyi sonuç verir:
val forName = Class::class.java.getMethod("forName", String:: class.java)
val getMethod = Class::class.java.getMethod("getMethod", String:: class.java, arrayOf>()::class.java)
val someHiddenClass = forName.invoke(null, "android.some.hidden.Class") asClass
val someHiddenMethod = getMethod.invoke(someHiddenClass, "someHiddenMethod", String::class.java)
someHiddenMethod.invoke(null, "some important string")
Tuhaf değil mi? Evet, ama aynı zamanda hayır. API kara listesi, bir işlevi kimin çağırdığını izler. Kaynak muaf değilse çöker. İlk örnekte kaynak uygulamadır. Ancak ikinci örnekte kaynak sistemin kendisidir. Doğrudan istediğimizi elde etmek için yansımayı kullanmak yerine, sisteme istediğimizi almasını söylemek için kullanıyoruz. Gizli fonksiyona yapılan çağrının kaynağı sistem olduğundan kara liste artık bizi etkilemiyor.
Yani işimiz bitti. Artık API kara listesini atlamanın bir yolunu bulduk. Biraz hantal ama her seferinde çift yansıtma yapmak zorunda kalmamak için bir sarmalayıcı işlevi yazabiliriz. Harika değil ama hiç yoktan iyidir. Ama aslında işimiz daha bitmedi. Bunu yapmanın, eski güzel günlerdeki gibi normal yansımayı veya değiştirilmiş bir SDK'yı kullanmamıza izin verecek daha iyi bir yolu var.
Kara listenin yaptırımı süreç bazında değerlendirildiğinden (çoğu durumda uygulama bazında aynıdır), sistemin söz konusu uygulamanın muaf olup olmadığını kaydetmesinin bir yolu olabilir. Neyse ki var ve bizim için erişilebilir. Bu yeni keşfedilen çift yansıma hilesini kullanarak, tek kullanımlık bir kod bloğumuz var:
val forName = Class::class.java.getDeclaredMethod("forName", String:: class.java)
val getDeclaredMethod = Class::class.java.getDeclaredMethod("getDeclaredMethod", String:: class.java, arrayOf>()::class.java) val vmRuntimeClass = forName.invoke(null, "dalvik.system.VMRuntime") asClass
val getRuntime = getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null) as Method
val setHiddenApiExemptions = getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", arrayOf(arrayOf<String>()::class.java)) asMethodval vmRuntime = getRuntime.invoke(null)
setHiddenApiExemptions.invoke(vmRuntime, arrayOf("L"))
Tamam, teknik olarak bu, sisteme uygulamamızın API kara listesinden muaf olduğunu söylemiyor. Aslında kara listeye alınmaması gereken işlevleri belirtmek için çalıştırabileceğiniz başka bir ADB komutu daha var. Yukarıda faydalandığımız şey budur. Kod temel olarak sistemin uygulamamız için muaf olduğunu düşündüğü her şeyi geçersiz kılar. Sonunda "L" harfinin geçmesi tüm yöntemlerin muaf olduğu anlamına gelir. Belirli yöntemleri muaf tutmak istiyorsanız Smali sözdizimini kullanın: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;
.
Artık aslında işimiz bitti. Özel bir Uygulama sınıfı oluşturun, bu kodu onCreate()
yöntem ve bam, artık kısıtlama yok.
Bu yöntemi ilk kez keşfettiği için VirtualXposed ve Taichi'nin geliştiricisi XDA Üyesi Weishu'ya teşekkür ederiz. Bu geçici çözümü bana gösterdiği için XDA Tanınan Geliştirici topjohnwu'ya da teşekkür ederiz. İşte nasıl çalıştığı hakkında biraz daha fazla bilgi, Çince olmasına rağmen. ben de Stack Overflow'ta bunun hakkında yazdı, JNI'da da bir örnekle.