Rooten ist nicht jedermanns Sache. So können Sie mithilfe der Shizuku-Bibliothek erhöhte Berechtigungen auf Shell-Ebene in Ihrer App erhalten.
Es gibt viele Gründe, warum die Ihrer App normalerweise gewährten Berechtigungen möglicherweise nicht ausreichen. Vielleicht geht es Ihnen wie mir und Sie haben Spaß daran, hackige Apps zu erstellen, die die Android-API missbrauchen. Einige der von mir verwendeten APIs sind durch spezielle Berechtigungen geschützt. Manchmal kann nur der Shell-Benutzer (ADB) oder das System darauf zugreifen. Es gibt jedoch eine Lösung – Shizuku.
Mit Shizuku können Sie System-APIs fast direkt und vollständig in Java oder Kotlin aufrufen. Dieser Leitfaden zeigt Ihnen, wie Sie Shizuku implementieren und verwenden.
Was ist Shizuku?
Bevor wir uns an die Verwendung von Shizuku machen, könnte es hilfreich sein zu wissen, was es genau ist. Wenn Sie damit vertraut sind Magisk, dann ist Shizuku ähnlich. Aber statt den Root-Zugriff zu verwalten, verwaltet es den Shell-Zugriff.
Shizuku führt seinen eigenen Prozess mit Berechtigungen auf Shell-Ebene aus. Wie der Benutzer diesen Prozess aktiviert, hängt von seinem Gerät, seiner Android-Version und seiner Wahl ab. Shizuku kann über ADB oder über drahtloses ADB auf dem Gerät aktiviert werden (auf Android 11 und höher) oder über Root-Zugriff. Apps, die Shizuku implementieren, können dann die Erlaubnis anfordern, diesen Prozess zum Ausführen erhöhter Vorgänge zu verwenden.
Kostenlos.
4.1.
Warum Shizuku?
Während der Zugriff auf das System auf Shell-Ebene Ihnen nicht so viele Möglichkeiten bietet Wurzel, es bietet Ihnen immer noch mehr Zugriff als eine normale App. Darüber hinaus können Sie die Android-APIs dank der Funktionsweise von Shizuku fast wie gewohnt nutzen. Sie müssen sich nicht auf Shell-Befehle verlassen (können es aber, wenn Sie möchten).
Wenn Ihre App spezielle Berechtigungen benötigt, die nur über ADB (oder mit Root) gewährt werden können, sind Shizuku und Android 11 eine hervorragende Kombination. Sie können Shizuku einfach verwenden, um spezielle Berechtigungen vollständig auf dem Gerät zu erteilen.
Auch bei Geräten, die nicht über Android 11 verfügen, kann Shizuku nützlich sein. Die App stellt den Benutzern Anweisungen und Skripte zur Verfügung, sodass Sie dies nicht tun müssen.
Integration
Das Hinzufügen von Shizuku zu Ihrer App ist nicht die einfachste, aber auch nicht schwierig. Leider ist das Entwicklerdokumentation ist nicht ganz vollständig, aber dieser Artikel deckt Sie ab. So integrieren Sie Shizuku in Ihre App.
Abhängigkeiten
Der erste Schritt besteht darin, die Shizuku-Abhängigkeiten hinzuzufügen. Fügen Sie in Ihrem build.gradle auf Modulebene Folgendes zum Abhängigkeitsblock hinzu.
def shizuku_version = '11.0.3'
implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"
Stellen Sie sicher, dass Sie die Version bei Bedarf aktualisieren. 11.0.3 ist zum Zeitpunkt des Schreibens die neueste Version.
Anbieter
Damit Shizuku funktioniert, müssen Sie dem Manifest Ihrer App einen Anbieterblock hinzufügen. Öffnen Sie AndroidManifest.xml und fügen Sie Folgendes in den Anwendungsblock ein.
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" />
Erlaubnis
Zur Autorisierung nutzt Shizuku eine Laufzeitberechtigung. Wir werden gleich mit der Erteilung dieser Erlaubnis beginnen. Fügen Sie es zunächst zu AndroidManifest.xml im Manifestblock hinzu.
Nachdem nun alles hinzugefügt ist, ist die grundlegende Integration abgeschlossen. Lassen Sie Gradle eine Projektsynchronisierung durchführen und fahren Sie mit der Verwendung fort.
Verwendung
Verfügbarkeit überprüfen
Bevor wir uns mit der Verwendung von Shizuku befassen, wollen wir darüber sprechen, sicherzustellen, dass es tatsächlich verfügbar ist.
Bevor Sie prüfen, ob die Berechtigung erteilt wurde, und bevor Sie API-Aufrufe über Shizuku durchführen, können Sie mit der folgenden Methode sicherstellen, dass diese Prüfungen und Aufrufe erfolgreich sind:
Shizuku.pingBinder()
Wenn Shizuku installiert ist und ausgeführt wird, wird dies erneut angezeigt WAHR. Andernfalls wird false zurückgegeben.
Erlaubnis erteilen
Da Shizuku eine Laufzeitberechtigung verwendet, muss diese Ihrer App gewährt werden, bevor Sie mit dem Shell-Zugriff etwas tun können. Es sind auch zwei API-Versionen im Umlauf, mit unterschiedlichen Möglichkeiten der Gewährung. In diesem Abschnitt erfahren Sie, wie Sie mit beidem umgehen.
Überprüfung
Bevor Sie die Erlaubnis beantragen, prüfen Sie am besten, ob Sie diese bereits haben. Wenn Sie dies tun, können Sie mit allem fortfahren, was Sie tun müssen. Andernfalls müssen Sie es anfordern, bevor Sie fortfahren können.
Um zu überprüfen, ob Sie die Berechtigung zur Nutzung von Shizuku haben, können Sie Folgendes verwenden. Bei diesem Code wird davon ausgegangen, dass Sie ihn innerhalb einer Aktivität ausführen.
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;
}
Anfordern
Wenn Sie eine Erlaubnis zur Nutzung von Shizuku anfordern müssen, gehen Sie wie folgt vor.
Der SHIZUKU_CODE Die unten verwendete Variable sollte eine Ganzzahl mit einem konstanten Wert sein (statische Variable).
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);
}
Um auf das Ergebnis zu warten, müssen Sie die Aktivitäten überschreiben onRequestPermissionsResult() Methode. Sie müssen auch implementieren Shizuku. OnRequestPermissionResultListener. Das Beispiel, das ich zeigen werde, geht davon aus, dass Ihre Aktivität diese Schnittstelle implementiert, Sie können sie jedoch bei Bedarf in einer Variablen implementieren.
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.
}
}
Verwendung von APIs
Nachdem Shizuku nun eingerichtet und Berechtigungen erteilt wurden, können Sie mit dem tatsächlichen Aufruf von APIs mit Shizuku beginnen. Der Prozess ist hier etwas anders, als Sie es vielleicht gewohnt sind. Anstatt anzurufen getSystemService()
und Casting zu so etwas wie WindowManager
, müssen Sie stattdessen die internen APIs für diese verwenden (z. B. IWindowManager
).
Shizuku enthält eine Umgehung für die versteckte API-Blacklist von Android, sodass Sie sich bei der Verwendung darüber keine Sorgen machen müssen. Wenn Sie jedoch neugierig sind, wie Sie das selbst umgehen können, schauen Sie sich mein vorheriges Tutorial an.
Hier ist ein kurzes Beispiel (mit Reflektion), das Ihnen zeigt, wie Sie eine Instanz von erhalten können IPackageManager
und verwenden Sie es, um einer App eine Laufzeitberechtigung zu erteilen.
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);
Der Prozess für andere APIs ist ähnlich. Rufen Sie die Verweise auf die Klasse und ihre Stub-Unterklasse ab. Holen Sie sich den Verweis auf die asInterface
Methode für die Stub-Klasse. Holen Sie sich die Verweise auf die gewünschten Methoden aus der Klasse selbst. Rufen Sie dann die auf asInterface
Methodenreferenz, die Sie haben, ersetzen "package"
oben mit dem Service, den Sie benötigen. Diese Instanz kann dann für Methodenaufrufe übergeben werden.
Profi-Tipp: Sie können die Verwendung von Reflection ganz vermeiden, wenn Sie ein modifiziertes SDK installieren. Schauen Sie sich das GitHub-Repository von anggrayudi an für die geänderten SDKs und Installationsanweisungen. Wenn dies installiert ist, wird der obige Code (in Kotlin) viel einfacher.
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Alle AIDL-basierten APIs können mit dieser Methode von überall in Ihrer App verwendet werden, solange Shizuku ausgeführt wird und Ihre App über die Berechtigung verfügt.
Benutzerservice
Während die Binder-Methode die meisten Anwendungsfälle abdeckt, kann es vorkommen, dass Sie eine API benötigen, die nicht über eine direkte Binder-Schnittstelle verfügt. In diesem Fall können Sie die Benutzerservicefunktion in Shizuku nutzen.
Diese Methode funktioniert ähnlich wie ein normaler Dienst in Android. Sie „starten“ es, kommunizieren durch Bindung mit einer ServiceConnection und führen Ihre Logik in der Serviceklasse aus. Der Unterschied besteht darin, dass Sie den Android-Dienst nicht verwenden und alles innerhalb des Dienstes mit ADB-Berechtigungen ausgeführt wird.
Nun gibt es einige Einschränkungen. Der Benutzerdienst wird in einem völlig separaten Prozess und Benutzer ausgeführt, sodass Sie nur über Ihre eigenen AIDL-Rückrufe und Binder mit dem Rest Ihrer App interagieren können. Da es auch nicht in einem ordnungsgemäßen App-Prozess ausgeführt wird, funktionieren einige Dinge wie die Bindung von Android-Diensten möglicherweise nicht ordnungsgemäß.
Dies erfordert außerdem Shizuku Version 10 oder höher. Während die meisten Quellen für die App derzeit Version 11 haben, sollten Sie dennoch eine Versionsprüfung einbinden, die im Beispiel enthalten ist.
Definieren eines AIDL
Um zu beginnen, müssen Sie eine neue AIDL-Datei erstellen. Sie können dies in Android Studio tun, indem Sie mit der rechten Maustaste auf eine beliebige Stelle in der Android-Dateiansicht klicken, den Mauszeiger über die Option „Neu“ bewegen und die Option „AIDL“ auswählen. Geben Sie den Namen der Datei ein (z. B. „IUserService“) und Android Studio erstellt eine Vorlagendatei für Sie.
Wenn Sie mit der Funktionsweise von AIDLs nicht vertraut sind, schauen Sie sich unbedingt um Dokumentation von Google.
Entfernen Sie die Vorlagenmethoden aus der AIDL und fügen Sie dann eine hinzu destroy()
Methode mit der richtigen ID für Shizuku. Dies wird aufgerufen, wenn Shizuku den Benutzerdienst beendet, und sollte verwendet werden, um alle Referenzen oder die laufende Logik zu bereinigen, die Sie haben.
Beispiel-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.
}
Implementierung des AIDL
Als nächstes muss die AIDL tatsächlich implementiert werden.
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)
}
}
Bei den obigen Beispielen wird davon ausgegangen, dass Sie über Folgendes verfügen Versteckte Android-API SDK installiert.
Einrichten der Serviceverbindung
Nachdem der Benutzerdienst nun definiert und implementiert ist, ist es an der Zeit, ihn für die Verwendung einzurichten. Als Erstes sollten Sie eine ServiceConnection definieren, mit der Sie kommunizieren möchten (z. B. von der Hauptaktivität in Ihrer 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;
}
}
Der binder
Variable ist das, was Sie verwenden, um über Ihre App mit dem Benutzerdienst zu kommunizieren. Um zu überprüfen, ob es zur Verwendung verfügbar ist, stellen Sie einfach sicher, dass es nicht null ist pingBinder()
kehrt zurück WAHR, genau wie im Codebeispiel oben.
Erstellen der Benutzerdienstargumente
Bevor Sie den Benutzerdienst steuern können, müssen Sie einige Argumente definieren, die Shizuku beim Starten und Stoppen verwenden soll. Dazu gehören Dinge wie die tatsächliche Angabe des Klassennamens des Dienstes an Shizuku, die Angabe des Prozesssuffixes, die Angabe, ob er debuggbar ist oder nicht, und die Version, um die es sich handelt.
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)
Starten, Stoppen und Binden des Benutzerdienstes
Die Start- und Bindungsaktionen sowie die Stopp- und Entbindungsaktionen sind in Shizuku vereinheitlicht. Es gibt keine separaten Start- und Bindungsmethoden oder Stopp- und Unbind-Methoden.
Hier erfahren Sie, wie es geht starten und binden der Benutzerdienst.
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.
}
Hier erfahren Sie, wie es geht anhalten und losbinden der Benutzerdienst.
Java:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
Kotlin:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
Aufrufen des Benutzerdienstes
Sobald der Benutzerdienst gestartet ist, können Sie ihn verwenden. Überprüfen Sie einfach, ob die binder
Die Variable ist ungleich Null und pingbar, und rufen Sie dann Ihre Methode auf.
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)
}
Abschluss
Wenn Sie das alles befolgt haben, sollten Sie jetzt über eine funktionierende Shizuku-Integration verfügen. Denken Sie daran, Ihre Benutzer anzuweisen, Shizuku zu installieren und ordnungsgemäß zu überprüfen, ob Shizuku verfügbar ist, bevor Sie versuchen, es zu verwenden.