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