diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1b01983..df68ac1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -106,6 +106,8 @@ dependencies { implementation(libs.androidx.navigation.compose) implementation(libs.shizuku.provider) implementation(libs.shizuku.api) + implementation(libs.dhizuku.api) implementation(libs.androidx.biometric) implementation(libs.androidx.fragment) + implementation(libs.hiddenApiBypass) } \ No newline at end of file diff --git a/app/src/main/java/android/app/admin/IDevicePolicyManager.java b/app/src/main/java/android/app/admin/IDevicePolicyManager.java new file mode 100644 index 0000000..87c8c59 --- /dev/null +++ b/app/src/main/java/android/app/admin/IDevicePolicyManager.java @@ -0,0 +1,13 @@ +package android.app.admin; + +import android.os.Binder; +import android.os.IBinder; +import android.os.IInterface; + +public interface IDevicePolicyManager extends IInterface { + abstract class Stub extends Binder implements IDevicePolicyManager { + public static IDevicePolicyManager asInterface(IBinder obj) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt index a180ab2..1988e3f 100644 --- a/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt +++ b/app/src/main/java/com/bintianqi/owndroid/MainActivity.kt @@ -3,6 +3,7 @@ package com.bintianqi.owndroid import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context +import android.os.Build import android.os.Build.VERSION import android.os.Bundle import android.widget.Toast @@ -40,7 +41,9 @@ import androidx.navigation.compose.rememberNavController import com.bintianqi.owndroid.dpm.* import com.bintianqi.owndroid.ui.Animations import com.bintianqi.owndroid.ui.theme.OwnDroidTheme +import com.rosan.dhizuku.api.Dhizuku import kotlinx.coroutines.flow.MutableStateFlow +import org.lsposed.hiddenapibypass.HiddenApiBypass import java.util.Locale var backToHomeStateFlow = MutableStateFlow(false) @@ -54,6 +57,12 @@ class MainActivity : FragmentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) super.onCreate(savedInstanceState) val sharedPref = applicationContext.getSharedPreferences("data", Context.MODE_PRIVATE) + if (sharedPref.getBoolean("dhizuku", false)) { + if (VERSION.SDK_INT >= 28) HiddenApiBypass.setHiddenApiExemptions("") + if(!Dhizuku.init(applicationContext)) { + Toast.makeText(applicationContext, R.string.failed_to_init_dhizuku, Toast.LENGTH_SHORT).show() + } + } if(sharedPref.getBoolean("auth", false)) { showAuth.value = true } diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt index 652ddd6..4680189 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/DPM.kt @@ -1,14 +1,22 @@ package com.bintianqi.owndroid.dpm +import android.annotation.SuppressLint import android.app.PendingIntent import android.app.admin.DevicePolicyManager +import android.app.admin.IDevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageInstaller +import android.content.pm.PackageManager import android.os.Build.VERSION +import androidx.activity.ComponentActivity.CONTEXT_IGNORE_SECURITY +import androidx.activity.ComponentActivity.DEVICE_POLICY_SERVICE import androidx.activity.result.ActivityResultLauncher import com.bintianqi.owndroid.PackageInstallerReceiver +import com.rosan.dhizuku.api.Dhizuku +import com.rosan.dhizuku.api.Dhizuku.binderWrapper +import com.rosan.dhizuku.api.DhizukuBinderWrapper import kotlinx.coroutines.flow.MutableStateFlow import java.io.IOException import java.io.InputStream @@ -46,3 +54,26 @@ fun installPackage(context: Context, inputStream: InputStream) { val pendingIntent = PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_IMMUTABLE).intentSender session.commit(pendingIntent) } + +@SuppressLint("PrivateApi") +fun binderWrapperDevicePolicyManager(appContext: Context): DevicePolicyManager { + try { + val context = appContext.createPackageContext(Dhizuku.getOwnerComponent().packageName, CONTEXT_IGNORE_SECURITY) + val manager = context.getSystemService(DEVICE_POLICY_SERVICE) as DevicePolicyManager + val field = manager.javaClass.getDeclaredField("mService") + field.isAccessible = true + val oldInterface = field[manager] as IDevicePolicyManager + if (oldInterface is DhizukuBinderWrapper) return manager + val oldBinder = oldInterface.asBinder() + val newBinder = binderWrapper(oldBinder) + val newInterface = IDevicePolicyManager.Stub.asInterface(newBinder) + field[manager] = newInterface + return manager + } catch (e: NoSuchFieldException) { + throw RuntimeException(e) + } catch (e: IllegalAccessException) { + throw RuntimeException(e) + } catch (e: PackageManager.NameNotFoundException) { + throw RuntimeException(e) + } +} diff --git a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt index 7b4c38f..b8edcd8 100644 --- a/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt +++ b/app/src/main/java/com/bintianqi/owndroid/dpm/Permissions.kt @@ -6,7 +6,9 @@ import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Build.VERSION +import android.os.RemoteException import android.widget.Toast import androidx.activity.ComponentActivity import androidx.compose.animation.AnimatedVisibility @@ -38,6 +40,8 @@ import com.bintianqi.owndroid.R import com.bintianqi.owndroid.Receiver import com.bintianqi.owndroid.backToHomeStateFlow import com.bintianqi.owndroid.ui.* +import com.rosan.dhizuku.api.Dhizuku +import com.rosan.dhizuku.api.DhizukuRequestPermissionListener import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -88,12 +92,42 @@ private fun Home(localNavCtrl:NavHostController,listScrollState:ScrollState) { val context = LocalContext.current val dpm = context.getSystemService(ComponentActivity.DEVICE_POLICY_SERVICE) as DevicePolicyManager val receiver = ComponentName(context, Receiver::class.java) + val sharedPref = LocalContext.current.getSharedPreferences("data", Context.MODE_PRIVATE) + var dhizukuStatus by remember { mutableStateOf(sharedPref.getBoolean("dhizuku", false)) } Column(modifier = Modifier.fillMaxSize().verticalScroll(listScrollState)) { Text( text = stringResource(R.string.permission), style = typography.headlineLarge, modifier = Modifier.padding(top = 8.dp, bottom = 5.dp, start = 15.dp) ) + SwitchItem( + R.string.dhizuku, "", null, + { dhizukuStatus }, + { + if(!it) { + sharedPref.edit().putBoolean("dhizuku", false).apply() + dhizukuStatus = sharedPref.getBoolean("dhizuku", false) + return@SwitchItem + } + Dhizuku.init(context) + if(Dhizuku.isPermissionGranted()) { + sharedPref.edit().putBoolean("dhizuku", true).apply() + dhizukuStatus = sharedPref.getBoolean("dhizuku", false) + } else { + Dhizuku.requestPermission(object: DhizukuRequestPermissionListener() { + @Throws(RemoteException::class) + override fun onRequestPermission(grantResult: Int) { + if(grantResult == PackageManager.PERMISSION_GRANTED) { + sharedPref.edit().putBoolean("dhizuku", true).apply() + dhizukuStatus = sharedPref.getBoolean("dhizuku", false) + } else { + Toast.makeText(context, R.string.permission_not_granted, Toast.LENGTH_SHORT).show() + } + } + }) + } + } + ) SubPageItem( R.string.device_admin, stringResource(if(dpm.isAdminActive(receiver)) R.string.activated else R.string.deactivated), operation = { localNavCtrl.navigate("DeviceAdmin") } diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index f502e9d..9215610 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -99,6 +99,9 @@ OwnDroid: Devre Dışı OwnDroid: İş Profili Başarıyla Oluşturuldu + + Permission not granted + Failed to initialize Dhizuku İzni Kontrol Et Sahipleri Listele diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 1faecb5..cd338f1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -94,6 +94,9 @@ OwnDroid:已禁用 OwnDroid:创建工作资料成功 + + 未授权 + Dhizuku初始化失败 检查Shizuku 列出Owners diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa3c1ad..28be277 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -101,6 +101,10 @@ OwnDroid: Disabled OwnDroid: Create work profile success + + Dhizuku + Permission not granted + Failed to initialize Dhizuku Shizuku Check permission diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 01038b9..767e417 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,8 @@ accompanist-drawablepainter = "0.35.0-alpha" shizuku = "13.1.5" biometric = "1.2.0-alpha05" fragment = "1.8.0-beta01" +dhizuku = "2.5.2" +hiddenApiBypass = "4.3" [libraries] androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" } @@ -23,6 +25,8 @@ androidx-biometric = { group = "androidx.biometric", name = "biometric", version shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" } shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" } +dhizuku-api = { module = "io.github.iamr0s:Dhizuku-API", version.ref = "dhizuku" } +hiddenApiBypass = { module = "org.lsposed.hiddenapibypass:hiddenapibypass", version.ref = "hiddenApiBypass" } androidx-fragment = { group = "androidx.fragment", name = "fragment", version.ref = "fragment" } [plugins]