1 package android.platform.test.rule
2 
3 import android.os.SystemProperties
4 import android.platform.uiautomator_helpers.DeviceHelpers.shell
5 import android.provider.Settings
6 import org.junit.runner.Description
7 
8 /**
9  * Makes sure settings that are required to run tests are present and in the correct states.
10  *
11  * Suggests commands to fix them.
12  */
13 class EnsureDeviceSettingsRule : TestWatcher() {
14 
15     private val setupErrors = mutableListOf<SetupError>()
startingnull16     override fun starting(description: Description?) {
17         checkAdbRootEnabled()
18         checkTestHarnessEnabled()
19         checkStayAwakeEnabled()
20         if (setupErrors.isNotEmpty()) throwSetupErrors()
21     }
22 
checkAdbRootEnablednull23     private fun checkAdbRootEnabled() {
24         val adbIdResult = uiDevice.shell("id -u").trim()
25 
26         if (adbIdResult != "0") {
27             setupErrors.add(
28                 SetupError(
29                     description = "ADB root access is required but disabled.",
30                     adbCommandToFixIt = "adb root"
31                 )
32             )
33         }
34     }
35 
checkTestHarnessEnablednull36     private fun checkTestHarnessEnabled() {
37         val mobileHarnessModeEnabled = SystemProperties.getBoolean(TEST_HARNESS_PROP, false)
38         if (!mobileHarnessModeEnabled) {
39             setupErrors.add(
40                 SetupError(
41                     description = "Test harness' mode is required but disabled.",
42                     adbCommandToFixIt =
43                         "adb shell setprop $TEST_HARNESS_PROP 1; " +
44                             "adb shell am force-stop $LAUNCHER_PACKAGE",
45                 )
46             )
47         }
48     }
49 
50     /**
51      * Setting value of "Stay awake" is bit-based with 4 bits responsible for different types of
52      * charging. So the value is device-dependent but non-zero value means the settings is on. See
53      * [Settings.Global.STAY_ON_WHILE_PLUGGED_IN] for more information.
54      */
checkStayAwakeEnablednull55     private fun checkStayAwakeEnabled() {
56         val stayAwakeResult =
57             Settings.Global.getInt(
58                 context.contentResolver,
59                 Settings.Global.STAY_ON_WHILE_PLUGGED_IN
60             )
61         if (stayAwakeResult == 0) {
62             setupErrors.add(
63                 SetupError(
64                     description = "'Stay awake' option in developer settings should be enabled",
65                     adbCommandToFixIt = "adb shell settings put global stay_on_while_plugged_in 7"
66                 )
67             )
68         }
69     }
70 
throwSetupErrorsnull71     private fun throwSetupErrors() {
72         val message = setupErrors.map { it.description }.joinToString("\n")
73         val command = setupErrors.map { it.adbCommandToFixIt }.joinToString("; \\\n")
74         throw AssertionError(
75             """
76 
77       ${"-".repeat(80)}
78       SETUP ERROR:
79       $message
80 
81       Run the following command to fix:
82 
83       ${command.prependIndent("   ")}
84       ${"-".repeat(80)}
85       """
86                 .trimIndent()
87         )
88     }
89 
90     private data class SetupError(
91         val description: String,
92         val adbCommandToFixIt: String,
93     )
94 
95     private companion object {
96         const val TEST_HARNESS_PROP = "ro.test_harness"
97         const val LAUNCHER_PACKAGE = "com.google.android.apps.nexuslauncher"
98     }
99 }
100