כיצד להעלות את הרשאות האפליקציה באמצעות ספריית Shizuku

השתרשות לא מתאימה לכולם. כך תוכל לקבל הרשאות גבוהות ברמת מעטפת באפליקציה שלך באמצעות ספריית Shizuku.

יש הרבה סיבות לכך שההרשאות הניתנות בדרך כלל לאפליקציה שלך לא מספיקות. אולי אתה כמוני ונהנה ליצור אפליקציות פריצות שעושות שימוש לרעה ב-Android API. חלק ממשקי ה-API שבהם אני משתמש נעולים מאחורי הרשאות מיוחדות. לפעמים רק משתמש המעטפת (ADB) או המערכת יכולים לגשת אליהם. בכל זאת יש פתרון - Shizuku.

Shizuku מאפשרת לך לקרוא לממשקי API של המערכת כמעט ישירות, והכל ב-Java או Kotlin. מדריך זה יראה לך כיצד ליישם ולהשתמש ב- Shizuku.

מה זה שיזוקו?

לפני שנתחיל להשתמש ב- Shizuku, אולי זה יעזור לדעת מה זה בדיוק. אם אתה מכיר מאגיסק, אז Shizuku דומה. אבל במקום לנהל גישת שורש, הוא מנהל גישת מעטפת.

Shizuku מפעיל תהליך משלה עם הרשאות ברמת מעטפת. האופן שבו המשתמש מפעיל את התהליך תלוי במכשיר שלו, בגרסת האנדרואיד ובבחירתו. ניתן להפעיל את שיזוקו דרך ADB, דרך ADB אלחוטית במכשיר (באנדרואיד 11 ואילך), או דרך גישת שורש. אפליקציות המטשמות Shizuku יכולות לאחר מכן לבקש הרשאה להשתמש בתהליך זה כדי לבצע פעולות מוגברות.

שיזוקומפתח: Xingchen & Rikka

מחיר: חינם.

4.1.

הורד

למה שיזוקו?

אמנם גישה ברמת מעטפת למערכת לא מאפשרת לך לעשות כל כך הרבה כמו שורש, זה עדיין נותן לך יותר גישה מאשר אפליקציה רגילה. נוסף על כך, הדרך שבה Shizuku עובדת מאפשרת לך להשתמש בממשקי ה-API של אנדרואיד כמעט כמו רגיל. אתה לא צריך להסתמך על פקודות מעטפת (למרות שאתה יכול אם אתה רוצה).

אם האפליקציה שלך זקוקה להרשאות מיוחדות שניתן להעניק רק דרך ADB (או עם שורש), 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 לבצע סנכרון של פרויקט, והמשך אל Usage.


נוֹהָג

בדיקת זמינות

לפני שנכנס לאופן השימוש ב- Shizuku, בואו נדבר על לוודא שהוא באמת זמין לשימוש.

לפני שתבדוק אם ההרשאה ניתנת, ולפני ביצוע קריאות API דרך Shizuku, אתה יכול לוודא שהבדיקות והשיחות הללו יצליחו בשיטה הבאה:

Shizuku.pingBinder()

אם Shizuku מותקן ופועל, זה יחזור נָכוֹן. אחרת, הוא יחזיר false.

מתן רשות

מכיוון ש-Shizuku משתמש בהרשאת זמן ריצה, יש להעניק אותה לאפליקציה שלך לפני שתוכל לעשות משהו עם גישת מעטפת. ישנן גם שתי גרסאות API במחזור, עם דרכים שונות להענקתן. סעיף זה יראה לך כיצד לטפל בשניהם.

בודק

לפני שאתה מבקש את ההרשאה, הדבר הטוב ביותר לעשות הוא לבדוק אם כבר יש לך אותה. אם תעשה זאת, תוכל להמשיך עם כל מה שאתה צריך לעשות. אחרת, תצטרך לבקש זאת לפני שתמשיך.

