Rootovanie nie je pre každého. Tu je návod, ako môžete vo svojej aplikácii získať zvýšené povolenia na úrovni shellu pomocou knižnice Shizuku.
Existuje mnoho dôvodov, prečo povolenia bežne udelené vašej aplikácii nemusia stačiť. Možno ste ako ja a baví vás vytváranie hacknutých aplikácií, ktoré zneužívajú Android API. Niektoré z rozhraní API, ktoré používam, sú uzamknuté špeciálnymi povoleniami. Niekedy k nim má prístup iba používateľ prostredia (ADB) alebo systém. Existuje však riešenie - Shizuku.
Shizuku vám umožňuje volať systémové API takmer priamo a úplne v jazyku Java alebo Kotlin. Táto príručka vám ukáže, ako implementovať a používať Shizuku.
Čo je Shizuku?
Predtým, ako začneme používať Shizuku, môže byť užitočné vedieť, čo to presne je. Ak ste oboznámení s Magisk, potom je Shizuku podobný. Ale namiesto toho, aby spravoval root prístup, spravuje prístup k shellu.
Shizuku spúšťa svoj vlastný proces s povoleniami na úrovni shellu. Spôsob, akým používateľ tento proces aktivuje, závisí od jeho zariadenia, verzie systému Android a výberu. Shizuku je možné aktivovať cez ADB, cez bezdrôtové ADB na zariadení (
v systéme Android 11 a novšom), alebo prostredníctvom prístupu root. Aplikácie implementujúce Shizuku potom môžu požiadať o povolenie použiť tento proces na vykonávanie zvýšených operácií.Cena: zadarmo.
4.1.
Prečo Shizuku?
Zatiaľ čo prístup k systému na úrovni shellu vám neumožňuje robiť toľko ako koreň, stále vám poskytuje väčší prístup ako bežná aplikácia. Okrem toho spôsob, akým Shizuku funguje, vám umožňuje používať rozhrania Android API takmer ako normálne. Nemusíte sa spoliehať na príkazy shellu (hoci môžete, ak chcete).
Ak vaša aplikácia potrebuje špeciálne povolenia, ktoré možno udeliť iba prostredníctvom ADB (alebo s root), Shizuku a Android 11 tvoria skvelé párovanie. Shizuku môžete použiť iba na udelenie špeciálnych povolení plne na zariadení.
Dokonca aj so zariadeniami, ktoré nemajú Android 11, môže byť Shizuku užitočné. Aplikácia poskytuje pokyny a skripty pre používateľov, takže vy nemusíte.
integrácia
Pridanie Shizuku do vašej aplikácie nie je najjednoduchšie, ale nie je to ani ťažké. Bohužiaľ, vývojárska dokumentácia nie je úplne úplný, ale tento článok sa vám už venoval. Tu je návod, ako integrovať Shizuku do vašej aplikácie.
Závislosti
Prvým krokom je pridanie závislostí Shizuku. Vo svojom build.gradle na úrovni modulu pridajte do bloku závislostí nasledovné.
def shizuku_version = '11.0.3'
implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"
V prípade potreby aktualizujte verziu. 11.0.3 je najneskôr v čase písania.
Poskytovateľ
Aby Shizuku fungovalo, musíte do manifestu svojej aplikácie pridať blok poskytovateľa. Otvorte súbor AndroidManifest.xml a do bloku aplikácie pridajte nasledujúce.
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" />
Povolenie
Na autorizáciu používa Shizuku povolenie na spustenie. O chvíľu sa dostaneme k udeleniu tohto povolenia. Zatiaľ ho pridajte do súboru AndroidManifest.xml do bloku manifestu.
Teraz, keď sa k tomu všetkému pridá, je základná integrácia hotová. Nechajte Gradle vykonať synchronizáciu projektu a pokračujte na Použitie.
Použitie
Kontrola dostupnosti
Predtým, ako sa pustíme do toho, ako používať Shizuku, porozprávajme sa o tom, ako sa uistiť, že je skutočne k dispozícii na použitie.
Pred skontrolovaním, či je povolenie udelené, a pred uskutočnením volaní API cez Shizuku sa môžete uistiť, že tieto kontroly a volania budú úspešné pomocou nasledujúcej metódy:
Shizuku.pingBinder()
Ak je Shizuku nainštalovaný a spustený, vráti sa pravda. V opačnom prípade vráti hodnotu false.
Udelenie povolenia
Keďže Shizuku používa povolenie runtime, musíte ho udeliť vašej aplikácii skôr, ako budete môcť robiť čokoľvek s prístupom k shellu. V obehu sú aj dve verzie API s rôznymi spôsobmi udeľovania. Táto časť vám ukáže, ako zvládnuť oboje.
Kontrola
Pred požiadaním o povolenie je najlepšie skontrolovať, či ho už máte. Ak tak urobíte, môžete pokračovať vo všetkom, čo potrebujete urobiť. V opačnom prípade si ho budete musieť pred pokračovaním vyžiadať.
Ak chcete skontrolovať, či máte povolenie používať Shizuku, môžete použiť nasledujúce. Tento kód predpokladá, že ho spúšťate v rámci aktivity.
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;
}
Žiada sa
Ak potrebujete požiadať o povolenie používať Shizuku, tu je návod.
The SHIZUKU_CODE premenná použitá nižšie by mala byť celé číslo so stálou hodnotou (statická premenná).
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);
}
Ak si chcete vypočuť výsledok, budete musieť prepísať hodnoty aktivity onRequestPermissionsResult() metóda. Budete tiež musieť implementovať Shizuku. OnRequestPermissionResultListener. Príklad, ktorý ukážem, predpokladá, že vaša aktivita implementuje toto rozhranie, ale ak chcete, môžete ho implementovať do premennej.
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.
}
}
Používanie rozhraní API
Teraz, keď je Shizuku nastavené a povolenia sú udelené, môžete začať skutočne volať API pomocou Shizuku. Postup je tu trochu iný, ako ste zvyknutí. Namiesto volania getSystemService()
a casting na niečo podobné WindowManager
, budete musieť namiesto toho použiť interné rozhrania API (napr. IWindowManager
).
Shizuku obsahuje obídenie pre skrytú čiernu listinu API pre Android, takže sa o to pri používaní nemusíte starať. Ak ste zvedaví, ako to sami obísť, pozrite si môj predchádzajúci návod.
Tu je rýchly príklad (pomocou reflexie), ktorý vám ukáže, ako môžete získať inštanciu IPackageManager
a použite ho na udelenie povolenia na spustenie aplikácie.
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);
Postup pre ostatné API je podobný. Získajte odkazy na triedu a jej podtriedu Stub. Získajte odkaz na asInterface
metóda pre triedu Stub. Získajte odkazy na požadované metódy zo samotnej triedy. Potom vyvolajte asInterface
referenciu na metódu, ktorú máte, nahraďte "package"
vyššie s akoukoľvek službou, ktorú potrebujete. Táto inštancia sa potom môže odovzdať na vyvolanie metódy.
Pro-tip: Ak si nainštalujete upravenú súpravu SDK, môžete sa úplne vyhnúť používaniu reflexie. Pozrite sa na úložisko GitHub Anggrayudi pre upravené súpravy SDK a pokyny na inštaláciu. S týmto nainštalovaným sa vyššie uvedený kód (v Kotline) stáva oveľa jednoduchším.
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Akékoľvek API založené na AIDL je možné použiť s touto metódou odkiaľkoľvek vo vašej aplikácii, pokiaľ je spustená Shizuku a vaša aplikácia má povolenie.
Používateľská služba
Zatiaľ čo metóda Binder pokrýva väčšinu prípadov použitia, môžu nastať situácie, keď budete potrebovať API, ktoré nemá priame rozhranie Binder. V takom prípade môžete použiť funkciu User Service v Shizuku.
Táto metóda funguje podobne ako bežná služba v systéme Android. „Spustíte“ ho, komunikujete tak, že sa k nemu pripojíte pomocou ServiceConnection a spustíte svoju logiku v triede služieb. Rozdiel je v tom, že nepoužívate službu Android a všetko v rámci služby beží s povoleniami ADB.
Teraz existujú určité obmedzenia. Používateľská služba beží v úplne samostatnom procese a používateľovi, takže so zvyškom svojej aplikácie nemôžete interagovať inak ako prostredníctvom vlastných spätných volaní AIDL a viazačov. Keďže tiež nefunguje v správnom procese aplikácie, niektoré veci, ako napríklad viazanie služieb Android, nemusia fungovať správne.
Vyžaduje si to aj Shizuku verziu 10 alebo novšiu. Zatiaľ čo väčšina zdrojov pre aplikáciu má momentálne verziu 11, mali by ste stále zahrnúť kontrolu verzie, ktorá bude zahrnutá v príklade.
Definovanie AIDL
Ak chcete začať, budete musieť vytvoriť nový súbor AIDL. Môžete to urobiť v Android Studio kliknutím pravým tlačidlom myši na čokoľvek v zobrazení súborov Android, umiestnením kurzora myši na možnosť „Nový“ a výberom možnosti „AIDL“. Zadajte názov súboru (napr. „IUserService“) a Android Studio vám vytvorí súbor šablóny.
Ak neviete, ako fungujú AIDL, určite si to pozrite Dokumentácia Google.
Odstráňte metódy šablóny z AIDL a potom pridajte a destroy()
metóda so správnym ID pre Shizuku. Toto sa zavolá, keď Shizuku zabíja službu používateľa, a malo by sa použiť na vyčistenie všetkých odkazov alebo prebiehajúcej logiky, ktoré máte.
Príklad 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.
}
Implementácia AIDL
Ďalšia vec, ktorú treba urobiť, je skutočne implementovať 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)
}
}
Vyššie uvedené príklady predpokladajú, že máte Skryté API pre Android SDK nainštalované.
Nastavenie pripojenia k službe
Teraz, keď je používateľská služba definovaná a implementovaná, je čas nastaviť ju na používanie. Prvá vec, ktorú by ste mali urobiť, je definovať ServiceConnection, kde s ním chcete komunikovať (napr. z hlavnej aktivity vo vašej aplikácii).
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
premenná je to, čo budete používať na komunikáciu so službou používateľa z vašej aplikácie. Ak chcete skontrolovať, či je k dispozícii na použitie, skontrolujte, či nie je nulový a to pingBinder()
sa vracia pravda, rovnako ako v príklade kódu vyššie.
Vytvorenie argumentov používateľskej služby
Predtým, ako budete môcť ovládať Službu používateľa, budete musieť definovať niektoré argumenty, ktoré má Shizuku použiť pri jej spustení a zastavení. Patria sem veci, ako je vlastne povedať Shizuku názov triedy služby, špecifikovať príponu procesu, či je alebo nie je laditeľná a aká je verzia.
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)
Spustenie, zastavenie a viazanie používateľskej služby
Akcie spustenia a viazania a akcie zastavenia a rozviazania sú v Shizuku zjednotené. Neexistujú samostatné metódy spustenia a viazania ani metódy zastavenia a zrušenia väzby.
Tu je návod, ako na to spustiť a zaviazať užívateľskú službu.
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.
}
Tu je návod, ako na to zastaviť a rozviazať užívateľskú službu.
Java:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
Kotlin:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
Vyvolanie používateľskej služby
Po spustení používateľskej služby ju môžete začať používať. Jednoducho skontrolujte, či je binder
premenná nemá hodnotu null a dá sa pingnúť, a potom zavolajte svoju metódu.
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)
}
Záver
Ak ste to všetko dodržali, teraz by ste mali mať fungujúcu integráciu Shizuku. Len nezabudnite povedať svojim používateľom, aby si nainštalovali Shizuku, a aby pred pokusom o použitie správne skontrolovali, či je Shizuku k dispozícii.