1 /* 2 * Copyright (C) 2018 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.tradefed.targetprep; 18 19 import com.android.tradefed.build.IBuildInfo; 20 import com.android.tradefed.config.Option; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.ITestDevice; 23 import com.android.tradefed.log.LogUtil.CLog; 24 25 /** 26 * Perform basic device preparation tasks such as check (and turn on/off) device framework. 27 * 28 * Options with prefix "enable" or "disable" enables or disables a feature, 29 * and setting them to false means no operation. 30 * 31 * For a same feature, if "enable" and "disable" options are both set to true will result in no-op. 32 * 33 * Any of the "restore" option will restore the feature to the state before test. This means actions 34 * may be taken even if the feature is not enabled. For example, if device framework is on, 35 * "enable-framework" is "false", "restore-all" is set to "true", and during a test the framework 36 * is turned on/off, this class will start the framework in the end. 37 */ 38 public class VtsDevicePreparer implements ITargetPreparer, ITargetCleaner { 39 @Option(name = "start-framework", 40 description = "Whether to start android framework on device. " 41 + "Setting this option to false does not stop framework. " 42 + "If both start-framework and stop-framework is set to true, " 43 + "no action will be taken.") 44 boolean mStartFramework = false; 45 46 @Option(name = "stop-framework", 47 description = "Whether to stop android framework on device. " 48 + "Setting this option to false does not start framework. " 49 + "If both start-framework and disable-framework is set to true, " 50 + "no action will be taken.") 51 boolean mStopFramework = false; 52 53 @Option(name = "restore-framework", 54 description = "Whether to restore the initial framework " 55 + "start/stop status after tests. " 56 + "This option will be overriden by restore-all if that is set to true.") 57 boolean mRestoreFramework = false; 58 59 @Option(name = "enable-adb-root", 60 description = "Whether to enable adb root on device. " 61 + "If set to true, `adb root` will be called. " 62 + "Setting this option to false does not disable adb root. " 63 + "This option requires enable-root setting be true in test plan setting. " 64 + "If both enable-adb-root and disable-adb-root is set to true, no action " 65 + "will be taken.") 66 boolean mEnableAdbRoot = false; 67 68 @Option(name = "disable-adb-root", 69 description = "Whether to disable adb root on device. " 70 + "If set to true, `adb root` will be called. " 71 + "Setting this option to false does not enable adb root. " 72 + "If both enable-adb-root and disable-adb-root is set to true, " 73 + "no action will be taken.") 74 boolean mDisableAdbRoot = false; 75 76 @Option(name = "restore-adb-root", 77 description = "Whether to restore the initial adb root " 78 + "status after tests. " 79 + "This option will be overriden by restore-all if that is set to true.") 80 boolean mRestoreAdbRoot = false; 81 82 @Option(name = "restore-all", 83 description = "Whether to restore device status after tests. " 84 + "This option overrides individual restore options such as restore-framework.") 85 boolean mRestoreAll = false; 86 87 @Option(name = "enable-radio-log", 88 description = "Whether to enable radio modem logcat. Device will reboot if enabled. " 89 + "This option requires adb root but will not automatically root adb. " 90 + "Setting this option false does not disable already enabled radio log.") 91 boolean mEnableRadioLog = false; 92 93 @Option(name = "restore-radio-log", 94 description = "Whether to restore radio modem logcat status after test. " 95 + "This option requires adb root but will not automatically root adb. " 96 + "If both enable-radio-log and this option is set to true, " 97 + "device will reboot again at the end of test.") 98 boolean mRestoreRadioLog = false; 99 100 public static long DEVICE_BOOT_TIMEOUT = 3 * 60 * 1000; 101 static final String SYSPROP_DEV_BOOTCOMPLETE = "dev.bootcomplete"; 102 static final String SYSPROP_SYS_BOOT_COMPLETED = "sys.boot_completed"; 103 public static String SYSPROP_RADIO_LOG = "persist.vendor.radio.adb_log_on"; 104 public static String SYSPROP_RADIO_LOG_OLD = "persist.radio.adb_log_on"; 105 public String mSyspropRadioLog = SYSPROP_RADIO_LOG; 106 107 // The name of a system property which tells whether to stop properly configured 108 // native servers where properly configured means a server's init.rc is 109 // configured to stop when that property's value is 1. 110 static final String SYSPROP_VTS_NATIVE_SERVER = "vts.native_server.on"; 111 112 boolean mInitialFrameworkStarted = true; 113 boolean mInitialAdbRoot = false; 114 DeviceOptionState mInitialRadioLog = DeviceOptionState.UNKNOWN; 115 ITestDevice mDevice = null; 116 117 // Whether to reboot device during setUp 118 boolean mRebootSetup = false; 119 // Whether to reboot device during tearDown 120 boolean mRebootTearDown = false; 121 122 public enum DeviceOptionState { 123 UNKNOWN, 124 ENABLED, 125 DISABLED, 126 NOT_AVAILABLE; 127 } 128 129 /** {@inheritDoc} */ 130 @Override setUp(ITestDevice device, IBuildInfo buildInfo)131 public void setUp(ITestDevice device, IBuildInfo buildInfo) 132 throws TargetSetupError, BuildError, DeviceNotAvailableException { 133 mDevice = device; 134 135 // The order of the following steps matters. 136 137 adbRootPreRebootSetUp(); 138 radioLogPreRebootSetup(); 139 frameworkPreRebootSetUp(); 140 141 if (mRebootSetup) { 142 device.reboot(); 143 device.waitForBootComplete(DEVICE_BOOT_TIMEOUT); 144 } 145 146 frameworkPostRebootSetUp(); 147 } 148 149 /** {@inheritDoc} */ 150 @Override tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)151 public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e) 152 throws DeviceNotAvailableException { 153 if (e instanceof DeviceNotAvailableException) { 154 CLog.i("Skip tear down due to DeviceNotAvailableException"); 155 return; 156 } 157 // The order of the following steps matters. 158 159 radioLogPreTearDown(); 160 161 if (mRebootTearDown) { 162 device.reboot(); 163 device.waitForBootComplete(DEVICE_BOOT_TIMEOUT); 164 } 165 166 adbRootPostTearDown(); 167 frameworkPostTearDown(); 168 } 169 170 /** 171 * Prepares adb root status. 172 * 173 * @throws DeviceNotAvailableException 174 */ adbRootPreRebootSetUp()175 private void adbRootPreRebootSetUp() throws DeviceNotAvailableException { 176 if (mRestoreAll || mRestoreAdbRoot) { 177 // mInitialAdbRoot is only needed at tearDown because AndroidDeviceController 178 // already checks initial adb root status 179 mInitialAdbRoot = mDevice.isAdbRoot(); 180 } 181 182 if (mEnableAdbRoot && !mDisableAdbRoot) { 183 adbRoot(); 184 } else if (mDisableAdbRoot && !mEnableAdbRoot) { 185 adbUnroot(); 186 } 187 } 188 189 /** 190 * Restores adb root status 191 * 192 * @throws DeviceNotAvailableException 193 * 194 */ adbRootPostTearDown()195 private void adbRootPostTearDown() throws DeviceNotAvailableException { 196 if (!mRestoreAll && !mRestoreAdbRoot) { 197 return; 198 } 199 200 if (mInitialAdbRoot) { 201 adbRoot(); 202 } else { 203 adbUnroot(); 204 } 205 } 206 207 /** 208 * Collect framework on/off status if restore option is enabled. 209 * @throws DeviceNotAvailableException 210 */ frameworkPreRebootSetUp()211 private void frameworkPreRebootSetUp() throws DeviceNotAvailableException { 212 if (mRestoreAll || mRestoreFramework) { 213 // mInitialFrameworkStarted is only needed at tearDown because AndroidDeviceController 214 // already checks initial framework status 215 mInitialFrameworkStarted = isFrameworkRunning(); 216 } 217 } 218 219 /** 220 * Prepares device framework start/stop status 221 * 222 * @throws DeviceNotAvailableException 223 */ frameworkPostRebootSetUp()224 private void frameworkPostRebootSetUp() throws DeviceNotAvailableException { 225 if (mStartFramework && !mStopFramework) { 226 startFramework(); 227 } else if (mStopFramework && !mStartFramework) { 228 stopFramework(); 229 } 230 } 231 232 /** 233 * Restores the framework start/stop status 234 * 235 * @throws DeviceNotAvailableException 236 */ frameworkPostTearDown()237 private void frameworkPostTearDown() throws DeviceNotAvailableException { 238 if (!mRestoreAll && !mRestoreFramework) { 239 return; 240 } 241 242 boolean current = isFrameworkRunning(); 243 244 if (mInitialFrameworkStarted && !current) { 245 startFramework(); 246 } else if (!mInitialFrameworkStarted && current) { 247 stopFramework(); 248 } 249 } 250 251 /** 252 * @throws DeviceNotAvailableException 253 * 254 */ radioLogPreRebootSetup()255 private void radioLogPreRebootSetup() throws DeviceNotAvailableException { 256 if (mEnableRadioLog || mRestoreAll || mRestoreRadioLog) { 257 mInitialRadioLog = radioLogGetState(); 258 259 if (mInitialRadioLog == DeviceOptionState.NOT_AVAILABLE) { 260 CLog.d("Radio modem log configured but the setting is not available " 261 + " on device. Skipping."); 262 return; 263 } 264 } 265 266 if (mEnableRadioLog && mInitialRadioLog == DeviceOptionState.DISABLED) { 267 this.setProperty(SYSPROP_RADIO_LOG, "1"); 268 CLog.d("Turing on radio modem log."); 269 mRebootSetup = true; 270 } 271 } 272 273 /** 274 * @throws DeviceNotAvailableException 275 * 276 */ radioLogPreTearDown()277 private void radioLogPreTearDown() throws DeviceNotAvailableException { 278 if (mInitialRadioLog == DeviceOptionState.NOT_AVAILABLE) { 279 return; 280 } 281 282 if (!mRestoreAll && !mRestoreRadioLog) { 283 return; 284 } 285 286 DeviceOptionState current = radioLogGetState(); 287 288 if (mInitialRadioLog == DeviceOptionState.DISABLED 289 && current == DeviceOptionState.ENABLED) { 290 CLog.d("Turing off radio modem log."); 291 this.setProperty(SYSPROP_RADIO_LOG, "0"); 292 } else if (mInitialRadioLog == DeviceOptionState.ENABLED 293 && current == DeviceOptionState.DISABLED) { 294 CLog.d("Turing on radio modem log."); 295 this.setProperty(SYSPROP_RADIO_LOG, "1"); 296 } else { 297 return; 298 } 299 300 mRebootTearDown = true; 301 } 302 303 /** 304 * Returns the state of radio modem log on/off state. 305 * @return DeviceOptionState specifying the state. 306 * @throws DeviceNotAvailableException 307 */ radioLogGetState()308 private DeviceOptionState radioLogGetState() throws DeviceNotAvailableException { 309 String radioProp = mDevice.getProperty(mSyspropRadioLog); 310 311 if (radioProp == null && mSyspropRadioLog != SYSPROP_RADIO_LOG_OLD) { 312 mSyspropRadioLog = SYSPROP_RADIO_LOG_OLD; 313 radioProp = mDevice.getProperty(mSyspropRadioLog); 314 } 315 316 if (radioProp == null) { 317 return DeviceOptionState.NOT_AVAILABLE; 318 } 319 320 switch (radioProp) { 321 case "1": 322 return DeviceOptionState.ENABLED; 323 case "0": 324 return DeviceOptionState.DISABLED; 325 default: 326 return DeviceOptionState.NOT_AVAILABLE; 327 } 328 } 329 330 // ----------------------- Below are device util methods ----------------------- 331 332 /** 333 * Executes command "adb root" if adb is not running as root. 334 * 335 * @throws DeviceNotAvailableException 336 */ adbRoot()337 void adbRoot() throws DeviceNotAvailableException { 338 if (!mDevice.isAdbRoot()) { 339 mDevice.executeAdbCommand("root"); 340 } 341 } 342 343 /** 344 * Executes command "adb unroot" if adb is running as root. 345 * 346 * @throws DeviceNotAvailableException 347 */ adbUnroot()348 void adbUnroot() throws DeviceNotAvailableException { 349 if (mDevice.isAdbRoot()) { 350 mDevice.executeAdbCommand("unroot"); 351 } 352 } 353 354 /** 355 * Start Android framework on a device. 356 * 357 * This method also starts VTS native servers which is required to start the framework 358 * This method will block until boot complete or timeout. 359 * A default timeout value will be used 360 * 361 * @throws DeviceNotAvailableException if timeout waiting for boot complete 362 */ startFramework()363 void startFramework() throws DeviceNotAvailableException { 364 startFramework(DEVICE_BOOT_TIMEOUT); 365 } 366 367 /** 368 * Starts Android framework on a device. 369 * 370 * This method also starts VTS native servers which is required to start the framework 371 * This method will block until boot complete or timeout. 372 * 373 * @param timeout timeout in milliseconds. 374 * @throws DeviceNotAvailableException if timeout waiting for boot complete 375 */ startFramework(long timeout)376 void startFramework(long timeout) throws DeviceNotAvailableException { 377 startNativeServers(); 378 mDevice.executeShellCommand("start"); 379 380 waitForFrameworkStartComplete(); 381 } 382 383 /** 384 * Wait for Android framework to complete starting. 385 * 386 * @throws DeviceNotAvailableException timed out 387 */ waitForFrameworkStartComplete()388 void waitForFrameworkStartComplete() throws DeviceNotAvailableException { 389 waitForFrameworkStartComplete(DEVICE_BOOT_TIMEOUT); 390 } 391 392 /** 393 * Wait for Android framework to complete starting. 394 * 395 * @param timeout 396 * @throws DeviceNotAvailableException 397 */ waitForFrameworkStartComplete(long timeout)398 void waitForFrameworkStartComplete(long timeout) throws DeviceNotAvailableException { 399 long start = System.currentTimeMillis(); 400 401 // First, wait for boot completion 402 mDevice.waitForBootComplete(timeout); 403 404 while (!isFrameworkRunning()) { 405 if (System.currentTimeMillis() - start >= timeout) { 406 throw new DeviceNotAvailableException( 407 "Timed out waiting for framework start complete.", 408 mDevice.getSerialNumber()); 409 } 410 411 try { 412 Thread.sleep(1000); 413 } catch (InterruptedException e) { 414 e.printStackTrace(); 415 throw new DeviceNotAvailableException( 416 "Intrupted while waiting for framework start complete.", 417 mDevice.getSerialNumber()); 418 } 419 } 420 } 421 422 /** 423 * Stops Android framework on a device. 424 * 425 * @throws DeviceNotAvailableException 426 */ stopFramework()427 void stopFramework() throws DeviceNotAvailableException { 428 mDevice.executeShellCommand("stop"); 429 this.setProperty(SYSPROP_SYS_BOOT_COMPLETED, "0"); 430 } 431 432 /** 433 * Restarts Android framework on a device. 434 * 435 * This method will block until start finish or timeout. 436 * 437 * @throws DeviceNotAvailableException 438 */ restartFramework()439 void restartFramework() throws DeviceNotAvailableException { 440 stopFramework(); 441 startFramework(); 442 } 443 444 /** 445 * Starts all native servers. 446 * 447 * @throws DeviceNotAvailableException 448 */ startNativeServers()449 void startNativeServers() throws DeviceNotAvailableException { 450 this.setProperty(SYSPROP_VTS_NATIVE_SERVER, "0"); 451 } 452 453 /** 454 * Stops all native servers. 455 * 456 * @throws DeviceNotAvailableException 457 */ stopNativeServers()458 void stopNativeServers() throws DeviceNotAvailableException { 459 this.setProperty(SYSPROP_VTS_NATIVE_SERVER, "1"); 460 } 461 462 /** 463 * Sets a sysprop on the device. 464 * 465 * TODO: to be removed once the API is added to TF 466 * 467 * @param key the key of a sysprop. 468 * @param value the value of a sysprop. 469 * @throws DeviceNotAvailableException 470 */ setProperty(String key, String value)471 void setProperty(String key, String value) throws DeviceNotAvailableException { 472 // TODO: check success 473 mDevice.executeShellCommand(String.format("setprop %s %s", key, value)); 474 } 475 isBootCompleted()476 boolean isBootCompleted() throws DeviceNotAvailableException { 477 String sysBootCompleted = mDevice.getProperty(SYSPROP_SYS_BOOT_COMPLETED); 478 String devBootCompleted = mDevice.getProperty(SYSPROP_DEV_BOOTCOMPLETE); 479 return sysBootCompleted != null && sysBootCompleted.equals("1") && devBootCompleted != null 480 && devBootCompleted.equals("1"); 481 } 482 483 /** 484 * Checks whether Android framework is started. 485 * 486 * @return True if started, False otherwise. 487 * @throws DeviceNotAvailableException 488 */ isFrameworkRunning()489 boolean isFrameworkRunning() throws DeviceNotAvailableException { 490 // First, check whether boot has completed. 491 if (!isBootCompleted()) { 492 return false; 493 } 494 495 String cmd = "ps -g system | grep system_server"; 496 return mDevice.executeShellCommand(cmd).contains("system_server"); 497 } 498 } 499