Rootiranje ni za vsakogar. Tukaj je opisano, kako lahko z uporabo knjižnice Shizuku pridobite povišana dovoljenja na ravni lupine v svoji aplikaciji.
Obstaja veliko razlogov, zakaj dovoljenja, ki so običajno dodeljena vaši aplikaciji, morda ne bodo zadostovala. Morda ste kot jaz in uživate v ustvarjanju hekerskih aplikacij, ki zlorabljajo Android API. Nekateri API-ji, ki jih uporabljam, so zaklenjeni za posebnimi dovoljenji. Včasih lahko do njih dostopa le uporabnik lupine (ADB) ali sistem. Vendar pa obstaja rešitev — Shizuku.
Shizuku vam omogoča skoraj neposredno klicanje sistemskih API-jev in v celoti v Javi ali Kotlinu. Ta vodnik vam bo pokazal, kako izvajati in uporabljati Shizuku.
Kaj je Shizuku?
Preden začnemo uporabljati Shizuku, bi bilo morda koristno vedeti, kaj točno je. Če poznate Magisk, potem je Shizuku podoben. Toda namesto upravljanja korenskega dostopa upravlja dostop lupine.
Shizuku vodi svoj proces z dovoljenji na ravni lupine. Kako uporabnik aktivira ta postopek, je odvisno od njegove naprave, različice Androida in izbire. Shizuku je mogoče aktivirati prek ADB, prek brezžičnega ADB v napravi (
v sistemu Android 11 in novejšem), ali prek korenskega dostopa. Aplikacije, ki izvajajo Shizuku, lahko nato zahtevajo dovoljenje za uporabo tega postopka za izvajanje povišanih operacij.Cena: brezplačno.
4.1.
Zakaj Shizuku?
Medtem ko vam dostop do sistema na ravni lupine ne omogoča toliko, kot korenina, vam še vedno omogoča več dostopa kot običajna aplikacija. Poleg tega vam način delovanja Shizuku omogoča uporabo Android API-jev skoraj kot običajno. Ni se vam treba zanašati na ukaze lupine (čeprav se lahko, če želite).
Če vaša aplikacija potrebuje posebna dovoljenja, ki jih je mogoče dodeliti samo prek ADB (ali s root), sta Shizuku in Android 11 odličen par. Shizuku lahko preprosto uporabite za podelitev posebnih dovoljenj v celoti v napravi.
Tudi pri napravah, ki nimajo sistema Android 11, je Shizuku lahko koristen. Aplikacija ponuja navodila in skripte za uporabnike, tako da vam ni treba.
Integracija
Dodajanje Shizukuja v vašo aplikacijo ni najenostavnejše, ni pa tudi težko. Na žalost, dokumentacijo razvijalca ni povsem popoln, vendar ste v tem članku obravnavali. Tukaj je opisano, kako integrirati Shizuku v svojo aplikacijo.
Odvisnosti
Prvi korak je dodajanje odvisnosti Shizuku. V svojem build.gradle na ravni modula bloku odvisnosti dodajte naslednje.
def shizuku_version = '11.0.3'
implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"
Po potrebi posodobite različico. 11.0.3 je najnovejši v času pisanja.
Ponudnik
Da bi Shizuku deloval, morate v manifest svoje aplikacije dodati blok ponudnika. Odprite AndroidManifest.xml in znotraj bloka aplikacije dodajte naslednje.
android: name="rikka.shizuku.ShizukuProvider"
android: authorities="${applicationId}.shizuku"
android: multiprocess="false"
android: enabled="true"
android: exported="true"
android: permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
Dovoljenje
Za avtorizacijo Shizuku uporablja dovoljenje za čas izvajanja. Kmalu se bomo lotili izdajanja tega dovoljenja. Za zdaj ga dodajte v AndroidManifest.xml znotraj bloka manifesta.
Zdaj, ko je vse to dodano, je osnovna integracija narejena. Naj Gradle izvede sinhronizacijo projekta in nadaljujte z uporabo.
Uporaba
Preverjanje razpoložljivosti
Preden se lotimo uporabe Shizukuja, se pogovorimo o tem, da zagotovimo, da je dejansko na voljo za uporabo.
Preden preverite, ali je dovoljenje odobreno, in preden opravite klice API prek Shizukuja, se lahko prepričate, da bodo ta preverjanja in klici uspešni z naslednjo metodo:
Shizuku.pingBinder()
Če je Shizuku nameščen in se izvaja, se bo to vrnilo prav. V nasprotnem primeru bo vrnil false.
Podelitev dovoljenja
Ker Shizuku uporablja dovoljenje za čas izvajanja, ga je treba odobriti vaši aplikaciji, preden lahko naredite kar koli z dostopom do lupine. V obtoku sta tudi dve različici API-ja z različnimi načini podeljevanja. Ta razdelek vam bo pokazal, kako ravnati z obema.
Preverjanje
Preden zahtevate dovoljenje, je najbolje, da preverite, ali ga že imate. Če to storite, lahko nadaljujete z vsem, kar morate storiti. V nasprotnem primeru ga boste morali zahtevati, preden nadaljujete.
Če želite preveriti, ali imate dovoljenje za uporabo Shizuku, lahko uporabite naslednje. Ta koda predvideva, da jo izvajate znotraj dejavnosti.
Kotlin:
val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED
}
Java:
boolean isGranted;
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;
} else {
isGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;
}
Zahtevanje
Če morate zaprositi za dovoljenje za uporabo Shizukuja, to storite tako.
The SHIZUKU_CODE spodaj uporabljena spremenljivka mora biti celo število s stalno vrednostjo (statična spremenljivka).
Kotlin:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(arrayOf(ShizukuProvider.PERMISSION), SHIZUKU_CODE)
} else {
Shizuku.requestPermission(SHIZUKU_CODE)
}
Java:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(newString[] { ShizukuProvider.PERMISSION }, SHIZUKU_CODE);
} else {
Shizuku.requestPermissions(SHIZUKU_CODE);
}
Če želite poslušati rezultat, boste morali preglasiti dejavnost onRequestPermissionsResult() metoda. Prav tako boste morali izvajati Shizuku. OnRequestPermissionResultListener. Primer, ki ga bom pokazal, predpostavlja, da vaša dejavnost implementira ta vmesnik, vendar ga lahko implementirate v spremenljivki, če želite.
Kotlin:
classExampleActivity : AppCompatActivity, Shizuku.OnRequestPermissionResultListener {
...override void onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
permissions.forEachIndexed { index, permission ->
if (permission == ShizukuProvider.PERMISSION) {
onRequestPermissionResult(requestCode, grantResults[index])
}
}
}override voidonRequestPermissionResult(requestCode: Int, grantResult: Int){
val isGranted = grantResult == PackageManager.PERMISSION_GRANTED
//Dostuff based on the result.
}
}
Java:
publicclassExampleActivityextendsAppCompatActivityimplementsShizuku.OnRequestPermissionResultListener{
...@Override
publicvoidonRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){
for (int i = 0; i < permissions.length; i++) {
String permission = permissions[i];
int result = grantResults[i];if (permission.equals(ShizukuProvider.PERMISSION) {
onRequestPermissionResult(requestCode, result);
}
}
}
@Override
publicvoidonRequestPermissionResult(int requestCode, int grantResult){
boolean isGranted = grantResult == PackageManager.PERMISSION_GRANTED;
//Dostuff based on the result.
}
}
Uporaba API-jev
Zdaj, ko je Shizuku nastavljen in so dovoljenja odobrena, lahko začnete dejansko klicati API-je z uporabo Shizukuja. Tukaj je postopek nekoliko drugačen, kot ste ga morda vajeni. Namesto klica getSystemService()
in ulivanje na nekaj podobnega WindowManager
, boste namesto tega morali uporabiti notranje API-je (npr. IWindowManager
).
Shizuku vključuje obhod za Androidov skriti črni seznam API-jev, tako da vam pri uporabi tega ne bo treba skrbeti. Če vas zanima, kako to sami zaobiti, si oglejte mojo prejšnjo vadnico.
Tukaj je hiter primer (z uporabo refleksije), ki vam pokaže, kako lahko dobite primerek IPackageManager
in ga uporabite za podelitev dovoljenja za čas izvajanja aplikaciji.
Kotlin:
val iPmClass = Class.forName("android.content.pm.IPackageManager")
val iPmStub = Class.forName("android.content.pm.IPackageManager\$Stub")
val asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder::class.java)
val grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String:: class.java /* package name */, String:: class.java /* permission name */, Int:: class.java /* user ID */)val iPmInstance = asInterfaceMethod.invoke(null, ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")))
grantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Java:
Class< ?>> iPmClass = Class.forName("android.content.pm.IPackageManager");
Class< ?>> iPmStub = Class.forName("android.content.pm.IPackageManager$Stub");
Method asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder.class);
Method grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String.class, String.class, int.class);val iPmInstance = asInterfaceMethod.invoke(null, new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
grantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);
Postopek za druge API-je je podoben. Pridobite sklice na razred in njegov podrazred Stub. Pridobite sklic na asInterface
metoda za razred Stub. Pridobite sklice na želene metode iz samega razreda. Nato pokličite asInterface
referenca metode, ki jo imate, zamenjava "package"
zgoraj s katero koli storitvijo, ki jo potrebujete. Ta primerek je nato mogoče posredovati za klice metod.
Pro-nasvet: uporabi refleksije se lahko v celoti izognete, če namestite spremenjen SDK. Oglejte si anggrayudijevo skladišče GitHub za spremenjene SDK-je in navodila za namestitev. Ko je ta nameščen, zgornja koda (v Kotlinu) postane veliko preprostejša.
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Vse API-je, ki temeljijo na AIDL, je mogoče uporabiti s to metodo od koder koli v vaši aplikaciji, če se Shizuku izvaja in ima vaša aplikacija dovoljenje.
Uporabniška storitev
Čeprav metoda Binder pokriva večino primerov uporabe, boste včasih potrebovali API, ki nima neposrednega vmesnika Binder. V tem primeru lahko uporabite funkcijo uporabniške storitve v Shizuku.
Ta metoda deluje podobno kot običajna storitev v sistemu Android. Vi ga "zaženete", komunicirate tako, da se nanj povežete s ServiceConnection in zaženete svojo logiko v storitvenem razredu. Razlika je v tem, da ne uporabljate storitve Android in vse v storitvi teče z dovoljenji ADB.
Zdaj obstajajo nekatere omejitve. Uporabniška storitev teče v popolnoma ločenem procesu in uporabniku, tako da ne morete komunicirati s preostalim delom aplikacije, razen prek lastnih povratnih klicev AIDL in povezovalnikov. Ker se tudi ne izvaja v ustreznem procesu aplikacije, nekatere stvari, kot je povezovanje storitev Android, morda ne bodo delovale pravilno.
To zahteva tudi različico Shizuku 10 ali novejšo. Medtem ko ima večina virov za aplikacijo trenutno različico 11, morate vseeno vključiti preverjanje različice, ki bo vključeno v primeru.
Definiranje AIDL
Za začetek boste morali ustvariti novo datoteko AIDL. To lahko storite v Android Studiu tako, da z desno miškino tipko kliknete kar koli v pogledu datoteke Android, premaknete miškin kazalec nad možnost »Novo« in izberete možnost »AIDL«. Vnesite ime datoteke (npr. "IUserService") in Android Studio bo za vas ustvaril datoteko predloge.
Če niste seznanjeni s tem, kako deluje AIDL, preverite Googlova dokumentacija.
Odstranite metode predloge iz AIDL in nato dodajte a destroy()
metodo z ustreznim ID-jem za Shizuku. To bo poklicano, ko Shizuku uniči uporabniško storitev, in ga je treba uporabiti za čiščenje vseh referenc ali tekoče logike, ki jo imate.
Primer AIDL:
packagetk.zwander.shizukudemo;
interfaceIUserService{
voiddestroy()= 16777114;
void grantPermission(String packageName, String permission, int userId) = 1; //You can specify your own method IDs in the AIDL. Check out the documentation for more details on this.
}
Izvajanje AIDL
Naslednja stvar je dejansko implementacija AIDL.
Java:
publicclassUserServiceextendsIUserService.Stub{
//Make sure to include an empty constructor.
publicUserService(){
}@Override
publicvoiddestroy(){
//Shizuku wants the service to be killed. Clean up and then exit.
System.exit(0);
}
@Override
publicvoidgrantPermission(String packageName, String permission, int userId){
//No need to use ShizukuBinderWrapper.
IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId);
}
}
Kotlin:
classUserService : IUserService.Stub {
//Include an empty constructor.
constructor() {
}override fun destroy(){
//Shizuku wants the service to be killed. Clean up and exit.
System.exit(0)
}
override fun grantPermission(packageName: String, permission: String, userId: Int) {
//No need for ShizukuBinderWrapper.
IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId)
}
}
Zgornji primeri predpostavljajo, da imate Skrit API za Android SDK nameščen.
Nastavitev storitvene povezave
Zdaj, ko je uporabniška storitev definirana in implementirana, je čas, da jo nastavite za uporabo. Prva stvar, ki jo morate narediti, je, da definirate ServiceConnection, kjer želite z njo komunicirati (npr. iz glavne dejavnosti v vaši aplikaciji).
Java:
private IUserService binder = null;
privatefinal ServiceConnection connection = new ServiceConnection() {
@Override
publicvoidonServiceConnected(ComponentName componentName, IBinder binder){
if (binder != null && binder.pingBinder()) {
binder = IUserService.Stub.asInterface(binder);
}
}
@Override
publicvoidonServiceDisconnected(ComponentName componentName){
binder = null;
}
}
Kotlin:
privatevar binder: IUserService? = null
private val connection = object: ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, binder: IBinder?){
if (binder != null && binder.pingBinder()) {
binder = IUserService.Stub.asInterface(binder)
}
}
override fun onServiceDisconnected(componentName: ComponentName){
binder = null;
}
}
The binder
spremenljivka je tisto, kar boste uporabljali za komunikacijo z uporabniško storitvijo iz vaše aplikacije. Če želite preveriti, ali je na voljo za uporabo, samo preverite, da ni nič in to pingBinder()
vrača prav, tako kot v zgornjem primeru kode.
Ustvarjanje argumentov uporabniške storitve
Preden lahko nadzirate uporabniško storitev, boste morali določiti nekaj argumentov, ki jih bo Shizuku uporabljal pri zagonu in zaustavitvi. Ti vključujejo stvari, kot je dejansko povedati Shizuku ime razreda storitve, določiti pripono procesa, ali je mogoče odpravljati napake ali ne, in katera različica je.
Java:
privatefinal Shizuku.UserServiceArgs serviceArgs = new Shizuku.UserServiceArgs(
newComponentName(BuildConfig.APPLICATION_ID, UserService.class.getName()))
.processNameSuffix("user_service")
.debuggable(BuildConfig.DEBUG)
.version(BuildConfig.VERSION_CODE);
Kotlin:
private val serviceArgs = Shizuku.UserServiceArgs(
ComponentName(BuildConfig.APPLICATION_ID, UserService.class::java.getName()))
.processNameSuffix("user_service")
.debuggable(BuildCOnfig.DEBUG)
.version(BuildConfig.VERSION_CODE)
Zagon, zaustavitev in vezava uporabniške storitve
Dejanja začetka in vezave ter dejanja ustavitve in razveze so v Shizuku poenotena. Ni ločenih metod za zagon in vezavo ali metod za zaustavitev in razvezo.
Tukaj je opisano, kako začeti in vezati uporabniško storitev.
Java:
if (Shizuku.getVersion >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection);
} else {
//Tell the user to upgrade Shizuku.
}
Kotlin:
if (Shizuku.getVersion() >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection)
} else {
//Tell the user to upgrade Shizuku.
}
Tukaj je opisano, kako ustavite in odvežite uporabniško storitev.
Java:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
Kotlin:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
Priklic uporabniške storitve
Ko je uporabniška storitev zagnana, jo lahko začnete uporabljati. Preprosto preverite, ali binder
spremenljivka ni ničelna in jo je mogoče pingati, nato pa pokličite svojo metodo.
Java:
if (binder != null && binder.pingBinder()) {
binder.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);
}
Kotlin:
if (binder?.pingBinder() == true) {
binder?.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
}
Zaključek
Če ste sledili vsem tem, bi zdaj morali imeti delujočo integracijo Shizuku. Samo ne pozabite povedati svojim uporabnikom, naj namestijo Shizuku, in pravilno preveriti, ali je Shizuku na voljo, preden ga poskusite uporabiti.