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]