root化は誰にでもできるわけではありません。 ここでは、Shizuku ライブラリを使用してアプリでシェルレベルの権限を昇格する方法を説明します。
アプリに通常付与されている権限では不十分な理由は数多くあります。 もしかしたら、あなたも私と同じで、Android API を悪用するハックなアプリの作成を楽しんでいるかもしれません。 私が使用している API の一部は、特別な権限によってロックされています。 場合によっては、シェル ユーザー (ADB) またはシステムのみがアクセスできることがあります。 でも解決策はある――雫。
Shizuku を使用すると、システム API をほぼ直接、完全に Java または Kotlin で呼び出すことができます。 このガイドでは、Shizuku の実装方法と使用方法を説明します。
雫とは何ですか?
Shizuku を使用する前に、Shizuku が正確に何であるかを知っておくと役立つかもしれません。 ご存知の方は マジスク, それなら雫も似ています。 ただし、ルート アクセスを管理する代わりに、シェル アクセスを管理します。
Shizuku は、シェルレベルのアクセス許可を使用して独自のプロセスを実行します。 ユーザーがそのプロセスをアクティブにする方法は、デバイス、Android のバージョン、選択によって異なります。 Shizuku は、ADB 経由、オンデバイスのワイヤレス ADB 経由でアクティブ化できます (Android 11 以降の場合)、または root アクセスを通じて。 その後、Shizuku を実装するアプリは、そのプロセスを使用して昇格された操作を実行する許可を要求できます。
価格:無料。
4.1.
なぜ雫なのか?
システムへのシェルレベルのアクセスでは、次のようなことはできません。 根, それでも、通常のアプリよりも多くのアクセスが可能です。 それに加えて、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.pingBinder()
Shizuku がインストールされ実行されている場合、これが返されます。 真実. それ以外の場合は false を返します。
許可の付与
Shizuku は実行時権限を使用するため、シェル アクセスで何かを行う前にアプリに実行時権限を付与する必要があります。 また、提供方法が異なる 2 つの API バージョンが流通しています。 このセクションでは、両方の対処方法を説明します。
チェック中
許可をリクエストする前に、許可をすでに持っているかどうかを確認することが最善の方法です。 そうすれば、必要な作業を続けることができます。 それ以外の場合は、続行する前にリクエストする必要があります。
Shizuku の使用許可があるかどうかを確認するには、以下を使用できます。 このコードは、アクティビティ内で実行していることを前提としています。
コトリン:
val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED
}
ジャワ:
boolean isGranted;
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
isGranted = checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED;
} else {
isGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED;
}
リクエスト中
Shizuku の使用許可をリクエストする必要がある場合は、次の方法で行います。
の SHIZUKU_CODE 以下で使用する変数は、定常値を持つ整数 (静的変数) である必要があります。
コトリン:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(arrayOf(ShizukuProvider.PERMISSION), SHIZUKU_CODE)
} else {
Shizuku.requestPermission(SHIZUKU_CODE)
}
ジャワ:
if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
requestPermissions(newString[] { ShizukuProvider.PERMISSION }, SHIZUKU_CODE);
} else {
Shizuku.requestPermissions(SHIZUKU_CODE);
}
結果をリッスンするには、アクティビティの onRequestPermissionsResult() 方法。 実装する必要もあります しずく。 OnRequestPermissionResultListener. これから示す例では、アクティビティがそのインターフェイスを実装していることを前提としていますが、必要に応じて変数に実装することもできます。
コトリン:
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.
}
}
ジャワ:
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 がセットアップされ、権限が付与されたので、実際に Shizuku を使用して API を呼び出すことができます。 ここでのプロセスは、あなたが慣れているプロセスとは少し異なります。 電話をかける代わりに getSystemService()
そして次のようなものにキャストします WindowManager
、代わりにこれらの内部 API を使用する必要があります (例: IWindowManager
).
Shizuku には Android の隠し API ブラックリストのバイパスが含まれているため、使用時にそのことを心配する必要はありません。 ただし、それを自分で回避する方法に興味がある場合は、私の以前のチュートリアルをチェックしてください。
これは、インスタンスを取得する方法を示す簡単な例 (リフレクションを使用) です。 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)
ジャワ:
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 のプロセスも同様です。 クラスとそのスタブ サブクラスへの参照を取得します。 への参照を取得します。 asInterface
スタブクラスのメソッド。 クラス自体から必要なメソッドへの参照を取得します。 次に、 asInterface
あなたが持っているメソッド参照、置き換えます "package"
上記に必要なサービスがすべて含まれています。 その後、そのインスタンスをメソッド呼び出しに渡すことができます。
プロからのヒント: 変更された SDK をインストールすると、リフレクションの使用を完全に回避できます。 anggrayudi の GitHub リポジトリをチェックしてください 変更された SDK とインストール手順については、こちらをご覧ください。 これをインストールすると、上記のコード (Kotlin 内) が非常に単純になります。
val iPm = IPackageManager.Stub.asInterface(ShizukuBinderWrapper(SystemServiceHelper.getService("package"))))
iPm.grantRuntimePermission("com.zacharee1.systemuituner", android.Manifest.permission.WRITE_SECURE_SETTINGS, 0)
Shizuku が実行されており、アプリに権限がある限り、AIDL ベースの API はアプリ内のどこからでもこのメソッドで使用できます。
ユーザーサービス
Binder メソッドはほとんどのユースケースをカバーしますが、直接の Binder インターフェイスを持たない API が必要な場合もあります。 その場合は、ShizukuのUser Service機能をご利用いただけます。
このメソッドは、Android の通常のサービスと同様に機能します。 これを「開始」し、ServiceConnection でバインドして通信し、サービス クラスでロジックを実行します。 違いは、Android のサービスを使用していないことと、サービス内のすべてが ADB 権限で実行されることです。
現在、いくつかの制限があります。 ユーザー サービスは完全に別のプロセスとユーザーで実行されるため、独自の AIDL コールバックとバインダーを介する以外、アプリの残りの部分と対話することはできません。 また、適切なアプリプロセスで実行されていないため、Android サービスのバインドなどの一部の機能が正しく動作しない可能性があります。
これには、Shizuku バージョン 10 以降も必要です。 現在、アプリのほとんどのソースのバージョンは 11 ですが、バージョン チェックを含める必要があります。これは例に含まれます。
AIDL の定義
まず、新しい AIDL ファイルを作成する必要があります。 Android Studio でこれを行うには、Android ファイルビュー内の何かを右クリックし、[新規] オプションの上にマウスを置き、[AIDL] オプションを選択します。 ファイルの名前 (例: 「IUserService」) を入力すると、Android Studio によってテンプレート ファイルが作成されます。
AIDL がどのように機能するかよくわからない場合は、必ずチェックしてください。 Googleのドキュメント.
AIDL からテンプレート メソッドを削除し、 destroy()
しずくの適切な ID を使用してメソッドを実行します。 これは、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 を実際に実装することです。
ジャワ:
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 隠し API SDKがインストールされました。
サービス接続のセットアップ
ユーザー サービスが定義されて実装されたので、今度はそれを使用できるように設定します。 最初に行う必要があるのは、通信する場所 (たとえば、アプリのメイン アクティビティから) ServiceConnection を定義することです。
ジャワ:
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;
}
}
の binder
変数は、アプリからユーザー サービスと通信するために使用するものです。 使用できるかどうかを確認するには、それが null ではないことと、 pingBinder()
戻り値 真実、上記のコード例と同じように。
ユーザーサービス引数の作成
ユーザー サービスを制御する前に、Shizuku が開始および停止するときに使用する引数をいくつか定義する必要があります。 これには、Shizuku にサービスのクラス名を実際に伝えること、プロセスのサフィックス、デバッグ可能かどうか、バージョンを指定することが含まれます。
ジャワ:
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で統一されています。 個別の開始メソッドとバインド メソッド、または停止メソッドとバインド解除メソッドはありません。
その方法は次のとおりです 開始とバインド ユーザーサービス。
ジャワ:
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.
}
その方法は次のとおりです 停止してバインドを解除する ユーザーサービス。
ジャワ:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true);
}
コトリン:
if (Shizuku.getVersion >= 10) {
Shizuku.unbindUserService(serviceArgs, connection, true)
}
ユーザーサービスの呼び出し
ユーザーサービスが開始されると、使用を開始できます。 かどうかを確認するだけです。 binder
変数が null でなく、ping 可能である場合は、メソッド呼び出しを行います。
ジャワ:
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 が利用可能であることを適切に確認することを忘れないでください。