Как повысить права доступа к приложениям с помощью библиотеки Shizuku

Существует множество причин, по которым разрешений, обычно предоставляемых вашему приложению, может быть недостаточно. Может быть, вы похожи на меня и вам нравится создавать хакерские приложения, которые злоупотребляют Android API. Некоторые из API, которые я использую, заблокированы специальными разрешениями. Иногда к ним может получить доступ только пользователь оболочки (ADB) или система. Но есть решение — Шизуку.

Shizuku позволяет вам вызывать системные API почти напрямую и полностью на Java или Kotlin. Это руководство покажет вам, как внедрить и использовать Shizuku.

Что такое Сидзуку?

Прежде чем мы начнем использовать Shizuku, было бы полезно узнать, что это такое. Если вы знакомы с Magisk, то Shizuku похож. Но вместо управления корневым доступом он управляет доступом к оболочке.

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

Почему Шизуку?

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

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

Даже с устройствами, которые не на Android 11, Shizuku может быть полезен. Приложение предоставляет инструкции и сценарии для пользователей, так что вам не нужно.

Интеграция

Добавить Shizuku в ваше приложение не просто, но и не сложно. К сожалению, документация для разработчиков не совсем полный, но эта статья вам рассказала. Вот как интегрировать Shizuku в ваше приложение.

Зависимости

Первый шаг — добавить зависимости Shizuku. В файле build.gradle на уровне модуля добавьте в блок зависимостей следующее.

def shizuku_version = '11.0.3'\nimplementation "dev.rikka.shizuku:api:$shizuku_version"\nimplementation "dev.rikka.shizuku:provider:$shizuku_version"

Обязательно обновите версию, если это необходимо. 11.0.3 является последней на момент написания.

Провайдер

Чтобы Shizuku работал, вам нужно добавить блок провайдера в манифест вашего приложения. Откройте AndroidManifest.xml и добавьте следующее в блок приложения.

<provider\n    android:name="rikka.shizuku.ShizukuProvider"\n    android:authorities="${applicationId}.shizuku"\n    android:multiprocess="false"\n    android:enabled="true"\n    android:exported="true"\n    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) {\n    checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED\n} else {\n    Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED\n}

Ява:

