Як підвищити дозволи програми за допомогою бібліотеки Shizuku

Рутування не для всіх. Ось як ви можете отримати підвищені дозволи на рівні оболонки у своїй програмі за допомогою бібліотеки Shizuku.

Є багато причин, чому дозволів, які зазвичай надаються вашій програмі, може бути недостатньо. Можливо, ви схожі на мене і вам подобається створювати хакерські програми, які зловживають Android API. Деякі API, якими я користуюся, заблоковані спеціальними дозволами. Іноді доступ до них може отримати лише користувач оболонки (ADB) або система. Але є вихід — Шизуку.

Shizuku дозволяє викликати системні API майже безпосередньо, і повністю в Java або Kotlin. Цей посібник покаже вам, як запровадити та використовувати Shizuku.

Що таке Шизуку?

Перш ніж ми перейдемо до використання Shizuku, можливо, буде корисно знати, що саме це таке. Якщо ви знайомі Magisk, тоді Шизуку схожий. Але замість того, щоб керувати кореневим доступом, він керує доступом до оболонки.

Shizuku запускає власний процес із дозволами на рівні оболонки. Як користувач активує цей процес, залежить від його пристрою, версії Android і вибору. Shizuku можна активувати через ADB, через бездротовий ADB на пристрої (

на Android 11 і новіших версіях), або через root-доступ. Програми, що реалізують Shizuku, можуть запитувати дозвіл на використання цього процесу для виконання операцій з підвищеними правами.

ШизукуРозробник: Сінчен і Рікка

Ціна: безкоштовно.

4.1.

Завантажити

Чому Shizuku?

Хоча доступ до системи на рівні оболонки не дозволяє зробити стільки, скільки корінь, він все одно надає вам більше доступу, ніж звичайна програма. Крім того, спосіб роботи Shizuku дозволяє використовувати Android API майже як зазвичай. Вам не потрібно покладатися на команди оболонки (хоча ви можете, якщо хочете).

Якщо вашій програмі потрібні спеціальні дозволи, які можна надати лише через ADB (або за допомогою root), Shizuku та Android 11 стануть чудовою парою. Ви можете просто використовувати Shizuku, щоб надати спеціальні дозволи повністю на пристрої.

Навіть на пристроях, які не працюють на Android 11, Shizuku може бути корисним. Програма надає інструкції та сценарії для користувачів, тож вам не потрібно.

Інтеграція

Додати Shizuku до програми не найпростіше, але й не складно. На жаль, документація розробника не зовсім повний, але ця стаття вас охопила. Ось як інтегрувати Shizuku у ваш додаток.

Залежності

Першим кроком є ​​додавання залежностей Shizuku. У своєму build.gradle на рівні модуля додайте наступне до блоку залежностей.

def shizuku_version = '11.0.3'

implementation "dev.rikka.shizuku: api:$shizuku_version"
implementation "dev.rikka.shizuku: provider:$shizuku_version"

За потреби обов’язково оновіть версію. 11.0.3 є останньою на момент написання.

Провайдер

Щоб Shizuku працював, вам потрібно додати блок провайдера до маніфесту програми. Відкрийте AndroidManifest.xml і додайте наступне в блок програми.

 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" />

Дозвіл

Для авторизації Shizuku використовує дозвіл виконання. Ми трохи почнемо надавати цей дозвіл. Наразі додайте його до AndroidManifest.xml у блоці маніфесту.

Тепер, коли все це додано, основна інтеграція виконана. Дозвольте Gradle виконати синхронізацію проекту та перейдіть до використання.


Використання

Перевірка наявності

Перш ніж перейти до того, як використовувати Shizuku, давайте поговоримо про те, щоб переконатися, що він дійсно доступний для використання.

Перш ніж перевіряти, чи надано дозвіл, і перед тим, як здійснювати виклики API через Shizuku, ви можете переконатися, що ці перевірки та виклики будуть успішними за допомогою такого методу:

Shizuku.pingBinder()

Якщо Shizuku встановлено та запущено, це повернеться правда. В іншому випадку він поверне false.

Надання дозволу

Оскільки Shizuku використовує дозвіл на виконання, його потрібно надати вашій програмі, перш ніж ви зможете робити будь-які дії з доступом до оболонки. Також в обігу є дві версії API з різними способами надання. Цей розділ покаже вам, як працювати з обома.

Перевірка

Перш ніж запитувати дозвіл, найкраще перевірити, чи ви його вже маєте. Якщо ви це зробите, ви можете продовжити все, що вам потрібно. В іншому випадку вам потрібно буде надіслати запит, перш ніж продовжити.

Щоб перевірити, чи є у вас дозвіл на використання Shizuku, ви можете скористатися наступним. Цей код припускає, що ви запускаєте його всередині Activity.

Котлін:

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;
}

Запит

Якщо вам потрібно отримати дозвіл на використання Shizuku, ось як це зробити.

The КОД_ШИЗУКУ змінна, яка використовується нижче, має бути цілим числом із постійним значенням (статична змінна).

Котлін:

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);
}

Щоб прослухати результат, вам потрібно перевизначити Activity onRequestPermissionsResult() метод. Вам також потрібно буде реалізувати Шизуку. OnRequestPermissionResultListener. Приклад, який я збираюся показати, припускає, що ваш Activity реалізує цей інтерфейс, але ви можете реалізувати його у змінній, якщо хочете.

Котлін:

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.
}
}

Використання API

Тепер, коли Shizuku налаштовано та дозволи надано, ви можете почати фактично викликати API за допомогою Shizuku. Процес тут дещо відрізняється від того, до якого ви звикли. Замість дзвонити getSystemService() і кастинг до чогось подібного WindowManager, замість цього вам доведеться використовувати внутрішні API (наприклад, IWindowManager).

Shizuku включає обхід прихованого чорного списку API Android, тому вам не потрібно про це турбуватися під час його використання. Якщо вам цікаво, як обійти це самостійно, перегляньте мій попередній посібник.

Ось короткий приклад (з використанням відображення), який показує вам, як ви можете отримати екземпляр IPackageManager і використовуйте його, щоб надати додатку дозвіл на виконання.

Котлін:

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);

Процес для інших API аналогічний. Отримайте посилання на клас і його підклас Stub. Отримайте посилання на asInterface метод для класу Stub. Отримайте посилання на потрібні вам методи з самого класу. Потім викличте asInterface посилання на метод, яке у вас є, заміна "package" вище з будь-якими послугами, які вам потрібні. Потім цей екземпляр можна передати для викликів методів.

Професійна порада: Ви можете взагалі уникнути використання відображення, якщо встановите модифікований SDK. Перегляньте репозиторій GitHub anggrayudi для змінених SDK та інструкцій зі встановлення. З його встановленням наведений вище код (у Kotlin) стає набагато простішим.

val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)

Будь-які API на основі AIDL можна використовувати з цим методом з будь-якого місця у вашій програмі, якщо Shizuku запущено та ваша програма має дозвіл.

Обслуговування користувачів

Хоча метод Binder охоплює більшість випадків використання, іноді вам знадобиться API, який не має прямого інтерфейсу Binder. У такому випадку ви можете скористатися функцією обслуговування користувачів у Shizuku.

Цей метод працює подібно до звичайного Сервісу в Android. Ви «запускаєте» його, спілкуєтеся, прив’язуючись до нього за допомогою ServiceConnection, і запускаєте свою логіку в сервісному класі. Різниця в тому, що ви не використовуєте службу Android, і будь-що всередині служби працює з дозволами ADB.

Зараз є деякі обмеження. Служба користувача працює в абсолютно окремому процесі та окремому користувачі, тому ви не можете взаємодіяти з рештою програми, окрім як через власні зворотні виклики AIDL і зв’язувачі. Оскільки він також не працює в належному процесі програми, деякі речі, як-от зв’язування служб Android, можуть не працювати належним чином.