כדי לבדוק אם יש לך הרשאה להשתמש ב- Shizuku, תוכל להשתמש בדברים הבאים. קוד זה בהנחה שאתה מפעיל אותו בתוך פעילות.

קוטלין:

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, הנה איך.

ה SHIZUKU_CODE המשתנה המשמש להלן צריך להיות מספר שלם עם ערך קבוע (משתנה סטטי).

קוטלין:

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

כדי להאזין לתוצאה, תצטרך לעקוף את הפעילות 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.

}
}

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 השחורה הנסתרת של אנדרואיד, כך שלא תצטרך לדאוג לגבי זה בעת השימוש בה. אם אתה סקרן איך לעקוף את זה בעצמך, עיין במדריך הקודם שלי.

הנה דוגמה מהירה (באמצעות השתקפות) שמראה לך איך אתה יכול לקבל מופע של 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 של angrayudi עבור ערכות ה-SDK שהשתנו והוראות ההתקנה. כאשר זה מותקן, הקוד לעיל (בקוטלין) הופך להרבה יותר פשוט.

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.

שיטה זו פועלת בדומה לשירות רגיל באנדרואיד. אתה "מתחיל" אותו, מתקשר באמצעות קישור אליו עם ServiceConnection, ומפעיל את הלוגיקה שלך במחלקת השירות. ההבדל הוא שאתה לא משתמש בשירות של אנדרואיד, וכל דבר בתוך השירות פועל עם הרשאות ADB.

עכשיו יש כמה מגבלות. שירות המשתמש פועל בתהליך נפרד לחלוטין ומשתמש, כך שאינך יכול ליצור אינטראקציה עם שאר האפליקציה שלך אלא באמצעות התקשרויות AIDL ו-Binders משלך. מכיוון שהוא גם לא פועל בתהליך אפליקציה תקין, ייתכן שחלק מהדברים כמו קשירת שירותי אנדרואיד לא יפעלו כראוי.

זה דורש גם Shizuku גרסה 10 ואילך. בעוד שלרוב המקורות לאפליקציה יש גרסה 11 כרגע, עדיין עליך לכלול בדיקת גרסה, שתיכלל בדוגמה.

הגדרת AIDL

כדי להתחיל, יהיה עליך ליצור קובץ AIDL חדש. אתה יכול לעשות זאת ב-Android Studio על ידי לחיצה ימנית על כל דבר בתצוגת קובץ אנדרואיד, ריחוף מעל האפשרות "חדש" ובחירה באפשרות "AIDL". הזן את שם הקובץ (למשל, "IUserService"), ו-Android Studio יצור עבורך קובץ תבנית.

אם אינך מכיר כיצד פועלות AIDL, הקפד לבדוק התיעוד של גוגל.

הסר את שיטות התבנית מה-AIDL ולאחר מכן הוסף א 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;
}
}

ה binder המשתנה הוא מה שבו תשתמש כדי לתקשר עם שירות המשתמשים מהאפליקציה שלך. כדי לבדוק אם זה זמין לשימוש, פשוט בדוק שזה לא null וזה pingBinder() החזרות נָכוֹן, בדיוק כמו בדוגמה של הקוד למעלה.

יצירת הארגומנטים של שירות המשתמש

לפני שתוכל לשלוט בשירות המשתמש, תצטרך להגדיר כמה ארגומנטים לשימוש שיזוקו בעת הפעלה ועצירה שלו. אלה כוללים דברים כמו למעשה לומר ל-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)

הפעלה, עצירה וקישור של שירות המשתמש

פעולות ההתחלה והכריכה ופעולות העצירה והביטול מאוחדות בשיזוקו. אין שיטות התחלה וחיבור נפרדות או שיטות עצירה וביטול חיבור.

הנה איך לעשות להתחיל ולחייב שירות המשתמשים.

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 זמין לפני שמנסים להשתמש בו.