1 /* 2 * Copyright (C) 2010 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.build.IDeviceBuildInfo; 21 import com.android.tradefed.config.GlobalConfiguration; 22 import com.android.tradefed.config.Option; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.DeviceUnresponsiveException; 25 import com.android.tradefed.device.IDeviceManager; 26 import com.android.tradefed.device.ITestDevice; 27 import com.android.tradefed.device.ITestDevice.RecoveryMode; 28 import com.android.tradefed.host.IHostOptions; 29 import com.android.tradefed.invoker.TestInformation; 30 import com.android.tradefed.log.LogUtil.CLog; 31 import com.android.tradefed.result.error.DeviceErrorIdentifier; 32 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption; 33 import com.android.tradefed.util.CommandStatus; 34 import com.android.tradefed.util.IRunUtil; 35 import com.android.tradefed.util.RunUtil; 36 37 import com.google.common.annotations.VisibleForTesting; 38 39 import java.util.ArrayList; 40 import java.util.Collection; 41 import java.util.concurrent.TimeUnit; 42 43 /** A {@link ITargetPreparer} that flashes an image on physical Android hardware. */ 44 public abstract class DeviceFlashPreparer extends BaseTargetPreparer { 45 46 /** 47 * Enum of options for handling the encryption of userdata image 48 */ 49 public static enum EncryptionOptions {ENCRYPT, IGNORE} 50 51 private static final int BOOT_POLL_TIME_MS = 5 * 1000; 52 53 @Option( 54 name = "device-boot-time", 55 description = "max time to wait for device to boot.", 56 isTimeVal = true 57 ) 58 private long mDeviceBootTime = 5 * 60 * 1000; 59 60 @Option(name = "userdata-flash", description = 61 "specify handling of userdata partition.") 62 private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH; 63 64 @Option(name = "encrypt-userdata", description = "specify if userdata partition should be " 65 + "encrypted; defaults to IGNORE, where no actions will be taken.") 66 private EncryptionOptions mEncryptUserData = EncryptionOptions.IGNORE; 67 68 @Option(name = "force-system-flash", description = 69 "specify if system should always be flashed even if already running desired build.") 70 private boolean mForceSystemFlash = false; 71 72 /* 73 * A temporary workaround for special builds. Should be removed after changes from build team. 74 * Bug: 18078421 75 */ 76 @Option(name = "skip-post-flash-flavor-check", description = 77 "specify if system flavor should not be checked after flash") 78 private boolean mSkipPostFlashFlavorCheck = false; 79 80 /* 81 * Used for update testing 82 */ 83 @Option(name = "skip-post-flash-build-id-check", description = 84 "specify if build ID should not be checked after flash") 85 private boolean mSkipPostFlashBuildIdCheck = false; 86 87 @Option(name = "wipe-skip-list", description = 88 "list of /data subdirectories to NOT wipe when doing UserDataFlashOption.TESTS_ZIP") 89 private Collection<String> mDataWipeSkipList = new ArrayList<>(); 90 91 /** 92 * @deprecated use host-options:concurrent-flasher-limit. 93 */ 94 @Deprecated 95 @Option(name = "concurrent-flasher-limit", description = 96 "No-op, do not use. Left for backwards compatibility.") 97 private Integer mConcurrentFlasherLimit = null; 98 99 @Option(name = "skip-post-flashing-setup", 100 description = "whether or not to skip post-flashing setup steps") 101 private boolean mSkipPostFlashingSetup = false; 102 103 @Option(name = "wipe-timeout", 104 description = "the timeout for the command of wiping user data.", isTimeVal = true) 105 private long mWipeTimeout = 4 * 60 * 1000; 106 107 @Option( 108 name = "fastboot-flash-option", 109 description = "additional options to pass with fastboot flash/update command." 110 ) 111 private Collection<String> mFastbootFlashOptions = new ArrayList<>(); 112 113 @Option( 114 name = "flash-ramdisk", 115 description = 116 "flashes ramdisk (boot partition) in addition " + "to regular system image") 117 private boolean mShouldFlashRamdisk = false; 118 119 /** 120 * Sets the device boot time 121 * <p/> 122 * Exposed for unit testing 123 */ setDeviceBootTime(long bootTime)124 void setDeviceBootTime(long bootTime) { 125 mDeviceBootTime = bootTime; 126 } 127 128 /** Gets the device boot wait time */ getDeviceBootWaitTime()129 protected long getDeviceBootWaitTime() { 130 return mDeviceBootTime; 131 } 132 133 /** 134 * Gets the interval between device boot poll attempts. 135 * <p/> 136 * Exposed for unit testing 137 */ getDeviceBootPollTimeMs()138 int getDeviceBootPollTimeMs() { 139 return BOOT_POLL_TIME_MS; 140 } 141 142 /** 143 * Gets the {@link IRunUtil} instance to use. 144 * <p/> 145 * Exposed for unit testing 146 */ getRunUtil()147 IRunUtil getRunUtil() { 148 return RunUtil.getDefault(); 149 } 150 151 /** 152 * Getg a reference to the {@link IDeviceManager} 153 * 154 * Exposed for unit testing 155 * 156 * @return the {@link IDeviceManager} to use 157 */ getDeviceManager()158 IDeviceManager getDeviceManager() { 159 return GlobalConfiguration.getDeviceManagerInstance(); 160 } 161 162 /** 163 * Gets the {@link IHostOptions} instance to use. 164 * <p/> 165 * Exposed for unit testing 166 */ getHostOptions()167 protected IHostOptions getHostOptions() { 168 return GlobalConfiguration.getInstance().getHostOptions(); 169 } 170 171 /** 172 * Set the userdata-flash option 173 * 174 * @param flashOption 175 */ setUserDataFlashOption(UserDataFlashOption flashOption)176 public void setUserDataFlashOption(UserDataFlashOption flashOption) { 177 mUserDataFlashOption = flashOption; 178 } 179 180 /** {@inheritDoc} */ 181 @Override setUp(TestInformation testInfo)182 public void setUp(TestInformation testInfo) 183 throws TargetSetupError, DeviceNotAvailableException, BuildError { 184 if (isDisabled()) { 185 CLog.i("Skipping device flashing."); 186 return; 187 } 188 ITestDevice device = testInfo.getDevice(); 189 IBuildInfo buildInfo = testInfo.getBuildInfo(); 190 CLog.i("Performing setup on %s", device.getSerialNumber()); 191 if (!(buildInfo instanceof IDeviceBuildInfo)) { 192 throw new IllegalArgumentException("Provided buildInfo is not a IDeviceBuildInfo"); 193 } 194 IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo) buildInfo; 195 if (mShouldFlashRamdisk && deviceBuild.getRamdiskFile() == null) { 196 throw new IllegalArgumentException( 197 "ramdisk flashing enabled but no ramdisk file was found in build info"); 198 } 199 // don't allow interruptions during flashing operations. 200 getRunUtil().allowInterrupt(false); 201 IDeviceManager deviceManager = getDeviceManager(); 202 long queueTime = -1; 203 long flashingTime = -1; 204 long start = -1; 205 try { 206 checkDeviceProductType(device, deviceBuild); 207 device.setRecoveryMode(RecoveryMode.ONLINE); 208 IDeviceFlasher flasher = createFlasher(device); 209 flasher.setWipeTimeout(mWipeTimeout); 210 // only surround fastboot related operations with flashing permit restriction 211 try { 212 start = System.currentTimeMillis(); 213 deviceManager.takeFlashingPermit(); 214 queueTime = System.currentTimeMillis() - start; 215 CLog.v( 216 "Flashing permit obtained after %ds", 217 TimeUnit.MILLISECONDS.toSeconds(queueTime)); 218 219 flasher.overrideDeviceOptions(device); 220 flasher.setUserDataFlashOption(mUserDataFlashOption); 221 flasher.setForceSystemFlash(mForceSystemFlash); 222 flasher.setDataWipeSkipList(mDataWipeSkipList); 223 flasher.setShouldFlashRamdisk(mShouldFlashRamdisk); 224 if (flasher instanceof FastbootDeviceFlasher) { 225 ((FastbootDeviceFlasher) flasher).setFlashOptions(mFastbootFlashOptions); 226 } 227 preEncryptDevice(device, flasher); 228 start = System.currentTimeMillis(); 229 flasher.flash(device, deviceBuild); 230 } finally { 231 flashingTime = System.currentTimeMillis() - start; 232 deviceManager.returnFlashingPermit(); 233 // report flashing status 234 CommandStatus status = flasher.getSystemFlashingStatus(); 235 if (status == null) { 236 CLog.i("Skipped reporting metrics because system partitions were not flashed."); 237 } else { 238 reportFlashMetrics(buildInfo.getBuildBranch(), buildInfo.getBuildFlavor(), 239 buildInfo.getBuildId(), device.getSerialNumber(), queueTime, 240 flashingTime, status); 241 } 242 } 243 // only want logcat captured for current build, delete any accumulated log data 244 device.clearLogcat(); 245 if (mSkipPostFlashingSetup) { 246 return; 247 } 248 // Temporary re-enable interruptable since the critical flashing operation is over. 249 getRunUtil().allowInterrupt(true); 250 device.waitForDeviceOnline(); 251 // device may lose date setting if wiped, update with host side date in case anything on 252 // device side malfunction with an invalid date 253 if (device.enableAdbRoot()) { 254 device.setDate(null); 255 } 256 // Disable interrupt for encryption operation. 257 getRunUtil().allowInterrupt(false); 258 checkBuild(device, deviceBuild); 259 postEncryptDevice(device, flasher); 260 // Once critical operation is done, we re-enable interruptable 261 getRunUtil().allowInterrupt(true); 262 try { 263 device.setRecoveryMode(RecoveryMode.AVAILABLE); 264 device.waitForDeviceAvailable(mDeviceBootTime); 265 } catch (DeviceUnresponsiveException e) { 266 // assume this is a build problem 267 throw new DeviceFailedToBootError( 268 String.format( 269 "Device %s did not become available after flashing %s", 270 device.getSerialNumber(), deviceBuild.getDeviceBuildId()), 271 device.getDeviceDescriptor(), 272 DeviceErrorIdentifier.ERROR_AFTER_FLASHING); 273 } 274 device.postBootSetup(); 275 } finally { 276 // Allow interruption at the end no matter what. 277 getRunUtil().allowInterrupt(true); 278 } 279 } 280 281 /** 282 * Possible check before flashing to ensure the device is as expected compare to the build info. 283 * 284 * @param device the {@link ITestDevice} to flash. 285 * @param deviceBuild the {@link IDeviceBuildInfo} used to flash. 286 * @throws BuildError 287 * @throws DeviceNotAvailableException 288 */ checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)289 protected void checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild) 290 throws BuildError, DeviceNotAvailableException { 291 // empty of purpose 292 } 293 294 /** 295 * Verifies the expected build matches the actual build on device after flashing 296 * @throws DeviceNotAvailableException 297 */ checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)298 private void checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild) 299 throws DeviceNotAvailableException { 300 // Need to use deviceBuild.getDeviceBuildId instead of getBuildId because the build info 301 // could be an AppBuildInfo and return app build id. Need to be more explicit that we 302 // check for the device build here. 303 if (!mSkipPostFlashBuildIdCheck) { 304 checkBuildAttribute(deviceBuild.getDeviceBuildId(), device.getBuildId(), 305 device.getSerialNumber()); 306 } 307 if (!mSkipPostFlashFlavorCheck) { 308 checkBuildAttribute(deviceBuild.getDeviceBuildFlavor(), device.getBuildFlavor(), 309 device.getSerialNumber()); 310 } 311 // TODO: check bootloader and baseband versions too 312 } 313 checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, String serial)314 private void checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, 315 String serial) throws DeviceNotAvailableException { 316 if (expectedBuildAttr == null || actualBuildAttr == null || 317 !expectedBuildAttr.equals(actualBuildAttr)) { 318 // throw DNAE - assume device hardware problem - we think flash was successful but 319 // device is not running right bits 320 throw new DeviceNotAvailableException( 321 String.format( 322 "Unexpected build after flashing. Expected %s, actual %s", 323 expectedBuildAttr, actualBuildAttr), 324 serial, 325 DeviceErrorIdentifier.ERROR_AFTER_FLASHING); 326 } 327 } 328 329 /** 330 * Create {@link IDeviceFlasher} to use. Subclasses can override 331 * @throws DeviceNotAvailableException 332 */ createFlasher(ITestDevice device)333 protected abstract IDeviceFlasher createFlasher(ITestDevice device) 334 throws DeviceNotAvailableException; 335 336 /** 337 * Handle encrypting of the device pre-flash. 338 * 339 * @see #postEncryptDevice(ITestDevice, IDeviceFlasher) 340 * @param device 341 * @throws DeviceNotAvailableException 342 * @throws TargetSetupError if the device could not be encrypted or unlocked. 343 */ preEncryptDevice(ITestDevice device, IDeviceFlasher flasher)344 private void preEncryptDevice(ITestDevice device, IDeviceFlasher flasher) 345 throws DeviceNotAvailableException, TargetSetupError { 346 switch (mEncryptUserData) { 347 case IGNORE: 348 return; 349 case ENCRYPT: 350 if (!device.isEncryptionSupported()) { 351 throw new TargetSetupError("Encryption is not supported", 352 device.getDeviceDescriptor()); 353 } 354 if (!device.isDeviceEncrypted()) { 355 switch(flasher.getUserDataFlashOption()) { 356 case TESTS_ZIP: // Intentional fall through. 357 case WIPE_RM: 358 // a new filesystem will not be created by the flasher, but the userdata 359 // partition is expected to be cleared anyway, so we encrypt the device 360 // with wipe 361 if (!device.encryptDevice(false)) { 362 throw new TargetSetupError("Failed to encrypt device", 363 device.getDeviceDescriptor()); 364 } 365 if (!device.unlockDevice()) { 366 throw new TargetSetupError("Failed to unlock device", 367 device.getDeviceDescriptor()); 368 } 369 break; 370 case RETAIN: 371 // original filesystem must be retained, so we encrypt in place 372 if (!device.encryptDevice(true)) { 373 throw new TargetSetupError("Failed to encrypt device", 374 device.getDeviceDescriptor()); 375 } 376 if (!device.unlockDevice()) { 377 throw new TargetSetupError("Failed to unlock device", 378 device.getDeviceDescriptor()); 379 } 380 break; 381 default: 382 // Do nothing, userdata will be encrypted post-flash. 383 } 384 } 385 break; 386 default: 387 // should not get here 388 return; 389 } 390 } 391 392 /** 393 * Handle encrypting of the device post-flash. 394 * <p> 395 * This method handles encrypting the device after a flash in cases where a flash would undo any 396 * encryption pre-flash, such as when the device is flashed or wiped. 397 * </p> 398 * 399 * @see #preEncryptDevice(ITestDevice, IDeviceFlasher) 400 * @param device 401 * @throws DeviceNotAvailableException 402 * @throws TargetSetupError If the device could not be encrypted or unlocked. 403 */ postEncryptDevice(ITestDevice device, IDeviceFlasher flasher)404 private void postEncryptDevice(ITestDevice device, IDeviceFlasher flasher) 405 throws DeviceNotAvailableException, TargetSetupError { 406 switch (mEncryptUserData) { 407 case IGNORE: 408 return; 409 case ENCRYPT: 410 if (!device.isEncryptionSupported()) { 411 throw new TargetSetupError("Encryption is not supported", 412 device.getDeviceDescriptor()); 413 } 414 switch(flasher.getUserDataFlashOption()) { 415 case FLASH: 416 if (!device.encryptDevice(true)) { 417 throw new TargetSetupError("Failed to encrypt device", 418 device.getDeviceDescriptor()); 419 } 420 break; 421 case WIPE: // Intentional fall through. 422 case FORCE_WIPE: 423 // since the device was just wiped, encrypt with wipe 424 if (!device.encryptDevice(false)) { 425 throw new TargetSetupError("Failed to encrypt device", 426 device.getDeviceDescriptor()); 427 } 428 break; 429 default: 430 // Do nothing, userdata was encrypted pre-flash. 431 } 432 if (!device.unlockDevice()) { 433 throw new TargetSetupError("Failed to unlock device", 434 device.getDeviceDescriptor()); 435 } 436 break; 437 default: 438 // should not get here 439 return; 440 } 441 } 442 443 @Override tearDown(TestInformation testInfo, Throwable e)444 public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException { 445 if (isDisabled()) { 446 CLog.i("Skipping device flashing tearDown."); 447 return; 448 } 449 ITestDevice device = testInfo.getDevice(); 450 if (mEncryptUserData == EncryptionOptions.ENCRYPT 451 && mUserDataFlashOption != UserDataFlashOption.RETAIN) { 452 if (e instanceof DeviceNotAvailableException) { 453 CLog.e("Device was encrypted but now unavailable. may need manual cleanup"); 454 } else if (device.isDeviceEncrypted()) { 455 if (!device.unencryptDevice()) { 456 throw new RuntimeException("Failed to unencrypt device"); 457 } 458 } 459 } 460 } 461 462 /** 463 * Reports device flashing timing data to metrics backend 464 * @param branch the branch where the device build originated from 465 * @param buildFlavor the build flavor of the device build 466 * @param buildId the build number of the device build 467 * @param serial the serial number of device 468 * @param queueTime the time spent waiting for a flashing limit to become available 469 * @param flashingTime the time spent in flashing device image zip 470 * @param flashingStatus the execution status of flashing command 471 */ reportFlashMetrics(String branch, String buildFlavor, String buildId, String serial, long queueTime, long flashingTime, CommandStatus flashingStatus)472 protected void reportFlashMetrics(String branch, String buildFlavor, String buildId, 473 String serial, long queueTime, long flashingTime, CommandStatus flashingStatus) { 474 // no-op as default implementation 475 } 476 477 /** 478 * Sets the option for whether ramdisk should be flashed 479 * 480 * @param shouldFlashRamdisk 481 */ 482 @VisibleForTesting setShouldFlashRamdisk(boolean shouldFlashRamdisk)483 void setShouldFlashRamdisk(boolean shouldFlashRamdisk) { 484 mShouldFlashRamdisk = shouldFlashRamdisk; 485 } 486 setSkipPostFlashFlavorCheck(boolean skipPostFlashFlavorCheck)487 protected void setSkipPostFlashFlavorCheck(boolean skipPostFlashFlavorCheck) { 488 mSkipPostFlashFlavorCheck = skipPostFlashFlavorCheck; 489 } 490 setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck)491 protected void setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck) { 492 mSkipPostFlashBuildIdCheck = skipPostFlashBuildIdCheck; 493 } 494 } 495