Rooting er ikke for alle. Her er, hvordan du kan få forhøjede tilladelser på shell-niveau i din app ved hjælp af Shizuku-biblioteket.
Der er mange grunde til, at de tilladelser, der normalt gives til din app, måske ikke er nok. Måske er du ligesom mig og nyder at skabe hackede apps, der misbruger Android API. Nogle af de API'er, jeg bruger, er låst bag særlige tilladelser. Nogle gange kan kun shell-brugeren (ADB) eller systemet få adgang til dem. Der er dog en løsning - Shizuku.
Shizuku giver dig mulighed for at kalde system-API'er næsten direkte og helt i Java eller Kotlin. Denne guide viser dig, hvordan du implementerer og bruger Shizuku.
Hvad er Shizuku?
Før vi går i gang med at bruge Shizuku, kan det være nyttigt at vide, hvad det præcist er. Hvis du er bekendt med Magisk, så ligner Shizuku. Men i stedet for at administrere root-adgang, administrerer den shell-adgang.
Shizuku kører sin egen proces med tilladelser på shell-niveau. Hvordan brugeren aktiverer denne proces afhænger af deres enhed, Android-version og valg. Shizuku kan aktiveres via ADB, via trådløs ADB på enheden (
på Android 11 og nyere), eller via root-adgang. Apps, der implementerer Shizuku, kan derefter anmode om tilladelse til at bruge denne proces til at udføre forhøjede operationer.Pris: Gratis.
4.1.
Hvorfor Shizuku?
Mens shell-niveau adgang til systemet ikke lader dig gøre så meget som rod, det giver dig stadig mere adgang end en normal app ville. Oven i købet lader den måde, Shizuku fungerer på, dig bruge Android API'erne næsten som normalt. Du behøver ikke at stole på shell-kommandoer (selvom du kan, hvis du vil).
Hvis din app har brug for specielle tilladelser, der kun kan gives via ADB (eller med root), er Shizuku og Android 11 en fantastisk parring. Du kan bare bruge Shizuku til at give specielle tilladelser fuldt ud på enheden.
Selv med enheder, der ikke er på Android 11, kan Shizuku være nyttig. Appen giver instruktioner og scripts til brugere, så du ikke behøver det.
Integration
At tilføje Shizuku til din app er ikke det enkleste, men det er heller ikke svært. Desværre udvikler dokumentation er ikke helt komplet, men denne artikel dækker dig. Sådan integrerer du Shizuku i din app.
Afhængigheder
Det første skridt er at tilføje Shizuku-afhængighederne. I din build.gradle på modulniveau skal du tilføje følgende til afhængighedsblokken.
def shizuku_version = '11.0.3'
implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"
Sørg for at opdatere versionen, hvis det er nødvendigt. 11.0.3 er senest i skrivende stund.
Udbyder
For at Shizuku kan fungere, skal du tilføje en udbyderblokering til din apps manifest. Åbn AndroidManifest.xml og tilføj følgende i applikationsblokken.
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" />
Tilladelse
For godkendelse bruger Shizuku en køretidstilladelse. Vi vil komme ind på at give den tilladelse om lidt. Indtil videre skal du tilføje det til AndroidManifest.xml inde i manifestblokken.
Nu hvor alt det er tilføjet, er den grundlæggende integration færdig. Lad Gradle udføre en projektsynkronisering, og fortsæt til Usage.
Brug
Kontrol af tilgængelighed
Før vi går ind i, hvordan man bruger Shizuku, lad os tale om at sikre, at den faktisk er tilgængelig til brug.
Før du kontrollerer, om tilladelsen er givet, og før du foretager API-kald gennem Shizuku, kan du sikre dig, at disse kontroller og opkald vil lykkes med følgende metode:
Shizuku.pingBinder()
Hvis Shizuku er installeret og kører, vender dette tilbage rigtigt. Ellers vil det returnere falsk.
Giver tilladelse
Da Shizuku bruger en runtime-tilladelse, skal den gives til din app, før du kan gøre noget med shell-adgang. Der er også to API-versioner i omløb, med forskellige måder at give det på. Dette afsnit viser dig, hvordan du håndterer begge dele.
Tjekker
Før du anmoder om tilladelsen, er den bedste ting at gøre at tjekke, om du allerede har den. Hvis du gør det, kan du fortsætte med, hvad du skal gøre. Ellers skal du anmode om det, før du fortsætter.
For at tjekke, om du har tilladelse til at bruge Shizuku, kan du bruge følgende. Denne kode antager, at du kører den i en aktivitet.
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;
}
Anmoder
Hvis du har brug for at anmode om tilladelse til at bruge Shizuku, se her hvordan.
Det SHIZUKU_CODE variabel, der bruges nedenfor, skal være et heltal med en stabil værdi (statisk variabel).
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);
}
For at lytte efter resultatet skal du tilsidesætte aktivitetens onRequestPermissionsResult() metode. Du skal også implementere Shizuku. OnRequestPermissionResultListener. Eksemplet, jeg skal vise, antager, at din aktivitet implementerer denne grænseflade, men du kan implementere den i en variabel, hvis du vil.
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.
}
}
Brug af API'er
Nu hvor Shizuku er konfigureret og tilladelser er givet, kan du faktisk begynde at kalde API'er ved hjælp af Shizuku. Processen her er lidt anderledes, end du måske er vant til. I stedet for at ringe getSystemService()
og casting til noget lignende WindowManager
, skal du bruge de interne API'er til disse i stedet (f.eks. IWindowManager
).
Shizuku inkluderer en bypass til Androids skjulte API-sortliste, så du behøver ikke at bekymre dig om det, når du bruger den. Hvis du er nysgerrig efter, hvordan du selv kan omgå det, så tjek min tidligere tutorial.
Her er et hurtigt eksempel (ved hjælp af refleksion), der viser dig, hvordan du kan få en forekomst af IPackageManager
og bruge det til at give en app en køretidstilladelse.
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);
Processen for andre API'er er den samme. Få referencerne til klassen og dens Stub-underklasse. Hent henvisningen til asInterface
metode til Stub-klassen. Hent referencerne til de metoder, du ønsker, fra klassen selv. Påkald derefter asInterface
metode reference du har, erstatter "package"
ovenfor med den service, du har brug for. Denne instans kan derefter videregives til metodeankaldelser.
Pro-tip: du kan helt undgå at bruge refleksion, hvis du installerer et modificeret SDK. Tjek anggrayudis GitHub-lager for de ændrede SDK'er og installationsvejledninger. Med dette installeret bliver ovenstående kode (i Kotlin) meget enklere.
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Alle AIDL-baserede API'er kan bruges med denne metode fra hvor som helst i din app, så længe Shizuku kører, og din app har tilladelse.
Brugerservice
Mens Binder-metoden dækker de fleste use-cases, kan der være tidspunkter, hvor du har brug for en API, der ikke har en direkte Binder-grænseflade. I så fald kan du bruge funktionen Brugerservice i Shizuku.
Denne metode fungerer på samme måde som en normal tjeneste i Android. Du "starter" den, kommunikerer ved at binde dig til den med en ServiceConnection og kører din logik i serviceklassen. Forskellen er, at du ikke bruger Androids tjeneste, og alt inde i tjenesten kører med ADB-tilladelser.
Nu er der nogle begrænsninger. Brugerservicen kører i en helt separat proces og bruger, så du kan ikke interagere med resten af din app undtagen gennem dine egne AIDL-tilbagekald og bindemidler. Da det heller ikke kører i en ordentlig app-proces, fungerer nogle ting som binding af Android-tjenester muligvis ikke korrekt.
Dette kræver også Shizuku version 10 eller nyere. Selvom de fleste kilder til appen har version 11 i øjeblikket, bør du stadig inkludere et versionstjek, som vil blive inkluderet i eksemplet.
Definition af en AIDL
For at komme i gang skal du oprette en ny AIDL-fil. Du kan gøre dette i Android Studio ved at højreklikke på hvad som helst i Android-filvisningen, holde musen over "Ny"-indstillingen og vælge "AIDL". Indtast navnet på filen (f.eks. "IUserService"), og Android Studio vil oprette en skabelonfil til dig.
Hvis du ikke er bekendt med, hvordan AIDL virker, så sørg for at tjekke ud Googles dokumentation.
Fjern skabelonmetoderne fra AIDL, og tilføj derefter en destroy()
metode med det korrekte ID for Shizuku. Dette vil blive kaldt, når Shizuku dræber brugertjenesten, og skal bruges til at rydde op i eventuelle referencer eller løbende logik, du har.
Eksempel 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.
}
Implementering af AIDL
Den næste ting at gøre er faktisk at implementere 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)
}
}
Ovenstående eksempler antager, at du har Android Hidden API SDK installeret.
Opsætning af serviceforbindelsen
Nu hvor brugertjenesten er defineret og implementeret, er det tid til at få den sat op til brug. Den første ting du skal gøre er at definere en ServiceConnection, hvor du vil kommunikere med den (f.eks. fra hovedaktiviteten i din app).
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;
}
}
Det binder
variabel er, hvad du vil bruge til at kommunikere med brugertjenesten fra din app. For at kontrollere, om den er tilgængelig til brug, skal du bare kontrollere, at den ikke er null og det pingBinder()
vender tilbage rigtigt, ligesom i kodeeksemplet ovenfor.
Oprettelse af User Service argumenter
Før du kan kontrollere brugertjenesten, skal du definere nogle argumenter, som Shizuku skal bruge, når den starter og stopper den. Disse inkluderer ting som rent faktisk at fortælle Shizuku klassens navn på tjenesten, specificering af processuffikset, om det kan fejlfindes eller ej, og hvilken version det er.
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)
Start, stop og binding af brugertjenesten
Start- og bindingshandlingerne og stop- og afbindingshandlingerne er forenet i Shizuku. Der er ikke separate start- og bindingsmetoder eller stop- og afbindingsmetoder.
Sådan gør du starte og binde brugertjenesten.
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.
}
Sådan gør du stop og afbind brugertjenesten.
Java:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
Kotlin:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
Påkaldelse af brugertjenesten
Når brugertjenesten er startet, kan du begynde at bruge den. Du skal blot kontrollere, om binder
variabel er ikke-null og pingbar, og lav derefter dit metodekald.
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)
}
Konklusion
Hvis du fulgte alt dette igennem, skulle du nu have en fungerende Shizuku-integration. Bare husk at fortælle dine brugere, at de skal installere Shizuku og kontrollere, at Shizuku er tilgængelig, før de prøver at bruge det.