Для цього також потрібна версія Shizuku 10 або новіша. Хоча наразі більшість джерел для програми мають версію 11, ви все одно повинні включити перевірку версії, яка буде включена в приклад.

Визначення AIDL

Щоб почати, вам потрібно буде створити новий файл AIDL. Ви можете зробити це в Android Studio, клацнувши правою кнопкою миші будь-що у вікні перегляду файлів Android, навівши курсор на опцію «Новий» і вибравши опцію «AIDL». Введіть назву файлу (наприклад, «IUserService»), і Android Studio створить для вас файл шаблону.

Якщо ви не знайомі з тим, як працюють AIDL, обов’язково перевірте Документація Google.

Видаліть шаблонні методи з AIDL, а потім додайте a destroy() метод із відповідним ідентифікатором для Shizuku. Це буде викликано, коли Shizuku вбиває службу користувача, і його слід використовувати для очищення будь-яких посилань або поточної логіки, яка у вас є.

Приклад 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.
}

Впровадження AIDL

Наступне, що потрібно зробити, це реалізувати 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);
}
}

Котлін:

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)
}
}

Наведені вище приклади припускають, що ви маєте Android Hidden API SDK встановлено.

Налаштування підключення служби

Тепер, коли службу користувача визначено та реалізовано, настав час налаштувати її для використання. Перше, що вам слід зробити, це визначити ServiceConnection, де ви хочете з ним спілкуватися (наприклад, з основної активності у вашій програмі).

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;
}
}

Котлін:

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 змінна — це те, що ви використовуватимете для зв’язку зі службою користувача з вашої програми. Щоб перевірити, чи він доступний для використання, просто переконайтеся, що він не нульовий і що pingBinder() повертається правда, як у прикладі коду вище.

Створення аргументів User Service

Перш ніж ви зможете керувати службою користувача, вам потрібно буде визначити деякі аргументи, які Shizuku використовуватиме під час запуску та зупинки. До них належать такі речі, як фактичне повідомлення Шизуку імені класу служби, визначення суфікса процесу, чи можна його налагоджувати, і яка це версія.

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);

Котлін:

private val serviceArgs = Shizuku.UserServiceArgs(
ComponentName(BuildConfig.APPLICATION_ID, UserService.class::java.getName()))
.processNameSuffix("user_service")
.debuggable(BuildCOnfig.DEBUG)
.version(BuildConfig.VERSION_CODE)

Запуск, зупинка та прив’язка служби користувача

Дії запуску та зв’язування, а також дії зупинки та розв’язування об’єднані в Shizuku. Немає окремих методів запуску та прив’язки або методів зупинки та розв’язування.

Ось як це зробити почати і зв'язати Служба користувача.

Java:

if (Shizuku.getVersion >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection);
} else {
//Tell the user to upgrade Shizuku.
}

Котлін:

if (Shizuku.getVersion() >= 10) {
//This only works on Shizuku 10 or later.
Shizuku.bindUserService(serviceArgs, connection)
} else {
//Tell the user to upgrade Shizuku.
}

Ось як це зробити зупинитися і розв'язати Служба користувача.

Java:

if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}

Котлін:

if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}

Виклик служби користувача

Після запуску служби користувача ви можете почати її використовувати. Просто перевірте, чи binder змінна не є нульовою та її можна перевірити, а потім виконайте виклик методу.

Java:

if (binder != null && binder.pingBinder()) {
binder.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);
}

Котлін:

if (binder?.pingBinder() == true) {
binder?.grantPermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
}

Висновок

Якщо ви дотримувалися всього цього, тепер у вас має бути робоча інтеграція Shizuku. Тільки не забудьте сказати своїм користувачам встановити Shizuku та переконатися, що Shizuku доступний, перш ніж спробувати ним скористатися.