boolean isGranted;\nif (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {\n    isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;\n} else {\n    isGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;\n}

Запрос

Если вам нужно запросить разрешение на использование Shizuku, вот как это сделать.

SHIZUKU_CODE используемая ниже переменная должна быть целым числом с постоянным значением (статическая переменная).

Котлин:

if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {\n    requestPermissions(arrayOf(ShizukuProvider.PERMISSION), SHIZUKU_CODE)\n} else {\n    Shizuku.requestPermission(SHIZUKU_CODE)\n}

Ява:

if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {\n    requestPermissions(new String[] { ShizukuProvider.PERMISSION }, SHIZUKU_CODE);\n} else {\n    Shizuku.requestPermissions(SHIZUKU_CODE);\n}

Чтобы прослушать результат, вам нужно переопределить Activity onRequestPermissionsResult() метод. Вам также потребуется реализовать Shizuku.OnRequestPermissionResultListener. В примере, который я собираюсь показать, предполагается, что ваша активность реализует этот интерфейс, но вы можете реализовать его в переменной, если хотите.

Котлин:

class ExampleActivity : AppCompatActivity, Shizuku.OnRequestPermissionResultListener {\n    ...\n    override void onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {\n        permissions.forEachIndexed { index, permission ->\n            if (permission == ShizukuProvider.PERMISSION) {\n                onRequestPermissionResult(requestCode, grantResults[index])\n            }\n       }\n    }\n    override void onRequestPermissionResult(requestCode: Int, grantResult: Int) {\n        val isGranted = grantResult == PackageManager.PERMISSION_GRANTED\n        //Do stuff based on the result.\n    }\n}

Ява:

public class ExampleActivity extends AppCompatActivity implements Shizuku.OnRequestPermissionResultListener {\n    ...\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        for (int i = 0; i < permissions.length; i++) {\n            String permission = permissions[i];\n            int result = grantResults[i];\n            if (permission.equals(ShizukuProvider.PERMISSION) {\n                onRequestPermissionResult(requestCode, result);\n            }\n        }\n    }\n    @Override\n    public void onRequestPermissionResult(int requestCode, int grantResult) {\n        boolean isGranted = grantResult == PackageManager.PERMISSION_GRANTED;\n        //Do stuff based on the result.\n    }\n}

Использование API

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

Shizuku включает в себя обход черного списка скрытых API Android, поэтому вам не нужно беспокоиться об этом при его использовании. Если вам интересно, как обойти это самостоятельно, ознакомьтесь с моим предыдущим руководством.

Вот краткий пример (с использованием отражения), показывающий, как вы можете получить экземпляр IPackageManager и используйте его, чтобы предоставить приложению разрешение во время выполнения.

Котлин:

val iPmClass = Class.forName("android.content.pm.IPackageManager")\nval iPmStub = Class.forName("android.content.pm.IPackageManager\$Stub")\nval asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder::class.java)\nval grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String::class.java /* package name */, String::class.java /* permission name */, Int::class.java /* user ID */)\nval iPmInstance = asInterfaceMethod.invoke(null, ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")))\ngrantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)

Ява:

Class<?> iPmClass = Class.forName("android.content.pm.IPackageManager");\nClass<?> iPmStub = Class.forName("android.content.pm.IPackageManager$Stub");\nMethod asInterfaceMethod = iPmStub.getMethod("asInterface", IBinder.class);\nMethod grantRuntimePermissionMethod = iPmClass.getMethod("grantRuntimePermission", String.class, String.class, int.class);\nval iPmInstance = asInterfaceMethod.invoke(null, new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));\ngrantRuntimePermissionMethod.invoke(iPmInstance, "com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0);

Процесс для других API аналогичен. Получите ссылки на класс и его подкласс Stub. Получить ссылку на asInterface метод для класса Stub. Получите ссылки на нужные методы из самого класса. Затем вызовите asInterface у вас есть ссылка на метод, заменив "package" выше с любой услугой, которая вам нужна. Затем этот экземпляр может быть передан для вызова метода.

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

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

С этим методом можно использовать любые 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, а затем добавьте destroy() метод с правильным идентификатором для Шизуку. Это будет вызываться, когда Shizuku убивает User Service, и должно использоваться для очистки любых ссылок или текущей логики, которые у вас есть.

Пример AIDL:

package tk.zwander.shizukudemo;\ninterface IUserService {\n    void destroy() = 16777114;\n    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.\n}

Внедрение AIDL

Следующее, что нужно сделать, это реализовать AIDL.

Ява:

public class UserService extends IUserService.Stub {\n    //Make sure to include an empty constructor.\n    public UserService() {\n    }\n    @Override\n    public void destroy() {\n        //Shizuku wants the service to be killed. Clean up and then exit.\n        System.exit(0);\n    }\n    @Override\n    public void grantPermission(String packageName, String permission, int userId) {\n        //No need to use ShizukuBinderWrapper.\n        IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId);\n    }\n}

Котлин:

class UserService : IUserService.Stub {\n    //Include an empty constructor.\n    constructor() {\n    }\n    override fun destroy() {\n        //Shizuku wants the service to be killed. Clean up and exit.\n        System.exit(0)\n    }\n    override fun grantPermission(packageName: String, permission: String, userId: Int) {\n        //No need for ShizukuBinderWrapper.\n        IPackageManager.Stub.asInterface(SystemServiceHelper.getService("package")).grantRuntimePermission(packageName, permission, userId)\n    }\n}

В приведенных выше примерах предполагается, что у вас есть Скрытый API Android SDK установлен.

Настройка подключения к сервису

Теперь, когда пользовательская служба определена и реализована, пришло время настроить ее для использования. Первое, что вам нужно сделать, это определить ServiceConnection, где вы хотите с ним взаимодействовать (например, из основного Activity в вашем приложении).

Ява:

private IUserService binder = null;\nprivate final ServiceConnection connection = new ServiceConnection() {\n    @Override\n    public void onServiceConnected(ComponentName componentName, IBinder binder) {\n        if (binder != null && binder.pingBinder()) {\n            binder = IUserService.Stub.asInterface(binder);\n        }\n    }\n    @Override\n    public void onServiceDisconnected(ComponentName componentName) {\n        binder = null;\n    }\n}

Котлин:

private var binder: IUserService? = null\nprivate val connection = object : ServiceConnection {\n    override fun onServiceConnected(componentName: ComponentName, binder: IBinder?) {\n        if (binder != null && binder.pingBinder()) {\n            binder = IUserService.Stub.asInterface(binder)\n        }\n    }\n    override fun onServiceDisconnected(componentName: ComponentName) {\n        binder = null;\n    }\n}

binder переменная — это то, что вы будете использовать для связи с User Service из вашего приложения. Чтобы проверить, доступен ли он для использования, просто убедитесь, что он не нулевой и что pingBinder() возвращается истинныйкак в примере кода выше.

Создание аргументов службы пользователя

Прежде чем вы сможете управлять пользовательской службой, вам необходимо определить некоторые аргументы, которые Shizuku будет использовать при запуске и остановке. К ним относятся такие вещи, как фактическое сообщение Сидзуку имени класса службы, указание суффикса процесса, возможность отладки и версия.

Ява:

private final Shizuku.UserServiceArgs serviceArgs = new Shizuku.UserServiceArgs(\n    new ComponentName(BuildConfig.APPLICATION_ID, UserService.class.getName()))\n        .processNameSuffix("user_service")\n        .debuggable(BuildConfig.DEBUG)\n        .version(BuildConfig.VERSION_CODE);

Котлин:

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

Запуск, остановка и привязка пользовательской службы

Действия запуска и привязки, а также действия остановки и развязки унифицированы в Шизуку. Не существует отдельных методов запуска и привязки или методов остановки и отмены привязки.

Вот как начать и связать Пользовательская служба.

Ява:

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

Котлин:

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

Вот как остановись и развяжись Пользовательская служба.

Ява:

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

Котлин:

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

Вызов службы пользователя

После того, как пользовательская служба запущена, вы можете начать ее использовать. Просто проверьте, соответствует ли binder переменная не равна нулю и доступна для проверки связи, а затем вызовите метод.

Ява:

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

Котлин:

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

Вывод

Если вы выполнили все это, теперь у вас должна быть работающая интеграция с Shizuku. Просто не забудьте сказать своим пользователям установить Shizuku и должным образом проверить, доступен ли Shizuku, прежде чем пытаться его использовать.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *