diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5327a1df..6982f8f6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,13 @@
+
+
+
+
+
+
+
{
+ val version = queryMagiskVersion()
+ val isMagisk = version != null || packageHit || mountHit || probeMagiskFiles()
+ RootResult.Rooted(
+ provider = if (isMagisk) RootProvider.MAGISK else RootProvider.OTHER,
+ version = version,
+ )
+ }
+
+ false -> RootResult.NotRooted
+ null -> RootResult.Unknown
+ }
delay(1000)
result
}
+
+ private fun probeMagiskPackage(context: Context): Boolean {
+ val pm = context.packageManager
+ return MAGISK_PACKAGES.any { name ->
+ try {
+ pm.getPackageInfo(name, 0)
+ true
+ } catch (_: PackageManager.NameNotFoundException) {
+ false
+ }
+ }
+ }
+
+ private fun probeMagiskMounts(): Boolean = try {
+ File("/proc/self/mounts").readText().contains("magisk", ignoreCase = true)
+ } catch (_: Exception) {
+ false
+ }
+
+ private fun probeMagiskFiles(): Boolean {
+ val result = Shell.cmd(
+ "test -d /data/adb/magisk || test -d /sbin/.magisk || test -d /data/adb/modules"
+ ).exec()
+ return result.isSuccess
+ }
+
+ private fun queryMagiskVersion(): String? {
+ val nameResult = Shell.cmd("magisk -V").exec()
+ if (nameResult.isSuccess) {
+ val name = nameResult.out.firstOrNull()?.trim().orEmpty()
+ if (name.isNotEmpty()) return name
+ }
+ val codeResult = Shell.cmd("magisk -vV").exec()
+ if (codeResult.isSuccess) {
+ val raw = codeResult.out.firstOrNull()?.trim().orEmpty()
+ val code = raw.substringBefore(':').toLongOrNull()
+ if (code != null) {
+ val major = code / 1000
+ val minor = (code % 1000) / 100
+ return "$major.$minor"
+ }
+ }
+ return null
+ }
}
diff --git a/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainScreen.kt b/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainScreen.kt
index 648a4ac8..a49099a2 100644
--- a/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainScreen.kt
+++ b/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainScreen.kt
@@ -74,6 +74,7 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import com.iboalali.basicrootchecker.R
+import com.iboalali.basicrootchecker.data.RootProvider
import com.iboalali.basicrootchecker.ui.theme.BasicRootCheckerTheme
import com.iboalali.basicrootchecker.update.AppUpdateEvent
import com.iboalali.basicrootchecker.util.PreviewLocales
@@ -319,6 +320,29 @@ fun MainScreenContent(
textAlign = TextAlign.Center,
)
}
+
+ if (uiState.rootStatus == RootStatus.ROOTED &&
+ uiState.rootProvider != RootProvider.UNKNOWN
+ ) {
+ val providerName = when (uiState.rootProvider) {
+ RootProvider.MAGISK -> stringResource(R.string.root_provider_magisk)
+ RootProvider.OTHER -> stringResource(R.string.root_provider_other)
+ RootProvider.UNKNOWN -> ""
+ }
+ val version = uiState.rootProviderVersion
+ val providerText = if (version != null) {
+ stringResource(R.string.root_provider_via_with_version, providerName, version)
+ } else {
+ stringResource(R.string.root_provider_via, providerName)
+ }
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = providerText,
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ textAlign = TextAlign.Center,
+ )
+ }
}
}
@@ -490,6 +514,8 @@ private fun MainScreenRootedPreview() {
MainScreenContent(
uiState = MainUiState(
rootStatus = RootStatus.ROOTED,
+ rootProvider = RootProvider.MAGISK,
+ rootProviderVersion = "27.0",
deviceMarketingName = "Pixel 8 Pro",
deviceModelName = "husky",
androidVersion = "Android 16",
diff --git a/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainViewModel.kt b/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainViewModel.kt
index 4b0f2672..948e5645 100644
--- a/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainViewModel.kt
+++ b/app/src/main/java/com/iboalali/basicrootchecker/ui/main/MainViewModel.kt
@@ -9,6 +9,8 @@ import com.iboalali.basicrootchecker.BuildConfig
import com.iboalali.basicrootchecker.R
import com.iboalali.basicrootchecker.analytics.Analytics
import com.iboalali.basicrootchecker.data.RootChecker
+import com.iboalali.basicrootchecker.data.RootProvider
+import com.iboalali.basicrootchecker.data.RootResult
import com.iboalali.basicrootchecker.data.UserPreferences
import com.iboalali.basicrootchecker.update.AppUpdateEvent
import com.iboalali.basicrootchecker.util.DeviceInfo
@@ -30,6 +32,8 @@ enum class RootStatus {
data class MainUiState(
val rootStatus: RootStatus = RootStatus.NOT_CHECKED,
+ val rootProvider: RootProvider = RootProvider.UNKNOWN,
+ val rootProviderVersion: String? = null,
val deviceMarketingName: String = "",
val deviceModelName: String = "",
val androidVersion: String = "",
@@ -87,16 +91,31 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
fun checkRoot() {
viewModelScope.launch {
- _uiState.update { it.copy(rootStatus = RootStatus.CHECKING) }
+ _uiState.update {
+ it.copy(
+ rootStatus = RootStatus.CHECKING,
+ rootProvider = RootProvider.UNKNOWN,
+ rootProviderVersion = null,
+ )
+ }
Analytics.trackRootCheckStarted()
- val result = RootChecker.checkRoot()
- val status = when (result) {
- true -> RootStatus.ROOTED
- false -> RootStatus.NOT_ROOTED
- null -> RootStatus.UNKNOWN
+ val result = RootChecker.check(getApplication())
+ val (status, provider, version) = when (result) {
+ is RootResult.Rooted -> Triple(RootStatus.ROOTED, result.provider, result.version)
+ RootResult.NotRooted -> Triple(RootStatus.NOT_ROOTED, RootProvider.UNKNOWN, null)
+ RootResult.Unknown -> Triple(RootStatus.UNKNOWN, RootProvider.UNKNOWN, null)
+ }
+ _uiState.update {
+ it.copy(
+ rootStatus = status,
+ rootProvider = provider,
+ rootProviderVersion = version,
+ )
}
- _uiState.update { it.copy(rootStatus = status) }
Analytics.trackRootCheckResult(status.name)
+ if (status == RootStatus.ROOTED) {
+ Analytics.trackRootProvider(provider.name, version)
+ }
}
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 09dfeb57..453e602d 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -7,6 +7,10 @@
Your Device has Root access
Couldn\'t get Root status of your device
Your Device doesn\'t have Root access
+ Magisk
+ Other
+ via %1$s
+ via %1$s v%2$s
Licenses
About
Settings