Az alkalmazásengedélyek emelése a Shizuku könyvtár használatával

A rootolás nem mindenkinek való. Így szerezhet magasabb shell-szintű engedélyeket az alkalmazásban a Shizuku könyvtár használatával.

Számos oka lehet annak, hogy az alkalmazásodnak általában adott engedélyek nem elegendőek. Lehet, hogy olyan vagy, mint én, és élvezed az Android API-val visszaélő, hacky alkalmazásokat. Egyes általam használt API-k speciális engedélyek mögött vannak zárva. Néha csak a shell felhasználó (ADB) vagy a rendszer érheti el őket. De van megoldás – Shizuku.

A Shizuku lehetővé teszi a rendszer API-k szinte közvetlenül, teljes egészében Java vagy Kotlin nyelvű hívását. Ez az útmutató megmutatja, hogyan kell megvalósítani és használni a Shizuku-t.

Mi az a Shizuku?

Mielőtt hozzálátnánk a Shizuku használatához, hasznos lehet tudni, hogy pontosan mi is az. Ha ismeri Magisk, akkor Shizuku is hasonló. De a root hozzáférés kezelése helyett a shell hozzáférést kezeli.

A Shizuku saját folyamatát futtatja shell szintű engedélyekkel. Az, hogy a felhasználó hogyan aktiválja ezt a folyamatot, az eszközétől, az Android verziójától és a választásától függ. A Shizuku aktiválható ADB-n keresztül, az eszközön lévő vezeték nélküli ADB-n keresztül (

Android 11 és újabb rendszeren), vagy root hozzáféréssel. A Shizukut megvalósító alkalmazások ezután engedélyt kérhetnek a folyamat használatához emelt szintű műveletek végrehajtására.

ShizukuFejlesztő: Xingchen és Rikka

Ár: Ingyenes.

4.1.

Letöltés

Miért Shizuku?

Míg a shell szintű hozzáférés a rendszerhez nem enged annyit, mint gyökér, még mindig több hozzáférést biztosít, mint egy normál alkalmazás. Ráadásul a Shizuku működése lehetővé teszi az Android API-k szinte szokásos használatát. Nem kell shell-parancsokra hagyatkoznod (bár ha akarod, megteheted).

Ha az alkalmazásodnak speciális engedélyekre van szüksége, amelyeket csak ADB-n keresztül (vagy root-on keresztül) lehet megadni, a Shizuku és az Android 11 nagyszerű párosítást alkot. Használhatja a Shizuku-t speciális engedélyek megadásához az eszközön.

A Shizuku még olyan eszközökön is hasznos lehet, amelyeken nincs Android 11. Az alkalmazás utasításokat és szkripteket biztosít a felhasználók számára, így Önnek nem kell tennie.

Integráció

A Shizuku hozzáadása az alkalmazáshoz nem a legegyszerűbb, de nem is nehéz. Sajnos a fejlesztői dokumentáció nem teljesen teljes, de ez a cikk foglalkozik veled. Így integrálhatja a Shizukut az alkalmazásba.

Függőségek

Az első lépés a Shizuku-függőségek hozzáadása. A modulszintű build.gradle fájlban adja hozzá a következőket a függőségek blokkhoz.

def shizuku_version = '11.0.3'

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

Szükség esetén frissítse a verziót. A 11.0.3 a legújabb a cikk írásakor.

Szolgáltató

A Shizuku működéséhez hozzá kell adnia egy szolgáltatói blokkot az alkalmazás jegyzékéhez. Nyissa meg az AndroidManifest.xml fájlt, és adja hozzá a következőket az alkalmazásblokkba.

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

Engedély

Az engedélyezéshez a Shizuku futásidejű engedélyt használ. Rövidesen belevágunk az engedély megadásával. Egyelőre adja hozzá az AndroidManifest.xml fájlhoz a jegyzékblokkon belül.

Most, hogy mindezt hozzáadtuk, az alapvető integráció megtörtént. Hagyja, hogy a Gradle szinkronizálja a projektet, és lépjen tovább a Használatba.


Használat

Elérhetőség ellenőrzése

Mielőtt rátérnénk a Shizuku használatára, beszéljünk arról, hogy megbizonyosodjunk arról, hogy valóban használható-e.

Mielőtt ellenőrizné, hogy az engedély megadva van-e, és mielőtt API-hívásokat kezdeményezne a Shizuku-n keresztül, a következő módszerrel meggyőződhet arról, hogy ezek az ellenőrzések és hívások sikeresek lesznek:

Shizuku.pingBinder()

Ha a Shizuku telepítve van és fut, akkor ez visszatér igaz. Ellenkező esetben hamis értéket ad vissza.

Engedély megadása

Mivel a Shizuku futásidejű engedélyt használ, ezt meg kell adni az alkalmazásnak, mielőtt bármit is tehetne a shell-hozzáféréssel. Két API-verzió is van forgalomban, különböző engedélyezési módokkal. Ez a rész bemutatja, hogyan kezelje mindkettőt.

Ellenőrzés

Mielőtt kérné az engedélyt, a legjobb, ha megnézi, hogy már rendelkezik-e vele. Ha igen, folytathatja azt, amit tennie kell. Ellenkező esetben a folytatás előtt kérnie kell.

A következők segítségével ellenőrizheti, hogy rendelkezik-e engedéllyel a Shizuku használatára. Ez a kód azt feltételezi, hogy egy tevékenységen belül futtatja.

Kotlin:

val isGranted = if (Shizuku.isPreV11() || Shizuku.getVersion() < 11) {
checkSelfPermission(ShizukuProvider.PERMISSION) == PackageManager.PERMISSION_GRANTED
} else {
Shizuku.checkSelfPermission() = PackageManager.PERMISSION_GRANTED
}

Jáva:

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

Kérés

Ha engedélyt kell kérnie a Shizuku használatához, a következőképpen teheti meg.

SHIZUKU_CODE Az alábbiakban használt változónak állandó értékű egész számnak kell lennie (statikus változó).

Kotlin:

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

Jáva:

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

Az eredmény figyeléséhez felül kell írnia a tevékenységet onRequestPermissionsResult() módszer. Azt is végre kell hajtani Shizuku. OnRequestPermissionResultListener. A bemutatott példa azt feltételezi, hogy a tevékenység megvalósítja ezt a felületet, de ha akarja, megvalósíthatja egy változóban.

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.

}
}

Jáva:

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-k használata

Most, hogy a Shizuku be van állítva és az engedélyek megadva, elkezdheti ténylegesen hívni az API-kat a Shizuku használatával. A folyamat itt egy kicsit más, mint amit megszokhattál. Hívás helyett getSystemService() és öntés valami hasonlóra WindowManager, helyette a belső API-kat kell használnia ezekhez (pl. IWindowManager).

A Shizuku tartalmaz egy bypass-t az Android rejtett API feketelistájához, így nem kell emiatt aggódnia használat közben. Ha azonban kíváncsi arra, hogyan kerülheti el ezt saját maga, tekintse meg előző oktatóanyagomat.

Íme egy gyors példa (reflexió segítségével), amely bemutatja, hogyan szerezhet be egy példányt IPackageManager és ezzel futásidejű engedélyt ad egy alkalmazásnak.

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)

Jáva:

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

A folyamat más API-k esetében is hasonló. Szerezze meg az osztályra és annak csonk-alosztályára vonatkozó hivatkozásokat. Szerezze meg a hivatkozást a asInterface metódus a Stub osztályhoz. Szerezze be magából az osztályból a kívánt metódusokra vonatkozó hivatkozásokat. Ezután hívja meg a asInterface metódushivatkozása van, lecseréli "package" fent, bármilyen szolgáltatással, amire szüksége van. Ez a példány ezután átadható metódushívásokhoz.

Profi tipp: teljesen elkerülheti a tükrözés használatát, ha módosított SDK-t telepít. Nézze meg az anggrayudi GitHub tárházát a módosított SDK-khoz és a telepítési utasításokhoz. Ezzel a telepítéssel a fenti kód (Kotlinban) sokkal egyszerűbbé válik.

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

Bármely AIDL-alapú API használható ezzel a módszerrel bárhonnan az alkalmazásból, amíg a Shizuku fut, és az alkalmazás rendelkezik engedéllyel.

Felhasználói szolgáltatás

Míg a Binder módszer a legtöbb használati esetet lefedi, előfordulhat, hogy olyan API-ra van szüksége, amely nem rendelkezik közvetlen Binder felülettel. Ebben az esetben használhatja a felhasználói szolgáltatás funkciót a Shizukuban.

Ez a módszer az Android normál szolgáltatásához hasonlóan működik. Ön "indítja", kommunikál úgy, hogy egy ServiceConnection-hoz kötődik, és futtatja a logikáját a szolgáltatási osztályban. A különbség az, hogy Ön nem használja az Android szolgáltatását, és a szolgáltatáson belül bármi ADB-engedéllyel fut.

Most van néhány korlátozás. A felhasználói szolgáltatás teljesen különálló folyamatban és felhasználóban fut, így nem tud kommunikálni az alkalmazás többi részével, kivéve a saját AIDL visszahívásain és kötőanyagain keresztül. Mivel szintén nem megfelelő alkalmazásfolyamatban fut, előfordulhat, hogy bizonyos dolgok, például az Android-szolgáltatások összerendelése, nem működnek megfelelően.

Ehhez a Shizuku 10-es vagy újabb verziója is szükséges. Bár az alkalmazás legtöbb forrása jelenleg a 11-es verzióval rendelkezik, továbbra is tartalmaznia kell egy verzióellenőrzést, amely szerepelni fog a példában.

Az AIDL meghatározása

A kezdéshez létre kell hoznia egy új AIDL fájlt. Ezt megteheti az Android Studióban úgy, hogy jobb gombbal kattintson bármire az Android fájlnézetben, mutasson az „Új” lehetőségre, és válassza az „AIDL” lehetőséget. Adja meg a fájl nevét (pl. „IUserService”), és az Android Studio létrehoz egy sablonfájlt az Ön számára.

Ha nem ismeri az AIDL működését, feltétlenül nézze meg A Google dokumentációja.

Távolítsa el a sablon metódusokat az AIDL-ből, majd adja hozzá a destroy() módszert a Shizuku megfelelő azonosítójával. Ezt akkor hívják meg, amikor Shizuku megöli a felhasználói szolgáltatást, és minden hivatkozás vagy folyamatban lévő logika megtisztítására kell használni.

Példa 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.
}

Az AIDL végrehajtása

A következő teendő az AIDL tényleges megvalósítása.

Jáva:

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

A fenti példák feltételezik, hogy rendelkezik a Android Hidden API SDK telepítve.

A szolgáltatási kapcsolat beállítása

Most, hogy a felhasználói szolgáltatás meghatározása és megvalósítása megtörtént, ideje beállítani a használatra. Az első dolog, amit meg kell tennie, meg kell határoznia egy ServiceConnection-t, ahol kommunikálni szeretne vele (például az alkalmazás fő tevékenységéből).

Jáva:

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

A binder változó az, amit a felhasználói szolgáltatással való kommunikációhoz fog használni az alkalmazásból. Annak ellenőrzéséhez, hogy használható-e, csak ellenőrizze, hogy nem nulla-e, és azt pingBinder() visszatér igaz, akárcsak a fenti kódpéldában.

Felhasználói szolgáltatás argumentumok létrehozása

Mielőtt vezérelhetné a felhasználói szolgáltatást, meg kell határoznia néhány argumentumot, amelyeket a Shizuku használ az indításakor és leállításakor. Ezek közé tartozik például a szolgáltatás osztálynevének tényleges megadása a Shizukunak, a folyamat utótagjának megadása, hogy hibakereshető-e vagy sem, és milyen verzióról van szó.

Jáva:

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)

A felhasználói szolgáltatás indítása, leállítása és összerendelése

Az indítási és kötési műveletek, valamint a leállítási és feloldási műveletek egységesek a Shizukuban. Nincsenek külön indítási és kötési metódusok, illetve leállítási és lekötési módszerek.

Íme, hogyan kell elindítani és bekötni a Felhasználói szolgáltatás.

Jáva:

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

Íme, hogyan kell állj meg és oldd fel a Felhasználói szolgáltatás.

Jáva:

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

Kotlin:

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

Felhasználói szolgáltatás meghívása

A felhasználói szolgáltatás elindítása után elkezdheti használni. Egyszerűen ellenőrizze, hogy a binder változó értéke nem nulla és pingelhető, majd hívja meg a metódusát.

Jáva:

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

Következtetés

Ha mindezt végigkövette, akkor most már működő Shizuku-integrációval kell rendelkeznie. Ne felejtse el elmondani a felhasználóknak, hogy telepítsék a Shizuku-t, és megfelelően ellenőrizze, hogy a Shizuku elérhető-e, mielőtt megpróbálná használni.