1 /* <lambda>null2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.testutils 18 19 import com.android.ddmlib.testrunner.TestResult 20 import com.android.tradefed.config.Option 21 import com.android.tradefed.invoker.TestInformation 22 import com.android.tradefed.result.CollectingTestListener 23 import com.android.tradefed.result.ddmlib.DefaultRemoteAndroidTestRunner 24 import com.android.tradefed.targetprep.BaseTargetPreparer 25 import com.android.tradefed.targetprep.TargetSetupError 26 import com.android.tradefed.targetprep.suite.SuiteApkInstaller 27 28 private const val CONNECTIVITY_CHECKER_APK = "ConnectivityTestPreparer.apk" 29 private const val CONNECTIVITY_PKG_NAME = "com.android.testutils.connectivitypreparer" 30 private const val CONNECTIVITY_CHECK_CLASS = "$CONNECTIVITY_PKG_NAME.ConnectivityCheckTest" 31 32 // As per the <instrumentation> defined in the checker manifest 33 private const val CONNECTIVITY_CHECK_RUNNER_NAME = "androidx.test.runner.AndroidJUnitRunner" 34 private const val IGNORE_WIFI_CHECK = "ignore-wifi-check" 35 private const val IGNORE_MOBILE_DATA_CHECK = "ignore-mobile-data-check" 36 37 // The default updater package names, which might be updating packages while the CTS 38 // are running 39 private val UPDATER_PKGS = arrayOf("com.google.android.gms", "com.android.vending") 40 41 /** 42 * A target preparer that sets up and verifies a device for connectivity tests. 43 * 44 * For quick and dirty local testing, the connectivity check can be disabled by running tests with 45 * "atest -- \ 46 * --test-arg com.android.testutils.ConnectivityTestTargetPreparer:ignore-mobile-data-check:true". \ 47 * --test-arg com.android.testutils.ConnectivityTestTargetPreparer:ignore-wifi-check:true". 48 */ 49 open class ConnectivityTestTargetPreparer : BaseTargetPreparer() { 50 private val installer = SuiteApkInstaller() 51 52 @Option( 53 name = IGNORE_WIFI_CHECK, 54 description = "Disables the check for wifi" 55 ) 56 private var ignoreWifiCheck = false 57 @Option( 58 name = IGNORE_MOBILE_DATA_CHECK, 59 description = "Disables the check for mobile data" 60 ) 61 private var ignoreMobileDataCheck = false 62 63 // The default value is never used, but false is a reasonable default 64 private var originalTestChainEnabled = false 65 private val originalUpdaterPkgsStatus = HashMap<String, Boolean>() 66 67 override fun setUp(testInfo: TestInformation) { 68 if (isDisabled) return 69 disableGmsUpdate(testInfo) 70 originalTestChainEnabled = getTestChainEnabled(testInfo) 71 originalUpdaterPkgsStatus.putAll(getUpdaterPkgsStatus(testInfo)) 72 setUpdaterNetworkingEnabled( 73 testInfo, 74 enableChain = true, 75 enablePkgs = UPDATER_PKGS.associateWith { false } 76 ) 77 runConnectivityCheckApk(testInfo) 78 refreshTime(testInfo) 79 } 80 81 private fun runConnectivityCheckApk(testInfo: TestInformation) { 82 installer.setCleanApk(true) 83 installer.addTestFileName(CONNECTIVITY_CHECKER_APK) 84 installer.setShouldGrantPermission(true) 85 installer.setUp(testInfo) 86 87 val testMethods = mutableListOf<String>() 88 if (!ignoreWifiCheck) { 89 testMethods.add("testCheckWifiSetup") 90 } 91 if (!ignoreMobileDataCheck) { 92 testMethods.add("testCheckTelephonySetup") 93 } 94 95 testMethods.forEach { 96 runTestMethod(testInfo, it) 97 } 98 } 99 100 private fun runTestMethod(testInfo: TestInformation, method: String) { 101 val runner = DefaultRemoteAndroidTestRunner( 102 CONNECTIVITY_PKG_NAME, 103 CONNECTIVITY_CHECK_RUNNER_NAME, 104 testInfo.device.iDevice 105 ) 106 runner.runOptions = "--no-hidden-api-checks" 107 runner.setMethodName(CONNECTIVITY_CHECK_CLASS, method) 108 109 val receiver = CollectingTestListener() 110 if (!testInfo.device.runInstrumentationTests(runner, receiver)) { 111 throw TargetSetupError( 112 "Device state check failed to complete", 113 testInfo.device.deviceDescriptor 114 ) 115 } 116 117 val runResult = receiver.currentRunResults 118 if (runResult.isRunFailure) { 119 throw TargetSetupError( 120 "Failed to check device state before the test: " + 121 runResult.runFailureMessage, 122 testInfo.device.deviceDescriptor 123 ) 124 } 125 126 val errorMsg = runResult.testResults.mapNotNull { (testDescription, testResult) -> 127 if (TestResult.TestStatus.FAILURE != testResult.status) { 128 null 129 } else { 130 "$testDescription: ${testResult.stackTrace}" 131 } 132 }.joinToString("\n") 133 if (errorMsg.isBlank()) return 134 135 throw TargetSetupError( 136 "Device setup checks failed. Check the test bench: \n$errorMsg", 137 testInfo.device.deviceDescriptor 138 ) 139 } 140 141 private fun disableGmsUpdate(testInfo: TestInformation) { 142 // This will be a no-op on devices without root (su) or not using gservices, but that's OK. 143 testInfo.exec( 144 "su 0 am broadcast " + 145 "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " + 146 "-e finsky.play_services_auto_update_enabled false" 147 ) 148 } 149 150 private fun clearGmsUpdateOverride(testInfo: TestInformation) { 151 testInfo.exec( 152 "su 0 am broadcast " + 153 "-a com.google.gservices.intent.action.GSERVICES_OVERRIDE " + 154 "--esn finsky.play_services_auto_update_enabled" 155 ) 156 } 157 158 private fun setUpdaterNetworkingEnabled( 159 testInfo: TestInformation, 160 enableChain: Boolean, 161 enablePkgs: Map<String, Boolean> 162 ) { 163 // Build.VERSION_CODES.S = 31 where this is not available, then do nothing. 164 if (testInfo.device.getApiLevel() < 31) return 165 testInfo.exec("cmd connectivity set-chain3-enabled $enableChain") 166 enablePkgs.forEach { (pkg, allow) -> 167 testInfo.exec("cmd connectivity set-package-networking-enabled $allow $pkg") 168 } 169 } 170 171 private fun getTestChainEnabled(testInfo: TestInformation) = 172 testInfo.exec("cmd connectivity get-chain3-enabled").contains("chain:enabled") 173 174 private fun getUpdaterPkgsStatus(testInfo: TestInformation) = 175 UPDATER_PKGS.associateWith { pkg -> 176 !testInfo.exec("cmd connectivity get-package-networking-enabled $pkg") 177 .contains(":deny") 178 } 179 180 private fun refreshTime(testInfo: TestInformation,) { 181 // Forces a synchronous time refresh using the network. Time is fetched synchronously but 182 // this does not guarantee that system time is updated when it returns. 183 // This avoids flakes where the system clock rolls back, for example when using test 184 // settings like test_url_expiration_time in NetworkMonitor. 185 testInfo.exec("cmd network_time_update_service force_refresh") 186 } 187 188 override fun tearDown(testInfo: TestInformation, e: Throwable?) { 189 if (isTearDownDisabled) return 190 installer.tearDown(testInfo, e) 191 setUpdaterNetworkingEnabled( 192 testInfo, 193 enableChain = originalTestChainEnabled, 194 enablePkgs = originalUpdaterPkgsStatus 195 ) 196 clearGmsUpdateOverride(testInfo) 197 } 198 } 199