Android 9 Pie ja Android 10 annavad hoiatusi või blokeerivad otse juurdepääsu peidetud API-dele. Siit saate teada, kuidas arendajad saavad peidetud API piirangutest mööda hiilida.
Tagasivaade enam kui aasta tagusesse aega ja me kõik ootame põnevusega, mis Android P beetaversioonides tulemas on. Kasutajad ootavad uusi funktsioone ja arendajad ootavad uusi tööriistu oma rakenduste paremaks muutmiseks. Kahjuks mõnede nende arendajate jaoks oli esimene Android P beetaversiooniga natuke vastik üllatus: peidetud API piirangud. Enne kui ma sukeldun sellesse, mida see täpselt tähendab, lubage mul selgitada veidi selle konteksti.
Mis see kõik on?
Androidi rakenduste arendajad ei pea rakenduse loomisel nullist alustama. Google pakub tööriistu, mis muudavad rakenduste arendamise lihtsamaks ja vähem korduvaks. Üks neist tööriistadest on Androidi SDK. SDK on sisuliselt viide kõigile funktsioonidele, mida arendajad saavad oma rakendustes turvaliselt kasutada. Need funktsioonid on standardvarustuses kõigis Google'i heakskiidetud Androidi variantides. SDK pole siiski ammendav. Androidi raamistikus on üsna palju "peidetud" osi, mis ei kuulu SDK-sse.
Need "peidetud" osad võivad olla väga kasulikud häkkivamate või madala tasemega asjade jaoks. Näiteks minu Rakendus vidinasahtel kasutab mitte-SDK-funktsiooni, et tuvastada, millal kasutaja käivitab vidinast rakenduse, et sahtel saaks automaatselt sulguda. Võib-olla mõtlete: "Miks mitte muuta need mitte-SDK-funktsioonid SDK osaks?" Probleem on selles, et nende toimimine pole täielikult prognoositav. Google ei saa tagada, et raamistiku kõik osad töötaksid igas heakskiidetud seadmes, seega valitakse kontrollimiseks olulisemad meetodid. Google ei garanteeri, et ülejäänud raamistik jääb järjepidevaks. Tootjad saavad neid peidetud funktsioone muuta või täielikult eemaldada. Isegi AOSP erinevates versioonides ei tea kunagi, kas peidetud funktsioon on endiselt olemas või töötab nii, nagu varem.
Kui te ei tea, miks ma olen kasutanud sõna "peidetud", siis sellepärast, et need funktsioonid ei kuulu isegi SDK-sse. Tavaliselt, kui proovite rakenduses kasutada peidetud meetodit või klassi, ei õnnestu seda kompileerida. Nende kasutamine nõuab peegeldus või muudetud SDK.
Android P puhul otsustas Google, et nende peitmisest ei piisa. Kui esimene beetaversioon välja anti, teatati, et enamik (mitte kõik) peidetud funktsioone polnud enam rakenduste arendajatele kasutamiseks saadaval. Esimene beetaversioon hoiatab teid, kui teie rakendus kasutas musta nimekirja kantud funktsiooni. Järgmised beetaversioonid ajasid teie rakenduse lihtsalt kokku. Vähemalt minu jaoks oli see must nimekiri üsna tüütu. See mitte ainult ei murdunud päris palju Navigeerimisžestid, kuid kuna peidetud funktsioonid on vaikimisi mustas nimekirjas (Google peab mõne väljalaske kohta käsitsi valgesse nimekirja lisama), oli mul vidinate sahtli tööle panemisega palju probleeme.
Nüüd oli musta nimekirja ümberlülitamiseks mitu võimalust. Esimene oli lihtsalt jätta oma rakenduse sihtimine API 27-le (Android 8.1), kuna must nimekiri kehtis ainult rakendustele, mis sihivad uusimat API-d. Kahjuks Google'iga API minimaalsed nõuded Play poes avaldamiseks oleks nii kaua võimalik sihtida ainult API 27. 1. novembri 2019 seisuga, peavad kõik Play poe rakenduste värskendused sihtima API 28 või uuemat versiooni.
Teine lahendus on tegelikult midagi, mida Google on Androidi sisse ehitatud. Musta nimekirja täielikuks keelamiseks on võimalik käivitada ADB käsk. See on suurepärane isiklikuks kasutamiseks ja testimiseks, kuid võin teile otsekohe öelda, et lõppkasutajate püüdmine ADB-d seadistada ja käivitada on õudusunenägu.
Kuhu see meid siis jätab? Kui tahame Play poodi üles laadida, ei saa me enam API 27 sihtida ja ADB-meetod pole lihtsalt elujõuline. See aga ei tähenda, et meil on valikuvõimalused otsas.
Peidetud API must nimekiri kehtib ainult mitte-lubatud kasutajarakendustele. Süsteemirakendused, platvormi allkirjaga allkirjastatud rakendused ja konfiguratsioonifailis määratud rakendused on kõik mustast nimekirjast vabastatud. Naljakas on see, et Google Play teenuste komplekt on kõik selles konfiguratsioonifailis määratud. Ma arvan, et Google on parem kui meist ülejäänud.
Igatahes räägime mustast nimekirjast edasi. Meid täna huvitab see, et süsteemirakendused on maksust vabastatud. See tähendab näiteks, et süsteemi kasutajaliidese rakendus saab kasutada kõiki peidetud funktsioone, mida ta soovib, kuna see asub süsteemisektsioonis. Ilmselgelt ei saa me rakendust lihtsalt süsteemisektsioonile suruda. Selleks on vaja root, head failihaldurit ja teadmisi õigustest... Lihtsam oleks kasutada ADB-d. See pole aga ainus viis, kuidas me saame olla süsteemirakendus, vähemalt mis puudutab peidetud API musta nimekirja.
Pöörake oma meelt tagasi seitsme lõigu tagusesse aega, kui mainisin refleksiooni. Kui sa ei tea, mis on peegeldus, siis soovitan lugeda sellel lehel, kuid siin on kiire kokkuvõte. Javas on peegeldus viis, kuidas pääseda ligi tavaliselt ligipääsmatutele klassidele, meetoditele ja väljadele. See on uskumatult võimas tööriist. Nagu ma selles lõigus ütlesin, oli peegeldus varem viis juurdepääsuks mitte-SDK funktsioonidele. API must nimekiri blokeerib peegelduse kasutamise, kuid ei blokeeri selle kasutamist kahekordne- peegeldus.
Siin läheb see natuke imelikuks. Tavaliselt, kui soovite kutsuda peidetud meetodit, teeksite midagi sellist (see on Kotlini keeles, kuid Java on sarnane):
val someHiddenClass = Class.forName("android.some.hidden.Class")
val someHiddenMethod = someHiddenClass.getMethod("someHiddenMethod", String::class.java)
someHiddenMethod.invoke(null, "some important string")
Tänu API mustale nimekirjale saate siiski ClassNotFoundExceptioni. Kui aga peegeldate kaks korda, töötab see hästi:
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")
Imelik eks? No jah, aga ka ei. API must nimekiri jälgib, kes funktsiooni kutsub. Kui allikas ei ole vabastatud, jookseb see kokku. Esimeses näites on allikaks rakendus. Teises näites on aga allikaks süsteem ise. Selle asemel, et kasutada peegeldust, et saada seda, mida me otse tahame, kasutame me seda selleks, et öelda süsteemile, et see peaks saama seda, mida me tahame. Kuna peidetud funktsiooni kõne allikas on süsteem, siis must nimekiri meid enam ei mõjuta.
Nii et oleme valmis. Meil on nüüd võimalus API mustast nimekirjast mööda minna. See on veidi kohmakas, kuid me võiksime kirjutada ümbrisfunktsiooni, et me ei peaks iga kord topeltpeegeldama. See pole suurepärane, kuid see on parem kui mitte midagi. Kuid tegelikult pole me veel lõpetanud. Selleks on parem viis, mis võimaldab meil kasutada tavalist peegeldust või muudetud SDK-d, nagu vanadel headel aegadel.
Kuna musta nimekirja jõustamist hinnatakse protsesside kaupa (mis on enamikul juhtudel sama, mis rakenduse kohta), võib süsteemil olla mõni viis registreerida, kas kõnealune rakendus on vabastatud või mitte. Õnneks on see olemas ja see on meile kättesaadav. Kasutades seda äsja leitud topeltpeegeldusega häkki, on meil ühekordselt kasutatav koodiplokk:
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"))
Olgu, tehniliselt ei ütle see süsteemile, et meie rakendus on API mustast nimekirjast vabastatud. Tegelikult on veel üks ADB-käsk, mida saate käivitada, et määrata funktsioone, mida ei tohiks musta nimekirja kanda. Seda me kasutame ülaltoodud eeliseid. Kood alistab põhimõtteliselt kõik, mida süsteem arvab, et see on meie rakenduse jaoks vabastatud. L-i läbimine lõpus tähendab, et kõik meetodid on vabastatud. Kui soovite teatud meetoditest vabastada, kasutage Smali süntaksit: Landroid/some/hidden/Class; Landroid/some/other/hidden/Class;
.
Nüüd oleme tegelikult valmis. Looge kohandatud rakendusklass, sisestage see kood onCreate()
meetod, ja bam, pole enam piiranguid.
Täname XDA liiget weishu, VirtualXposedi ja Taichi arendajat, selle meetodi algse avastamise eest. Samuti soovime tänada XDA tunnustatud arendajat topjohnwu, kes juhtis mulle selle lahenduse. Siin on natuke rohkem selle toimimise kohta, kuigi see on hiina keeles. mina ka kirjutas sellest Stack Overflow's, näitega ka JNI-s.