การรูตไม่ใช่สำหรับทุกคน ต่อไปนี้คือวิธีที่คุณสามารถรับสิทธิ์ระดับเชลล์ระดับสูงในแอปของคุณโดยใช้ไลบรารี Shizuku
มีเหตุผลหลายประการที่การอนุญาตตามปกติที่มอบให้กับแอปของคุณอาจไม่เพียงพอ บางทีคุณอาจเป็นเหมือนฉันและสนุกกับการสร้างแอปแฮ็กที่ใช้ Android API ในทางที่ผิด API บางตัวที่ฉันใช้ถูกล็อกตามสิทธิ์พิเศษ บางครั้งมีเพียงผู้ใช้เชลล์ (ADB) หรือระบบเท่านั้นที่สามารถเข้าถึงได้ มีวิธีแก้ไขอยู่ — ชิซึกุ
Shizuku ช่วยให้คุณสามารถเรียก API ของระบบได้เกือบจะโดยตรง และทั้งหมดเป็นภาษา Java หรือ Kotlin คู่มือนี้จะแสดงวิธีการนำไปใช้และใช้งาน Shizuku
ชิซึกุคืออะไร?
ก่อนที่เราจะไปใช้ Shizuku การรู้ว่ามันคืออะไรอาจเป็นประโยชน์ หากคุณคุ้นเคย. มาจิสก์แล้วชิซึกุก็คล้ายกัน แต่แทนที่จะจัดการการเข้าถึงรูท กลับจัดการการเข้าถึงเชลล์แทน
Shizuku รันกระบวนการของตัวเองด้วยสิทธิ์ระดับเชลล์ วิธีที่ผู้ใช้เปิดใช้งานกระบวนการนั้นขึ้นอยู่กับอุปกรณ์ เวอร์ชัน Android และตัวเลือก Shizuku สามารถเปิดใช้งานผ่าน ADB ผ่าน ADB ไร้สายบนอุปกรณ์ (บน Android 11 และใหม่กว่า) หรือผ่านการเข้าถึงรูท แอพที่ใช้ Shizuku จะสามารถขออนุญาตใช้กระบวนการนั้นเพื่อดำเนินการยกระดับได้
ราคา: ฟรี
4.1.
ทำไมต้องชิซึกุ?
ในขณะที่การเข้าถึงระบบระดับเชลล์ไม่ได้ทำให้คุณทำอะไรได้มากเท่ากับ รากแต่ยังช่วยให้คุณเข้าถึงได้มากกว่าแอปปกติ ยิ่งไปกว่านั้น วิธีการทำงานของ Shizuku ยังช่วยให้คุณใช้ Android API ได้เกือบเหมือนปกติ คุณไม่จำเป็นต้องพึ่งพาคำสั่งเชลล์ (แม้ว่าคุณจะทำได้ถ้าคุณต้องการก็ตาม)
หากแอปของคุณต้องการสิทธิ์พิเศษที่สามารถให้ผ่าน ADB เท่านั้น (หรือผ่านรูท) Shizuku และ Android 11 จะเป็นการจับคู่ที่ยอดเยี่ยม คุณสามารถใช้ Shizuku เพื่อให้สิทธิ์พิเศษบนอุปกรณ์ได้อย่างเต็มที่
แม้ว่าอุปกรณ์ที่ไม่ได้ใช้ Android 11 แต่ 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 ทำงานได้ คุณต้องเพิ่มบล็อกผู้ให้บริการลงในไฟล์ Manifest ของแอป เปิด 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 สิ่งนี้จะกลับมา จริง. มิฉะนั้นจะคืนค่าเท็จ
การอนุญาต
เนื่องจาก Shizuku ใช้สิทธิ์รันไทม์ คุณจะต้องให้สิทธิ์แก่แอปของคุณก่อนจึงจะสามารถดำเนินการใดๆ ด้วยการเข้าถึงเชลล์ได้ นอกจากนี้ยังมี 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 มีวิธีดังต่อไปนี้
ที่ ชิซูกุ_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 และได้รับสิทธิ์อนุญาตแล้ว คุณสามารถเริ่มเรียกใช้ 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)
ชวา:
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
ขณะนี้มีข้อจำกัดอยู่บ้าง ฝ่ายบริการผู้ใช้ทำงานในกระบวนการและผู้ใช้ที่แยกจากกันโดยสิ้นเชิง ดังนั้นคุณจึงไม่สามารถโต้ตอบกับแอปที่เหลือได้ ยกเว้นผ่านทางการโทรกลับและ Binders ของ AIDL ของคุณเอง เนื่องจากแอปไม่ทำงานในกระบวนการแอปที่เหมาะสม บางอย่าง เช่น การผูกบริการ Android อาจทำงานไม่ถูกต้อง
สิ่งนี้ต้องใช้ Shizuku เวอร์ชัน 10 หรือใหม่กว่าด้วย แม้ว่าแหล่งที่มาส่วนใหญ่สำหรับแอปจะมีเวอร์ชัน 11 ในปัจจุบัน แต่คุณยังคงควรรวมการตรวจสอบเวอร์ชันซึ่งจะรวมอยู่ในตัวอย่าง
การกำหนด AIDL
ในการเริ่มต้น คุณจะต้องสร้างไฟล์ AIDL ใหม่ คุณทำได้ใน Android Studio โดยคลิกขวาที่อะไรก็ได้ในมุมมองไฟล์ Android วางเมาส์เหนือตัวเลือก "ใหม่" แล้วเลือกตัวเลือก "AIDL" ป้อนชื่อไฟล์ (เช่น "IUserService") แล้ว Android Studio จะสร้างไฟล์เทมเพลตให้คุณ
หากคุณไม่คุ้นเคยกับวิธีการทำงานของ AIDL โปรดลองดู เอกสารของ Google.
ลบวิธีการเทมเพลตออกจาก AIDL แล้วเพิ่ม destroy()
วิธีการที่มี ID ที่เหมาะสมสำหรับ Shizuku สิ่งนี้จะถูกเรียกเมื่อ Shizuku ทำลายบริการผู้ใช้ และควรใช้เพื่อล้างข้อมูลอ้างอิงหรือตรรกะต่อเนื่องที่คุณมี
ตัวอย่างโรคเอดส์:
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)
}
}
ตัวอย่างข้างต้นถือว่าคุณมี API ที่ซ่อนอยู่ของ Android ติดตั้ง 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
ตัวแปรคือสิ่งที่คุณจะใช้เพื่อสื่อสารกับบริการผู้ใช้จากแอปของคุณ หากต้องการตรวจสอบว่าสามารถใช้งานได้หรือไม่ เพียงตรวจสอบว่าไม่เป็นโมฆะหรือไม่ 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)
การเริ่มต้น การหยุด และการเชื่อมโยงบริการผู้ใช้
การกระทำเริ่มต้นและผูกและการกระทำหยุดและเลิกผูกจะรวมกันเป็นหนึ่งเดียวในชิซูกุ ไม่มีวิธีการเริ่มต้นและผูกแยกหรือวิธีการหยุดและเลิกผูก
ต่อไปนี้คือวิธีการ เริ่มต้นและผูกมัด การบริการผู้ใช้
ชวา:
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
ตัวแปรไม่เป็นค่าว่างและสามารถ 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 พร้อมใช้งานก่อนที่จะลองใช้