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.annotations.VisibleForTesting; 20 import com.android.tradefed.build.IDeviceBuildInfo; 21 import com.android.tradefed.command.remote.DeviceDescriptor; 22 import com.android.tradefed.config.GlobalConfiguration; 23 import com.android.tradefed.device.DeviceNotAvailableException; 24 import com.android.tradefed.device.IManagedTestDevice; 25 import com.android.tradefed.device.ITestDevice; 26 import com.android.tradefed.device.TestDeviceState; 27 import com.android.tradefed.error.HarnessRuntimeException; 28 import com.android.tradefed.host.IHostOptions; 29 import com.android.tradefed.host.IHostOptions.PermitLimitType; 30 import com.android.tradefed.invoker.logger.InvocationMetricLogger; 31 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey; 32 import com.android.tradefed.invoker.tracing.CloseableTraceScope; 33 import com.android.tradefed.log.ITestLogger; 34 import com.android.tradefed.log.LogUtil.CLog; 35 import com.android.tradefed.result.InputStreamSource; 36 import com.android.tradefed.result.LogDataType; 37 import com.android.tradefed.result.error.DeviceErrorIdentifier; 38 import com.android.tradefed.result.error.ErrorIdentifier; 39 import com.android.tradefed.result.error.InfraErrorIdentifier; 40 import com.android.tradefed.util.CommandResult; 41 import com.android.tradefed.util.CommandStatus; 42 import com.android.tradefed.util.FileUtil; 43 import com.android.tradefed.util.FuseUtil; 44 import com.android.tradefed.util.IRunUtil; 45 import com.android.tradefed.util.RunUtil; 46 import com.android.tradefed.util.ZipUtil2; 47 import com.android.tradefed.util.image.DeviceImageTracker; 48 import com.android.tradefed.util.image.DeviceImageTracker.FileCacheTracker; 49 import com.android.tradefed.util.image.IncrementalImageUtil; 50 51 import com.google.common.collect.ImmutableSet; 52 53 import org.apache.commons.compress.archivers.zip.ZipFile; 54 55 import java.io.File; 56 import java.io.IOException; 57 import java.util.ArrayList; 58 import java.util.Arrays; 59 import java.util.Collection; 60 import java.util.HashMap; 61 import java.util.List; 62 import java.util.Map; 63 import java.util.Random; 64 import java.util.concurrent.TimeUnit; 65 import java.util.regex.Matcher; 66 import java.util.regex.Pattern; 67 import java.util.stream.Collectors; 68 69 /** A class that relies on fastboot to flash an image on physical Android hardware. */ 70 public class FastbootDeviceFlasher implements IDeviceFlasher { 71 public static final String BASEBAND_IMAGE_NAME = "radio"; 72 73 private static final String FASTBOOT_VERSION = "fastboot_version"; 74 private static final int MAX_RETRY_ATTEMPTS = 3; 75 private static final int RETRY_SLEEP = 2 * 1000; // 2s sleep between retries 76 77 private static final String SLOT_PROP = "ro.boot.slot_suffix"; 78 private static final String SLOT_VAR = "current-slot"; 79 private static final String SKIP_REBOOT_PARAM = "--skip-reboot"; 80 private static final ImmutableSet<String> DISK_SPACE_ERRORS = 81 ImmutableSet.of("No space left on device", "failed to create temporary file"); 82 83 private long mWipeTimeout = 4 * 60 * 1000; 84 85 private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH; 86 87 private IFlashingResourcesRetriever mResourceRetriever; 88 89 private ITestsZipInstaller mTestsZipInstaller = null; 90 91 private Collection<String> mFlashOptions = new ArrayList<>(); 92 93 private Collection<String> mDataWipeSkipList = null; 94 95 private boolean mForceSystemFlash; 96 97 private CommandStatus mFbCmdStatus; 98 99 private CommandStatus mSystemFlashStatus; 100 101 private boolean mShouldFlashRamdisk = false; 102 103 private String mRamdiskPartition = "root"; 104 105 private String mSystemBuildId = null; 106 private String mSystemBuildFlavor = null; 107 108 private IncrementalImageUtil mIncrementalFlashing = null; 109 private ITestLogger mTestLogger = null; 110 111 @VisibleForTesting getFuseUtil()112 protected FuseUtil getFuseUtil() { 113 return new FuseUtil(); 114 } 115 116 /** 117 * {@inheritDoc} 118 */ 119 @Override setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever)120 public void setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever) { 121 mResourceRetriever = retriever; 122 } 123 getFlashingResourcesRetriever()124 protected IFlashingResourcesRetriever getFlashingResourcesRetriever() { 125 return mResourceRetriever; 126 } 127 setTestLogger(ITestLogger logger)128 void setTestLogger(ITestLogger logger) { 129 mTestLogger = logger; 130 } 131 132 /** 133 * {@inheritDoc} 134 */ 135 @Override setUserDataFlashOption(UserDataFlashOption flashOption)136 public void setUserDataFlashOption(UserDataFlashOption flashOption) { 137 mUserDataFlashOption = flashOption; 138 } 139 140 /** 141 * {@inheritDoc} 142 */ 143 @Override getUserDataFlashOption()144 public UserDataFlashOption getUserDataFlashOption() { 145 return mUserDataFlashOption; 146 } 147 setTestsZipInstaller(ITestsZipInstaller testsZipInstaller)148 void setTestsZipInstaller(ITestsZipInstaller testsZipInstaller) { 149 mTestsZipInstaller = testsZipInstaller; 150 } 151 getTestsZipInstaller()152 ITestsZipInstaller getTestsZipInstaller() { 153 // Lazily initialize the TestZipInstaller. 154 if (mTestsZipInstaller == null) { 155 if (mDataWipeSkipList == null) { 156 mDataWipeSkipList = new ArrayList<String>(); 157 } 158 if (mDataWipeSkipList.isEmpty()) { 159 // To maintain backwards compatibility. Keep media by default. 160 // TODO: deprecate and remove this. 161 mDataWipeSkipList.add("media"); 162 } 163 mTestsZipInstaller = new DefaultTestsZipInstaller(mDataWipeSkipList); 164 } 165 return mTestsZipInstaller; 166 } 167 168 /** 169 * Sets a list of options to pass with flash/update commands. 170 * 171 * @param flashOptions 172 */ setFlashOptions(Collection<String> flashOptions)173 public void setFlashOptions(Collection<String> flashOptions) { 174 // HACK: To workaround TF's command line parsing, options starting with a dash 175 // needs to be prepended with a whitespace and trimmed before they are used. 176 mFlashOptions = flashOptions.stream().map(String::trim).collect(Collectors.toList()); 177 } 178 setIncrementalFlashing(IncrementalImageUtil incrementalUtil)179 public void setIncrementalFlashing(IncrementalImageUtil incrementalUtil) { 180 mIncrementalFlashing = incrementalUtil; 181 } 182 183 /** {@inheritDoc} */ 184 @Override preFlashOperations(ITestDevice device, IDeviceBuildInfo deviceBuild)185 public void preFlashOperations(ITestDevice device, IDeviceBuildInfo deviceBuild) 186 throws TargetSetupError, DeviceNotAvailableException { 187 boolean initialStateFastbootD = 188 supportsFlashingInFastbootD() && 189 TestDeviceState.FASTBOOTD.equals(device.getDeviceState()); 190 if (initialStateFastbootD) { 191 CLog.i("Using flashing from fastbootd"); 192 InvocationMetricLogger.addInvocationMetrics( 193 InvocationMetricKey.FLASHING_FROM_FASTBOOTD, 1); 194 } 195 196 CLog.i("Flashing device %s with build %s", device.getSerialNumber(), 197 deviceBuild.getDeviceBuildId()); 198 199 // Get system build id and build flavor before booting into fastboot 200 if (TestDeviceState.ONLINE.equals(device.getDeviceState())) { 201 setSystemBuildInfo(device.getBuildId(), device.getBuildFlavor()); 202 } 203 downloadFlashingResources(device, deviceBuild); 204 if (device instanceof IManagedTestDevice) { 205 String fastbootVersion = ((IManagedTestDevice) device).getFastbootVersion(); 206 if (fastbootVersion != null) { 207 deviceBuild.addBuildAttribute(FASTBOOT_VERSION, fastbootVersion); 208 } 209 } 210 211 if (mIncrementalFlashing != null 212 && mIncrementalFlashing.useUpdatedFlow() 213 && shouldFlashSystem(mSystemBuildId, mSystemBuildFlavor, deviceBuild)) { 214 try { 215 mIncrementalFlashing.updateDeviceWithNewFlow( 216 deviceBuild.getBootloaderImageFile(), deviceBuild.getBasebandImageFile()); 217 } catch (TargetSetupError e) { 218 mIncrementalFlashing = null; 219 // In case of TargetSetupError for incremental flashing, 220 // fallback to full flashing. 221 CLog.e(e); 222 DeviceImageTracker.getDefaultCache().invalidateTracking(device.getSerialNumber()); 223 if (mTestLogger != null) { 224 try (InputStreamSource source = device.getLogcatDump()) { 225 mTestLogger.testLog( 226 "apply-update-logcat-failure", LogDataType.LOGCAT, source); 227 } 228 } 229 if (TestDeviceState.ONLINE.equals(device.getDeviceState())) { 230 device.rebootIntoBootloader(); 231 } 232 } 233 } else { 234 if (!initialStateFastbootD) { 235 device.rebootIntoBootloader(); 236 } 237 } 238 preFlashSetup(device, deviceBuild); 239 } 240 241 /** {@inheritDoc} */ 242 @Override flash(ITestDevice device, IDeviceBuildInfo deviceBuild)243 public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild) 244 throws TargetSetupError, DeviceNotAvailableException { 245 if (mIncrementalFlashing != null && mIncrementalFlashing.updateCompleted()) { 246 return; 247 } 248 handleUserDataFlashing(device, deviceBuild); 249 checkAndFlashBootloader(device, deviceBuild); 250 checkAndFlashBaseband(device, deviceBuild); 251 flashExtraImages(device, deviceBuild); 252 checkAndFlashSystem(device, mSystemBuildId, mSystemBuildFlavor, deviceBuild); 253 } 254 buildFastbootCommand(String action, boolean skipReboot, String... args)255 private String[] buildFastbootCommand(String action, boolean skipReboot, String... args) { 256 List<String> cmdArgs = new ArrayList<>(); 257 if ("flash".equals(action) || "update".equals(action) || "flashall".equals(action)) { 258 if (skipReboot) { 259 // need to skip reboot if flashing root ramdisk, because this will be typically 260 // used together with flashing of user build, and 261 cmdArgs.add(SKIP_REBOOT_PARAM); 262 } 263 cmdArgs.addAll(mFlashOptions); 264 } 265 cmdArgs.add(action); 266 cmdArgs.addAll(Arrays.asList(args)); 267 return cmdArgs.toArray(new String[cmdArgs.size()]); 268 } 269 270 /** 271 * Perform any additional pre-flashing setup required. No-op unless overridden. 272 * 273 * @param device the {@link ITestDevice} to prepare 274 * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files 275 * @throws DeviceNotAvailableException 276 * @throws TargetSetupError 277 */ preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)278 protected void preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild) 279 throws DeviceNotAvailableException, TargetSetupError {} 280 281 /** 282 * Handle flashing of userdata/cache partition 283 * 284 * @param device the {@link ITestDevice} to flash 285 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 286 * @throws DeviceNotAvailableException 287 * @throws TargetSetupError 288 */ handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)289 protected void handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild) 290 throws DeviceNotAvailableException, TargetSetupError { 291 if (UserDataFlashOption.FORCE_WIPE.equals(mUserDataFlashOption) || 292 UserDataFlashOption.WIPE.equals(mUserDataFlashOption)) { 293 CommandResult result = device.executeFastbootCommand(mWipeTimeout, "-w"); 294 handleFastbootResult(device, result, "-w"); 295 } else { 296 flashUserData(device, deviceBuild); 297 wipeCache(device); 298 } 299 } 300 301 /** 302 * Flash an individual partition of a device 303 * 304 * @param device the {@link ITestDevice} to flash 305 * @param imgFile a {@link File} pointing to the image to be flashed 306 * @param partition the name of the partition to be flashed 307 */ flashPartition(ITestDevice device, File imgFile, String partition)308 protected void flashPartition(ITestDevice device, File imgFile, String partition) 309 throws DeviceNotAvailableException, TargetSetupError { 310 CLog.d( 311 "fastboot flash %s %s [size=%d]", 312 partition, imgFile.getAbsolutePath(), imgFile.length()); 313 executeLongFastbootCmd( 314 device, 315 buildFastbootCommand( 316 "flash", mShouldFlashRamdisk, partition, imgFile.getAbsolutePath())); 317 } 318 319 /** 320 * Wipe the specified partition with `fastboot erase <name>` 321 * 322 * @param device the {@link ITestDevice} to operate on 323 * @param partition the name of the partition to be wiped 324 */ wipePartition(ITestDevice device, String partition)325 protected void wipePartition(ITestDevice device, String partition) 326 throws DeviceNotAvailableException, TargetSetupError { 327 String wipeMethod = device.getUseFastbootErase() ? "erase" : "format"; 328 CLog.d("fastboot %s %s", wipeMethod, partition); 329 CommandResult result = device.fastbootWipePartition(partition); 330 handleFastbootResult(device, result, wipeMethod, partition); 331 } 332 333 /** 334 * Checks with the bootloader if the specified partition exists or not 335 * 336 * @param device the {@link ITestDevice} to operate on 337 * @param partition the name of the partition to be checked 338 */ hasPartition(ITestDevice device, String partition)339 protected boolean hasPartition(ITestDevice device, String partition) 340 throws DeviceNotAvailableException { 341 String partitionType = String.format("partition-type:%s", partition); 342 CommandResult result = device.executeFastbootCommand("getvar", partitionType); 343 if (!CommandStatus.SUCCESS.equals(result.getStatus()) 344 || result.getStderr().contains("FAILED")) { 345 return false; 346 } 347 Pattern regex = Pattern.compile(String.format("^%s:\\s*\\S+$", partitionType), 348 Pattern.MULTILINE); 349 return regex.matcher(result.getStderr()).find(); 350 } 351 352 /** 353 * Downloads extra flashing image files needed 354 * 355 * @param device the {@link ITestDevice} to download resources for 356 * @param localBuild the {@link IDeviceBuildInfo} to populate. Assumes device image file is 357 * already set 358 * @throws DeviceNotAvailableException if device is not available 359 * @throws TargetSetupError if failed to retrieve resources 360 */ downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)361 protected void downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild) 362 throws TargetSetupError, DeviceNotAvailableException { 363 IFlashingResourcesParser resourceParser = createFlashingResourcesParser(localBuild, 364 device.getDeviceDescriptor()); 365 366 if (resourceParser.getRequiredBoards() == null) { 367 throw new TargetSetupError(String.format("Build %s is missing required board info.", 368 localBuild.getDeviceBuildId()), device.getDeviceDescriptor()); 369 } 370 String deviceProductType = device.getProductType(); 371 if (deviceProductType == null) { 372 // treat this as a fatal device error 373 throw new DeviceNotAvailableException( 374 String.format( 375 "Could not determine product type for device %s", 376 device.getSerialNumber()), 377 device.getSerialNumber(), 378 DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE); 379 } 380 verifyRequiredBoards(device, resourceParser, deviceProductType); 381 382 String bootloaderVersion = resourceParser.getRequiredBootloaderVersion(); 383 // only set bootloader image if this build doesn't have one already 384 // TODO: move this logic to the BuildProvider step 385 if (bootloaderVersion != null && localBuild.getBootloaderImageFile() == null) { 386 CLog.v("Bootloader image was not included in the build artifacts (%s, %s), " 387 + "fetching from blob service instead.", 388 localBuild.getDeviceBuildId(), localBuild.getDeviceBuildFlavor()); 389 localBuild.setBootloaderImageFile( 390 getFlashingResourcesRetriever() 391 .retrieveFile(getBootloaderFilePrefix(device), bootloaderVersion), 392 bootloaderVersion); 393 } 394 String basebandVersion = resourceParser.getRequiredBasebandVersion(); 395 // only set baseband image if this build doesn't have one already 396 if (basebandVersion != null && localBuild.getBasebandImageFile() == null) { 397 CLog.v("Baseband image was not included in the build artifacts (%s, %s), " 398 + "fetching from blob service instead.", 399 localBuild.getDeviceBuildId(), localBuild.getDeviceBuildFlavor()); 400 localBuild.setBasebandImage(getFlashingResourcesRetriever().retrieveFile( 401 BASEBAND_IMAGE_NAME, basebandVersion), basebandVersion); 402 } 403 downloadExtraImageFiles(resourceParser, getFlashingResourcesRetriever(), localBuild); 404 } 405 406 /** 407 * Verify that the device's product type supports the build-to-be-flashed. 408 * 409 * <p>The base implementation will verify that the deviceProductType is included in the {@link 410 * IFlashingResourcesParser#getRequiredBoards()} collection. Subclasses may override as desired. 411 * 412 * @param device the {@link ITestDevice} to be flashed 413 * @param resourceParser the {@link IFlashingResourcesParser} 414 * @param deviceProductType the <var>device</var>'s product type 415 * @throws TargetSetupError if the build's required board info did not match the device 416 */ verifyRequiredBoards( ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType)417 protected void verifyRequiredBoards( 418 ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType) 419 throws TargetSetupError { 420 if (!containsIgnoreCase(resourceParser.getRequiredBoards(), deviceProductType)) { 421 throw new TargetSetupError( 422 String.format( 423 "Device %s is %s. Expected %s", 424 device.getSerialNumber(), 425 deviceProductType, 426 resourceParser.getRequiredBoards()), 427 device.getDeviceDescriptor(), 428 InfraErrorIdentifier.UNEXPECTED_DEVICE_CONFIGURED); 429 } 430 } 431 containsIgnoreCase(Collection<String> stringList, String anotherString)432 private static boolean containsIgnoreCase(Collection<String> stringList, String anotherString) { 433 for (String aString : stringList) { 434 if (aString != null && aString.equalsIgnoreCase(anotherString)) { 435 return true; 436 } 437 } 438 return false; 439 } 440 441 /** 442 * Hook to allow subclasses to download extra custom image files if needed. 443 * 444 * @param resourceParser the {@link IFlashingResourcesParser} 445 * @param retriever the {@link IFlashingResourcesRetriever} 446 * @param localBuild the {@link IDeviceBuildInfo} 447 * @throws TargetSetupError 448 */ downloadExtraImageFiles(IFlashingResourcesParser resourceParser, IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)449 protected void downloadExtraImageFiles(IFlashingResourcesParser resourceParser, 450 IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild) 451 throws TargetSetupError { 452 } 453 454 /** 455 * Factory method for creating a {@link IFlashingResourcesParser}. 456 * <p/> 457 * Exposed for unit testing. 458 * 459 * @param localBuild the {@link IDeviceBuildInfo} to parse 460 * @param descriptor the descriptor of the device being flashed. 461 * @return a {@link IFlashingResourcesParser} created by the factory method. 462 * @throws TargetSetupError 463 */ createFlashingResourcesParser(IDeviceBuildInfo localBuild, DeviceDescriptor descriptor)464 protected IFlashingResourcesParser createFlashingResourcesParser(IDeviceBuildInfo localBuild, 465 DeviceDescriptor descriptor) throws TargetSetupError { 466 try { 467 return new FlashingResourcesParser(localBuild.getDeviceImageFile()); 468 } catch (TargetSetupError e) { 469 // Rethrow with descriptor since FlashingResourceParser doesn't have it. 470 throw new TargetSetupError(e.getMessage(), e, descriptor); 471 } 472 } 473 474 /** 475 * If needed, flash the bootloader image on device. 476 * 477 * <p>Will only flash bootloader if current version on device != required version. 478 * 479 * @param device the {@link ITestDevice} to flash 480 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the bootloader image to flash 481 * @return <code>true</code> if bootloader was flashed, <code>false</code> if it was skipped 482 * @throws DeviceNotAvailableException if device is not available 483 * @throws TargetSetupError if failed to flash bootloader 484 */ checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)485 protected boolean checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild) 486 throws DeviceNotAvailableException, TargetSetupError { 487 String currentBootloaderVersion = getImageVersion(device, "bootloader"); 488 if (deviceBuild.getBootloaderVersion() != null && 489 !deviceBuild.getBootloaderVersion().equals(currentBootloaderVersion)) { 490 CLog.i("Flashing bootloader %s", deviceBuild.getBootloaderVersion()); 491 flashBootloader(device, deviceBuild.getBootloaderImageFile()); 492 if (mIncrementalFlashing != null) { 493 mIncrementalFlashing.notifyBootloaderNeedsRevert(); 494 } 495 return true; 496 } else { 497 CLog.i("Bootloader is already version %s, skipping flashing", currentBootloaderVersion); 498 return false; 499 } 500 } 501 502 /** 503 * Flashes the given bootloader image and reboots back into bootloader 504 * 505 * @param device the {@link ITestDevice} to flash 506 * @param bootloaderImageFile the bootloader image {@link File} 507 * @throws DeviceNotAvailableException if device is not available 508 * @throws TargetSetupError if failed to flash 509 */ flashBootloader(ITestDevice device, File bootloaderImageFile)510 protected void flashBootloader(ITestDevice device, File bootloaderImageFile) 511 throws DeviceNotAvailableException, TargetSetupError { 512 // bootloader images are small, and flash quickly. so use the 'normal' timeout 513 executeFastbootCmd( 514 device, 515 buildFastbootCommand( 516 "flash", 517 mShouldFlashRamdisk, 518 getBootPartitionName(), 519 bootloaderImageFile.getAbsolutePath())); 520 device.rebootIntoBootloader(); 521 } 522 523 /** 524 * Get the boot partition name for this device flasher. 525 * 526 * <p>Defaults to 'bootloader'. Subclasses should override if necessary. 527 */ getBootPartitionName()528 protected String getBootPartitionName() { 529 return "bootloader"; 530 } 531 532 /** 533 * Get the bootloader file prefix. 534 * <p/> 535 * Defaults to {@link #getBootPartitionName()}. Subclasses should override if necessary. 536 * 537 * @param device the {@link ITestDevice} to flash 538 * @throws DeviceNotAvailableException if device is not available 539 * @throws TargetSetupError if failed to get prefix 540 */ getBootloaderFilePrefix(ITestDevice device)541 protected String getBootloaderFilePrefix(ITestDevice device) throws TargetSetupError, 542 DeviceNotAvailableException { 543 return getBootPartitionName(); 544 } 545 546 /** 547 * If needed, flash the baseband image on device. Will only flash baseband if current version on 548 * device != required version 549 * 550 * @param device the {@link ITestDevice} to flash 551 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to flash 552 * @throws DeviceNotAvailableException if device is not available 553 * @throws TargetSetupError if failed to flash baseband 554 */ checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)555 protected void checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild) 556 throws DeviceNotAvailableException, TargetSetupError { 557 if (checkShouldFlashBaseband(device, deviceBuild)) { 558 CLog.i("Flashing baseband %s", deviceBuild.getBasebandVersion()); 559 flashBaseband(device, deviceBuild.getBasebandImageFile()); 560 if (mIncrementalFlashing != null) { 561 mIncrementalFlashing.notifyBasebadNeedsRevert(); 562 } 563 } 564 } 565 566 /** 567 * Check if the baseband on the provided device needs to be flashed. 568 * 569 * @param device the {@link ITestDevice} to check 570 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to check 571 * @throws DeviceNotAvailableException if device is not available 572 * @throws TargetSetupError if failed to flash baseband 573 */ checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)574 protected boolean checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild) 575 throws DeviceNotAvailableException, TargetSetupError { 576 String currentBasebandVersion = getImageVersion(device, "baseband"); 577 boolean shouldFlash = 578 (deviceBuild.getBasebandVersion() != null 579 && !deviceBuild.getBasebandVersion().equals(currentBasebandVersion)); 580 if (!shouldFlash) { 581 CLog.i("Baseband is already version %s, skipping flashing", currentBasebandVersion); 582 } 583 return shouldFlash; 584 } 585 586 /** 587 * Flashes the given baseband image and reboot back into bootloader 588 * 589 * @param device the {@link ITestDevice} to flash 590 * @param basebandImageFile the baseband image {@link File} 591 * @throws DeviceNotAvailableException if device is not available 592 * @throws TargetSetupError if failed to flash baseband 593 */ flashBaseband(ITestDevice device, File basebandImageFile)594 protected void flashBaseband(ITestDevice device, File basebandImageFile) 595 throws DeviceNotAvailableException, TargetSetupError { 596 flashPartition(device, basebandImageFile, BASEBAND_IMAGE_NAME); 597 device.rebootIntoBootloader(); 598 } 599 600 /** 601 * Wipe the cache partition on device. 602 * 603 * @param device the {@link ITestDevice} to flash 604 * @throws DeviceNotAvailableException if device is not available 605 * @throws TargetSetupError if failed to flash cache 606 */ wipeCache(ITestDevice device)607 protected void wipeCache(ITestDevice device) throws DeviceNotAvailableException, 608 TargetSetupError { 609 // only wipe cache if user data is being wiped 610 if (!mUserDataFlashOption.equals(UserDataFlashOption.RETAIN)) { 611 CLog.i("Wiping cache on %s", device.getSerialNumber()); 612 String partition = "cache"; 613 if (hasPartition(device, partition)) { 614 wipePartition(device, partition); 615 } 616 } else { 617 CLog.d("Skipping cache wipe on %s", device.getSerialNumber()); 618 } 619 } 620 621 /** 622 * Flash userdata partition on device. 623 * 624 * @param device the {@link ITestDevice} to flash 625 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 626 * @throws DeviceNotAvailableException if device is not available 627 * @throws TargetSetupError if failed to flash user data 628 */ flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)629 protected void flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild) 630 throws DeviceNotAvailableException, TargetSetupError { 631 switch (mUserDataFlashOption) { 632 case FLASH: 633 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), 634 deviceBuild.getUserDataImageFile().getAbsolutePath()); 635 flashPartition(device, deviceBuild.getUserDataImageFile(), "userdata"); 636 break; 637 case FLASH_IMG_ZIP: 638 flashUserDataFromDeviceImageFile(device, deviceBuild); 639 break; 640 case FORCE_WIPE: // intentional fallthrough 641 case WIPE: 642 CLog.i("Wiping userdata %s", device.getSerialNumber()); 643 wipePartition(device, "userdata"); 644 break; 645 646 case TESTS_ZIP: 647 device.rebootUntilOnline(); // required to install tests 648 if (device.isEncryptionSupported() && device.isDeviceEncrypted()) { 649 device.unlockDevice(); 650 } 651 getTestsZipInstaller().pushTestsZipOntoData(device, deviceBuild); 652 // Reboot into bootloader to continue the flashing process 653 device.rebootIntoBootloader(); 654 break; 655 656 case WIPE_RM: 657 device.rebootUntilOnline(); // required to install tests 658 getTestsZipInstaller().deleteData(device); 659 // Reboot into bootloader to continue the flashing process 660 device.rebootIntoBootloader(); 661 break; 662 663 default: 664 CLog.d("Skipping userdata flash for %s", device.getSerialNumber()); 665 } 666 } 667 668 /** 669 * Extracts the userdata.img from device image file and flashes it onto device 670 * 671 * @param device the {@link ITestDevice} to flash 672 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 673 * @throws DeviceNotAvailableException if device is not available 674 * @throws TargetSetupError if failed to extract or flash user data 675 */ flashUserDataFromDeviceImageFile( ITestDevice device, IDeviceBuildInfo deviceBuild)676 protected void flashUserDataFromDeviceImageFile( 677 ITestDevice device, IDeviceBuildInfo deviceBuild) 678 throws DeviceNotAvailableException, TargetSetupError { 679 File userdataImg = null; 680 try { 681 try (ZipFile zip = new ZipFile(deviceBuild.getDeviceImageFile())) { 682 userdataImg = ZipUtil2.extractFileFromZip(zip, "userdata.img"); 683 } catch (IOException ioe) { 684 throw new TargetSetupError("failed to extract userdata.img from image file", ioe, 685 device.getDeviceDescriptor()); 686 } 687 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), userdataImg); 688 flashPartition(device, userdataImg, "userdata"); 689 } finally { 690 FileUtil.deleteFile(userdataImg); 691 } 692 } 693 694 /** 695 * Flash any device specific partitions before flashing system and rebooting. No-op unless 696 * overridden. 697 * 698 * @param device the {@link ITestDevice} to flash 699 * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files 700 * @throws DeviceNotAvailableException 701 * @throws TargetSetupError 702 */ flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)703 protected void flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild) 704 throws DeviceNotAvailableException, TargetSetupError {} 705 706 /** 707 * If needed, flash the system image on device. 708 * 709 * <p>Please look at {@link #shouldFlashSystem(String, String, IDeviceBuildInfo)} 710 * 711 * <p>Regardless of path chosen, after method execution device should be booting into userspace. 712 * 713 * @param device the {@link ITestDevice} to flash 714 * @param systemBuildId the current build id running on the device 715 * @param systemBuildFlavor the current build flavor running on the device 716 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash 717 * @return <code>true</code> if system was flashed, <code>false</code> if it was skipped 718 * @throws DeviceNotAvailableException if device is not available 719 * @throws TargetSetupError if failed to flash bootloader 720 */ checkAndFlashSystem( ITestDevice device, String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)721 protected boolean checkAndFlashSystem( 722 ITestDevice device, 723 String systemBuildId, 724 String systemBuildFlavor, 725 IDeviceBuildInfo deviceBuild) 726 throws DeviceNotAvailableException, TargetSetupError { 727 if (shouldFlashSystem(systemBuildId, systemBuildFlavor, deviceBuild)) { 728 CLog.i("Flashing system %s", deviceBuild.getDeviceBuildId()); 729 flashSystem(device, deviceBuild); 730 return true; 731 } 732 CLog.i( 733 "System is already version %s and build flavor %s, skipping flashing", 734 systemBuildId, systemBuildFlavor); 735 if (mShouldFlashRamdisk) { 736 // even if we don't flash system, still flash ramdisk just in case: because the fact 737 // that the system had a different ramdisk won't be captured by a simple build check 738 flashRamdiskIfNeeded(device, deviceBuild); 739 CLog.i("Flashed ramdisk anyways per flasher settings."); 740 } 741 // reboot 742 device.rebootUntilOnline(); 743 return false; 744 } 745 746 /** 747 * Helper method used to determine if we need to flash the system image. 748 * 749 * @param systemBuildId the current build id running on the device 750 * @param systemBuildFlavor the current build flavor running on the device 751 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash 752 * @return <code>true</code> if we should flash the system, <code>false</code> otherwise. 753 */ shouldFlashSystem(String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)754 boolean shouldFlashSystem(String systemBuildId, String systemBuildFlavor, 755 IDeviceBuildInfo deviceBuild) { 756 if (mForceSystemFlash) { 757 // Flag overrides all logic. 758 return true; 759 } 760 // Err on the side of caution, if we failed to get the build id or build flavor, force a 761 // flash of the system. 762 if (systemBuildFlavor == null || systemBuildId == null) { 763 return true; 764 } 765 // If we have the same build id and build flavor we don't need to flash it. 766 if (systemBuildId.equals(deviceBuild.getDeviceBuildId())) { 767 FileCacheTracker tracker = 768 DeviceImageTracker.getDefaultCache() 769 .getBaselineDeviceImage(deviceBuild.getDeviceSerial()); 770 if (tracker != null 771 && tracker.buildId.equals(systemBuildId) 772 && tracker.flavor.equals(deviceBuild.getBuildFlavor())) { 773 if (mIncrementalFlashing != null 774 && mIncrementalFlashing.isSameBuildFlashingAllowed()) { 775 CLog.d("Same build incremental flashing is allowed"); 776 return true; 777 } 778 return false; 779 } 780 if (systemBuildFlavor.equalsIgnoreCase(deviceBuild.getBuildFlavor())) { 781 return false; 782 } 783 } 784 return true; 785 } 786 787 /** 788 * Flash the system image on device. 789 * 790 * @param device the {@link ITestDevice} to flash 791 * @param deviceBuild the {@link IDeviceBuildInfo} to flash 792 * @throws DeviceNotAvailableException if device is not available 793 * @throws TargetSetupError if fastboot command fails 794 */ flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)795 protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild) 796 throws DeviceNotAvailableException, TargetSetupError { 797 CLog.i( 798 "Flashing device %s with image %s", 799 device.getSerialNumber(), deviceBuild.getDeviceImageFile().getAbsolutePath()); 800 // give extra time to the update cmd 801 boolean tookPermit = false; 802 try (CloseableTraceScope ignored = new CloseableTraceScope("flash_system")) { 803 boolean shouldFlash = true; 804 if (mIncrementalFlashing != null) { 805 try { 806 mIncrementalFlashing.updateDevice( 807 deviceBuild.getBootloaderImageFile(), 808 deviceBuild.getBasebandImageFile()); 809 shouldFlash = false; 810 } catch (TargetSetupError e) { 811 // In case of TargetSetupError for incremental flashing, 812 // fallback to full flashing. 813 CLog.e(e); 814 DeviceImageTracker.getDefaultCache() 815 .invalidateTracking(device.getSerialNumber()); 816 if (TestDeviceState.ONLINE.equals(device.getDeviceState())) { 817 device.rebootIntoBootloader(); 818 } 819 } 820 } 821 long startWait = System.currentTimeMillis(); 822 if (shouldFlash && mIncrementalFlashing != null) { 823 // Take the permit in case of fallback from incremental 824 try (CloseableTraceScope waitFor = 825 new CloseableTraceScope("wait_for_flashing_permit")) { 826 // Only #flash is included in the critical section 827 getHostOptions().takePermit(PermitLimitType.CONCURRENT_FLASHER); 828 tookPermit = true; 829 long queueTime = System.currentTimeMillis() - startWait; 830 CLog.v( 831 "Flashing permit obtained after %ds", 832 TimeUnit.MILLISECONDS.toSeconds(queueTime)); 833 InvocationMetricLogger.addInvocationMetrics( 834 InvocationMetricKey.FLASHING_PERMIT_LATENCY, queueTime); 835 } 836 } 837 if (shouldFlash) { 838 if (deviceBuild.getDeviceImageFile().isDirectory()) { 839 InvocationMetricLogger.addInvocationMetrics( 840 InvocationMetricKey.FLASHING_METHOD, 841 FlashingMethod.FASTBOOT_FLASH_ALL.toString()); 842 flashWithAll(device, deviceBuild); 843 } else if (getHostOptions().shouldFlashWithFuseZip() 844 && getFuseUtil().canMountZip()) { 845 InvocationMetricLogger.addInvocationMetrics( 846 InvocationMetricKey.FLASHING_METHOD, 847 FlashingMethod.FASTBOOT_FLASH_ALL_FUSE_ZIP.toString()); 848 flashWithFuseZip(device, deviceBuild); 849 } else { 850 InvocationMetricLogger.addInvocationMetrics( 851 InvocationMetricKey.FLASHING_METHOD, 852 FlashingMethod.FASTBOOT_UPDATE.toString()); 853 flashWithUpdateCommand(device, deviceBuild); 854 } 855 } 856 flashRamdiskIfNeeded(device, deviceBuild); 857 // only transfer last fastboot command status over to system flash status after having 858 // flashing the system partitions 859 mSystemFlashStatus = mFbCmdStatus; 860 } finally { 861 // if system flash status is still null here, an exception has happened 862 if (mSystemFlashStatus == null) { 863 mSystemFlashStatus = CommandStatus.EXCEPTION; 864 } 865 if (tookPermit) { 866 getHostOptions().returnPermit(PermitLimitType.CONCURRENT_FLASHER); 867 } 868 } 869 } 870 871 /** 872 * Flash the system image on device by using an image directory with fastboot flashall command. 873 * 874 * @param device the {@link ITestDevice} to flash 875 * @param deviceBuild the {@link IDeviceBuildInfo} to flash 876 * @throws DeviceNotAvailableException if device is not available 877 * @throws TargetSetupError if fastboot command fails 878 */ flashWithAll(ITestDevice device, IDeviceBuildInfo deviceBuild)879 private void flashWithAll(ITestDevice device, IDeviceBuildInfo deviceBuild) 880 throws DeviceNotAvailableException, TargetSetupError { 881 try { 882 Map<String, String> systemVarMap = new HashMap<>(); 883 systemVarMap.put( 884 "ANDROID_PRODUCT_OUT", deviceBuild.getDeviceImageFile().getAbsolutePath()); 885 String[] fastbootArgs = buildFastbootCommand("flashall", mShouldFlashRamdisk); 886 executeLongFastbootCmd(device, systemVarMap, fastbootArgs); 887 } catch (DeviceNotAvailableException e) { 888 // We wrap the exception from recovery if it fails to provide a clear message 889 throw new DeviceNotAvailableException( 890 "Device became unavailable during fastboot 'flashall'. Please verify that " 891 + "the image you are flashing can boot properly.", 892 e, 893 device.getSerialNumber()); 894 } 895 } 896 897 /** 898 * Flash the system image on device by using fuse-zip mounting with fastboot flashall command. 899 * 900 * @param device the {@link ITestDevice} to flash 901 * @param deviceBuild the {@link IDeviceBuildInfo} to flash 902 * @throws DeviceNotAvailableException if device is not available 903 * @throws TargetSetupError if fastboot command fails 904 */ flashWithFuseZip(ITestDevice device, IDeviceBuildInfo deviceBuild)905 private void flashWithFuseZip(ITestDevice device, IDeviceBuildInfo deviceBuild) 906 throws DeviceNotAvailableException, TargetSetupError { 907 FuseUtil fuseUtil = getFuseUtil(); 908 File mountPoint = null; 909 Throwable exception = null; 910 try { 911 mountPoint = FileUtil.createTempDir("FlashAllMountPoint"); 912 fuseUtil.mountZip(deviceBuild.getDeviceImageFile().getAbsoluteFile(), mountPoint); 913 Map<String, String> systemVarMap = new HashMap<>(); 914 systemVarMap.put("ANDROID_PRODUCT_OUT", mountPoint.getAbsolutePath()); 915 String[] fastbootArgs = buildFastbootCommand("flashall", mShouldFlashRamdisk); 916 executeLongFastbootCmd(device, systemVarMap, fastbootArgs); 917 } catch (DeviceNotAvailableException e) { 918 // We wrap the exception from recovery if it fails to provide a clear message 919 exception = e; 920 throw new DeviceNotAvailableException( 921 "Device became unavailable during fastboot 'flashall'. Please verify that " 922 + "the image you are flashing can boot properly.", 923 e, 924 device.getSerialNumber()); 925 } catch (IOException e) { 926 exception = e; 927 throw new TargetSetupError( 928 String.format( 929 "Unable to create a temp dir for fuse zip to mount on, error: %s", 930 e.getMessage()), 931 InfraErrorIdentifier.FAIL_TO_CREATE_FILE); 932 } finally { 933 if (mountPoint != null) { 934 fuseUtil.unmountZip(mountPoint); 935 FileUtil.recursiveDelete(mountPoint); 936 } 937 // In case the unmount operation fails, deleting the mount point will fail as well. 938 if (mountPoint.exists()) { 939 String mountErrorMsg = 940 String.format( 941 "Failed to delete mount point %s, unmount operation might failed.", 942 mountPoint); 943 if (exception != null) { 944 // If a previous exception happened, surface the previous exception only 945 CLog.e(mountErrorMsg); 946 } else { 947 throw new HarnessRuntimeException( 948 mountErrorMsg, InfraErrorIdentifier.LAB_HOST_FILESYSTEM_ERROR); 949 } 950 } 951 } 952 } 953 954 /** 955 * Flash the system image on device by using fastboot update command. 956 * 957 * @param device the {@link ITestDevice} to flash 958 * @param deviceBuild the {@link IDeviceBuildInfo} to flash 959 * @throws DeviceNotAvailableException if device is not available 960 * @throws TargetSetupError if fastboot command fails 961 */ flashWithUpdateCommand(ITestDevice device, IDeviceBuildInfo deviceBuild)962 private void flashWithUpdateCommand(ITestDevice device, IDeviceBuildInfo deviceBuild) 963 throws DeviceNotAvailableException, TargetSetupError { 964 try { 965 executeLongFastbootCmd( 966 device, 967 buildFastbootCommand( 968 "update", 969 mShouldFlashRamdisk, 970 deviceBuild.getDeviceImageFile().getAbsolutePath())); 971 } catch (DeviceNotAvailableException e) { 972 // We wrap the exception from recovery if it fails to provide a clear message 973 throw new DeviceNotAvailableException( 974 "Device became unavailable during fastboot 'update'. Please verify that " 975 + "the image you are flashing can boot properly.", 976 e, 977 device.getSerialNumber()); 978 } 979 } 980 getImageVersion(ITestDevice device, String imageName)981 protected String getImageVersion(ITestDevice device, String imageName) 982 throws DeviceNotAvailableException, TargetSetupError { 983 return fetchImageVersion(getRunUtil(), device, imageName); 984 } 985 986 /** 987 * Helper method to get the current image version on device. 988 * 989 * @param device the {@link ITestDevice} to execute command on 990 * @param imageName the name of image to get. 991 * @return String the stdout output from command 992 * @throws DeviceNotAvailableException if device is not available 993 * @throws TargetSetupError if fastboot command fails or version could not be determined 994 */ fetchImageVersion(IRunUtil runUtil, ITestDevice device, String imageName)995 public static String fetchImageVersion(IRunUtil runUtil, ITestDevice device, String imageName) 996 throws DeviceNotAvailableException, TargetSetupError { 997 int attempts = 0; 998 String versionQuery = String.format("version-%s", imageName); 999 String patternString = String.format("%s:\\s(.*)\\s", versionQuery); 1000 Pattern versionOutputPattern = Pattern.compile(patternString); 1001 1002 while (attempts < MAX_RETRY_ATTEMPTS) { 1003 CLog.v("Executing short fastboot command 'getvar %s'", versionQuery); 1004 CommandResult result = device.executeFastbootCommand("getvar", versionQuery); 1005 String queryOutput = 1006 internalHandleFastbootResult(device, result, "getvar", versionQuery); 1007 Matcher matcher = versionOutputPattern.matcher(queryOutput); 1008 if (matcher.find()) { 1009 return matcher.group(1); 1010 } else { 1011 attempts++; 1012 CLog.w( 1013 "Could not find version for '%s'. Output '%s', retrying.", 1014 imageName, queryOutput); 1015 runUtil.sleep( 1016 RETRY_SLEEP * (attempts - 1) 1017 + new Random(System.currentTimeMillis()).nextInt(RETRY_SLEEP)); 1018 continue; 1019 } 1020 } 1021 throw new TargetSetupError(String.format( 1022 "Could not find version for '%s' after %d retry attempts", imageName, attempts), 1023 device.getDeviceDescriptor()); 1024 } 1025 1026 /** 1027 * Helper method to retrieve the current slot (for A/B capable devices). 1028 * 1029 * @param device the {@link ITestDevice} to execute command on. 1030 * @return "a", "b" or null (if device is not A/B capable) 1031 * @throws DeviceNotAvailableException 1032 * @throws TargetSetupError 1033 */ getCurrentSlot(ITestDevice device)1034 protected String getCurrentSlot(ITestDevice device) 1035 throws DeviceNotAvailableException, TargetSetupError { 1036 Matcher matcher; 1037 if (device.getDeviceState().equals(TestDeviceState.FASTBOOT)) { 1038 String queryOutput = executeFastbootCmd(device, "getvar", SLOT_VAR); 1039 Pattern outputPattern = Pattern.compile(String.format("^%s: _?([ab])", SLOT_VAR)); 1040 matcher = outputPattern.matcher(queryOutput); 1041 } else { 1042 String queryOutput = device.executeShellCommand(String.format("getprop %s", SLOT_PROP)); 1043 Pattern outputPattern = 1044 Pattern.compile(String.format("^\\[%s\\]: \\[_?([ab])\\]", SLOT_PROP)); 1045 matcher = outputPattern.matcher(queryOutput); 1046 } 1047 if (matcher.find()) { 1048 return matcher.group(1); 1049 } else { 1050 return null; 1051 } 1052 } 1053 1054 /** Exposed for testing. */ getRunUtil()1055 protected IRunUtil getRunUtil() { 1056 return RunUtil.getDefault(); 1057 } 1058 1059 /** 1060 * Helper method to execute fastboot command. 1061 * 1062 * @param device the {@link ITestDevice} to execute command on 1063 * @param cmdArgs the arguments to provide to fastboot 1064 * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some 1065 * fastboot commands are weird in that they dump output to stderr on success case 1066 * @throws DeviceNotAvailableException if device is not available 1067 * @throws TargetSetupError if fastboot command fails 1068 */ executeFastbootCmd(ITestDevice device, String... cmdArgs)1069 protected String executeFastbootCmd(ITestDevice device, String... cmdArgs) 1070 throws DeviceNotAvailableException, TargetSetupError { 1071 CLog.v("Executing short fastboot command %s", java.util.Arrays.toString(cmdArgs)); 1072 CommandResult result = device.executeFastbootCommand(cmdArgs); 1073 return handleFastbootResult(device, result, cmdArgs); 1074 } 1075 1076 /** 1077 * Helper method to execute a long-running fastboot command. 1078 * 1079 * <p>Note: Most fastboot commands normally execute within the timeout allowed by {@link 1080 * ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are flashing 1081 * devices at once, fastboot commands can take much longer than normal. 1082 * 1083 * @param device the {@link ITestDevice} to execute command on 1084 * @param cmdArgs the arguments to provide to fastboot 1085 * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some 1086 * fastboot commands are weird in that they dump output to stderr on success case 1087 * @throws DeviceNotAvailableException if device is not available 1088 * @throws TargetSetupError if fastboot command fails 1089 */ executeLongFastbootCmd(ITestDevice device, String... cmdArgs)1090 protected String executeLongFastbootCmd(ITestDevice device, String... cmdArgs) 1091 throws DeviceNotAvailableException, TargetSetupError { 1092 return executeLongFastbootCmd(device, new HashMap<>(), cmdArgs); 1093 } 1094 1095 /** 1096 * Helper method to execute a long-running fastboot command with environment variables. 1097 * 1098 * <p>Note: Most fastboot commands normally execute within the timeout allowed by {@link 1099 * ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are flashing 1100 * devices at once, fastboot commands can take much longer than normal. 1101 * 1102 * @param device the {@link ITestDevice} to execute command on 1103 * @param envVarMap the map which carries environment variables which need to be set before 1104 * running the fastboot command 1105 * @param cmdArgs the arguments to provide to fastboot 1106 * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some 1107 * fastboot commands are weird in that they dump output to stderr on success case 1108 * @throws DeviceNotAvailableException if device is not available 1109 * @throws TargetSetupError if fastboot command fails 1110 */ executeLongFastbootCmd( ITestDevice device, Map<String, String> envVarMap, String... cmdArgs)1111 protected String executeLongFastbootCmd( 1112 ITestDevice device, Map<String, String> envVarMap, String... cmdArgs) 1113 throws DeviceNotAvailableException, TargetSetupError { 1114 CommandResult result = device.executeLongFastbootCommand(envVarMap, cmdArgs); 1115 return handleFastbootResult(device, result, cmdArgs); 1116 } 1117 1118 /** 1119 * Interpret the result of a fastboot command 1120 * 1121 * @param device 1122 * @param result 1123 * @param cmdArgs 1124 * @return the stderr output from command if non-empty. Otherwise returns the stdout 1125 * @throws TargetSetupError 1126 */ 1127 @VisibleForTesting handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)1128 String handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs) 1129 throws TargetSetupError { 1130 try { 1131 String res = internalHandleFastbootResult(device, result, cmdArgs); 1132 mFbCmdStatus = CommandStatus.SUCCESS; 1133 return res; 1134 } catch (TargetSetupError e) { 1135 mFbCmdStatus = CommandStatus.FAILED; 1136 throw e; 1137 } 1138 } 1139 internalHandleFastbootResult( ITestDevice device, CommandResult result, String... cmdArgs)1140 private static String internalHandleFastbootResult( 1141 ITestDevice device, CommandResult result, String... cmdArgs) throws TargetSetupError { 1142 CLog.v("fastboot stdout: " + result.getStdout()); 1143 CLog.v("fastboot stderr: " + result.getStderr()); 1144 CommandStatus status = result.getStatus(); 1145 ErrorIdentifier errorIdentifier = null; 1146 boolean diskErrorIdentified = false; 1147 for (String diskError : DISK_SPACE_ERRORS) { 1148 if (result.getStderr().contains(diskError)) { 1149 errorIdentifier = InfraErrorIdentifier.NO_DISK_SPACE; 1150 status = CommandStatus.FAILED; 1151 diskErrorIdentified = true; 1152 break; 1153 } 1154 } 1155 1156 if (!diskErrorIdentified && result.getStderr().contains("FAILED")) { 1157 // if output contains "FAILED", just override to failure 1158 status = CommandStatus.FAILED; 1159 } 1160 if (status != CommandStatus.SUCCESS) { 1161 if (errorIdentifier == null) { 1162 errorIdentifier = DeviceErrorIdentifier.ERROR_AFTER_FLASHING; 1163 } 1164 throw new TargetSetupError( 1165 String.format( 1166 "fastboot command %s failed in device %s. stdout: %s, stderr: %s", 1167 cmdArgs[0], 1168 device.getSerialNumber(), 1169 result.getStdout(), 1170 result.getStderr()), 1171 device.getDeviceDescriptor(), 1172 errorIdentifier); 1173 } 1174 if (result.getStderr().length() > 0) { 1175 return result.getStderr(); 1176 } else { 1177 return result.getStdout(); 1178 } 1179 } 1180 1181 /** 1182 * {@inheritDoc} 1183 */ 1184 @Override overrideDeviceOptions(ITestDevice device)1185 public void overrideDeviceOptions(ITestDevice device) { 1186 // ignore 1187 } 1188 1189 /** 1190 * {@inheritDoc} 1191 */ 1192 @Override setForceSystemFlash(boolean forceSystemFlash)1193 public void setForceSystemFlash(boolean forceSystemFlash) { 1194 mForceSystemFlash = forceSystemFlash; 1195 } 1196 1197 /** 1198 * {@inheritDoc} 1199 */ 1200 @Override setDataWipeSkipList(Collection<String> dataWipeSkipList)1201 public void setDataWipeSkipList(Collection<String> dataWipeSkipList) { 1202 if (dataWipeSkipList == null) { 1203 dataWipeSkipList = new ArrayList<String>(); 1204 } 1205 if (dataWipeSkipList.isEmpty()) { 1206 // To maintain backwards compatibility. 1207 // TODO: deprecate and remove. 1208 dataWipeSkipList.add("media"); 1209 } 1210 mDataWipeSkipList = dataWipeSkipList; 1211 } 1212 1213 /** 1214 * {@inheritDoc} 1215 */ 1216 @Override setWipeTimeout(long timeout)1217 public void setWipeTimeout(long timeout) { 1218 mWipeTimeout = timeout; 1219 } 1220 1221 /** 1222 * {@inheritDoc} 1223 */ 1224 @Override getSystemFlashingStatus()1225 public CommandStatus getSystemFlashingStatus() { 1226 return mSystemFlashStatus; 1227 } 1228 1229 /** {@inheritDoc} */ 1230 @Override setShouldFlashRamdisk(boolean shouldFlashRamdisk)1231 public void setShouldFlashRamdisk(boolean shouldFlashRamdisk) { 1232 mShouldFlashRamdisk = shouldFlashRamdisk; 1233 } 1234 1235 /** {@inheritDoc} */ 1236 @Override setRamdiskPartition(String ramdiskPartition)1237 public void setRamdiskPartition(String ramdiskPartition) { 1238 mRamdiskPartition = ramdiskPartition; 1239 } 1240 1241 /** {@inheritDoc} */ 1242 @Override shouldFlashRamdisk()1243 public boolean shouldFlashRamdisk() { 1244 return mShouldFlashRamdisk; 1245 } 1246 flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild)1247 protected void flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild) 1248 throws TargetSetupError, DeviceNotAvailableException { 1249 if (mShouldFlashRamdisk) { 1250 // Flash ramdisk in bootloader 1251 device.rebootIntoBootloader(); 1252 executeLongFastbootCmd( 1253 device, 1254 "flash", 1255 mRamdiskPartition, 1256 deviceBuild.getRamdiskFile().getAbsolutePath()); 1257 device.reboot(); 1258 } 1259 } 1260 setSystemBuildInfo(String systemBuildId, String systemBuildFlavor)1261 protected void setSystemBuildInfo(String systemBuildId, String systemBuildFlavor) { 1262 mSystemBuildId = systemBuildId; 1263 mSystemBuildFlavor = systemBuildFlavor; 1264 } 1265 1266 /** Gets the {@link IHostOptions} instance to use. */ 1267 @VisibleForTesting getHostOptions()1268 IHostOptions getHostOptions() { 1269 return GlobalConfiguration.getInstance().getHostOptions(); 1270 } 1271 } 1272