Skip to content

Commit

Permalink
Test su request via instrumentation
Browse files Browse the repository at this point in the history
  • Loading branch information
topjohnwu committed Dec 25, 2024
1 parent 9a22426 commit fbc8f6b
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 37 deletions.
27 changes: 7 additions & 20 deletions app/core/src/main/java/com/topjohnwu/magisk/test/Environment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import androidx.core.net.toUri
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.topjohnwu.magisk.core.BuildConfig.APP_PACKAGE_NAME
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.download.DownloadNotifier
import com.topjohnwu.magisk.core.download.DownloadProcessor
import com.topjohnwu.magisk.core.ktx.cachedFile
import com.topjohnwu.magisk.core.model.su.SuPolicy
import com.topjohnwu.magisk.core.tasks.AppMigration
import com.topjohnwu.magisk.core.tasks.FlashZip
import com.topjohnwu.magisk.core.tasks.MagiskInstaller
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertTrue
import org.junit.Assume.assumeTrue
Expand All @@ -34,7 +34,11 @@ class Environment {
companion object {
@BeforeClass
@JvmStatic
fun before() = MagiskAppTest.before()
fun before() {
assertTrue("Should have root access", Shell.getShell().isRoot)
// Make sure the root service is running
RootUtils.Connection.await()
}

fun lsposed(): Boolean {
return Build.VERSION.SDK_INT >= 27 && Build.VERSION.SDK_INT <= 34
Expand Down Expand Up @@ -88,23 +92,6 @@ class Environment {
}
}

@Test
fun setupShellGrantTest() {
runBlocking {
// Inject an undetermined + mute logging policy for ADB shell
val policy = SuPolicy(
uid = 2000,
logging = false,
notification = false,
until = 0L
)
ServiceLocator.policyDB.update(policy)
// Bypass the need to actually show a dialog
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
Config.prefs.edit().commit()
}
}

@Test
fun setupAppHide() {
runBlocking {
Expand Down
73 changes: 66 additions & 7 deletions app/core/src/main/java/com/topjohnwu/magisk/test/MagiskAppTest.kt
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package com.topjohnwu.magisk.test

import android.app.Instrumentation
import android.content.Intent
import android.content.IntentFilter
import androidx.annotation.Keep
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.topjohnwu.magisk.core.Config
import com.topjohnwu.magisk.core.Info
import com.topjohnwu.magisk.core.utils.RootUtils
import com.topjohnwu.superuser.Shell
import com.topjohnwu.magisk.core.di.ServiceLocator
import com.topjohnwu.magisk.core.model.su.SuPolicy
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import java.io.FileInputStream
import java.util.concurrent.TimeUnit

@RunWith(AndroidJUnit4::class)
@Keep
Expand All @@ -17,15 +28,63 @@ class MagiskAppTest {
companion object {
@BeforeClass
@JvmStatic
fun before() {
assertTrue("Should have root access", Shell.getShell().isRoot)
// Make sure the root service is running
RootUtils.Connection.await()
}
fun before() = Environment.before()
}

private lateinit var inst: Instrumentation
private val uiAutomation get() = inst.uiAutomation

@Before
fun setup() {
inst = InstrumentationRegistry.getInstrumentation()
}

@Test
fun testZygisk() {
assertTrue("Zygisk should be enabled", Info.isZygiskEnabled)
}

@Test
fun testSuRequest() {
// Bypass the need to actually show a dialog
Config.suAutoResponse = Config.Value.SU_AUTO_ALLOW
Config.prefs.edit().commit()

// Inject an undetermined + mute logging policy for ADB shell
val policy = SuPolicy(
uid = 2000,
logging = false,
notification = false,
until = 0L
)
runBlocking {
ServiceLocator.policyDB.update(policy)
}

val filter = IntentFilter(Intent.ACTION_VIEW)
filter.addCategory(Intent.CATEGORY_DEFAULT)
val monitor = inst.addMonitor(filter, null, false)

// Try to call su from ADB shell
var pfd = uiAutomation.executeShellCommand("su -c id")

// Make sure SuRequestActivity is launched
val suRequest = monitor.waitForActivityWithTimeout(TimeUnit.SECONDS.toMillis(10))
assertNotNull("SuRequestActivity is not launched", suRequest)

// Check that the request went through
FileInputStream(pfd.fileDescriptor).use {
assertTrue(
"Cannot grant root permission from shell",
it.reader().readText().contains("uid=0")
)
}

// Check that the database is updated
runBlocking {
val policy = ServiceLocator.policyDB.fetch(2000)
?: throw AssertionError("PolicyDB is invalid")
assertEquals("Policy for shell is incorrect", SuPolicy.ALLOW, policy.policy)
}
}
}
10 changes: 0 additions & 10 deletions scripts/test_common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,13 @@ run_tests() {
# Run app tests
am_instrument '.MagiskAppTest,.AdditionalTest'

# Test shell su request
am_instrument '.Environment#setupShellGrantTest'
adb shell /system/xbin/su 2000 su -c id | tee /dev/fd/2 | grep -q 'uid=0'
adb shell am force-stop com.topjohnwu.magisk

# Test app hiding
am_instrument '.Environment#setupAppHide'
wait_for_pm com.topjohnwu.magisk

# Make sure it still works
am_instrument '.MagiskAppTest' true

# Test shell su request
am_instrument '.Environment#setupShellGrantTest' true
adb shell /system/xbin/su 2000 su -c id | tee /dev/fd/2 | grep -q 'uid=0'
adb shell am force-stop repackaged.com.topjohnwu.magisk

# Test app restore
am_instrument '.Environment#setupAppRestore' true
wait_for_pm repackaged.com.topjohnwu.magisk
Expand Down

0 comments on commit fbc8f6b

Please sign in to comment.