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.IDeviceBuildInfo; 20 import com.android.tradefed.command.remote.DeviceDescriptor; 21 import com.android.tradefed.device.DeviceNotAvailableException; 22 import com.android.tradefed.device.IManagedTestDevice; 23 import com.android.tradefed.device.ITestDevice; 24 import com.android.tradefed.device.TestDeviceState; 25 import com.android.tradefed.log.LogUtil.CLog; 26 import com.android.tradefed.result.error.DeviceErrorIdentifier; 27 import com.android.tradefed.util.CommandResult; 28 import com.android.tradefed.util.CommandStatus; 29 import com.android.tradefed.util.FileUtil; 30 import com.android.tradefed.util.IRunUtil; 31 import com.android.tradefed.util.RunUtil; 32 import com.android.tradefed.util.ZipUtil2; 33 34 import org.apache.commons.compress.archivers.zip.ZipFile; 35 36 import java.io.File; 37 import java.io.IOException; 38 import java.util.ArrayList; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.List; 42 import java.util.Random; 43 import java.util.regex.Matcher; 44 import java.util.regex.Pattern; 45 import java.util.stream.Collectors; 46 47 /** A class that relies on fastboot to flash an image on physical Android hardware. */ 48 public class FastbootDeviceFlasher implements IDeviceFlasher { 49 public static final String BASEBAND_IMAGE_NAME = "radio"; 50 51 private static final String FASTBOOT_VERSION = "fastboot_version"; 52 private static final int MAX_RETRY_ATTEMPTS = 3; 53 private static final int RETRY_SLEEP = 2 * 1000; // 2s sleep between retries 54 55 private static final String SLOT_PROP = "ro.boot.slot_suffix"; 56 private static final String SLOT_VAR = "current-slot"; 57 private static final String SKIP_REBOOT_PARAM = "--skip-reboot"; 58 59 private long mWipeTimeout = 4 * 60 * 1000; 60 61 private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH; 62 63 private IFlashingResourcesRetriever mResourceRetriever; 64 65 private ITestsZipInstaller mTestsZipInstaller = null; 66 67 private Collection<String> mFlashOptions = new ArrayList<>(); 68 69 private Collection<String> mDataWipeSkipList = null; 70 71 private boolean mForceSystemFlash; 72 73 private CommandStatus mFbCmdStatus; 74 75 private CommandStatus mSystemFlashStatus; 76 77 private boolean mShouldFlashRamdisk = false; 78 79 /** 80 * {@inheritDoc} 81 */ 82 @Override setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever)83 public void setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever) { 84 mResourceRetriever = retriever; 85 } 86 getFlashingResourcesRetriever()87 protected IFlashingResourcesRetriever getFlashingResourcesRetriever() { 88 return mResourceRetriever; 89 } 90 91 /** 92 * {@inheritDoc} 93 */ 94 @Override setUserDataFlashOption(UserDataFlashOption flashOption)95 public void setUserDataFlashOption(UserDataFlashOption flashOption) { 96 mUserDataFlashOption = flashOption; 97 } 98 99 /** 100 * {@inheritDoc} 101 */ 102 @Override getUserDataFlashOption()103 public UserDataFlashOption getUserDataFlashOption() { 104 return mUserDataFlashOption; 105 } 106 setTestsZipInstaller(ITestsZipInstaller testsZipInstaller)107 void setTestsZipInstaller(ITestsZipInstaller testsZipInstaller) { 108 mTestsZipInstaller = testsZipInstaller; 109 } 110 getTestsZipInstaller()111 ITestsZipInstaller getTestsZipInstaller() { 112 // Lazily initialize the TestZipInstaller. 113 if (mTestsZipInstaller == null) { 114 if (mDataWipeSkipList == null) { 115 mDataWipeSkipList = new ArrayList<String>(); 116 } 117 if (mDataWipeSkipList.isEmpty()) { 118 // To maintain backwards compatibility. Keep media by default. 119 // TODO: deprecate and remove this. 120 mDataWipeSkipList.add("media"); 121 } 122 mTestsZipInstaller = new DefaultTestsZipInstaller(mDataWipeSkipList); 123 } 124 return mTestsZipInstaller; 125 } 126 127 /** 128 * Sets a list of options to pass with flash/update commands. 129 * 130 * @param flashOptions 131 */ setFlashOptions(Collection<String> flashOptions)132 public void setFlashOptions(Collection<String> flashOptions) { 133 // HACK: To workaround TF's command line parsing, options starting with a dash 134 // needs to be prepended with a whitespace and trimmed before they are used. 135 mFlashOptions = flashOptions.stream().map(String::trim).collect(Collectors.toList()); 136 } 137 138 /** 139 * {@inheritDoc} 140 */ 141 @Override flash(ITestDevice device, IDeviceBuildInfo deviceBuild)142 public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild) throws TargetSetupError, 143 DeviceNotAvailableException { 144 145 CLog.i("Flashing device %s with build %s", device.getSerialNumber(), 146 deviceBuild.getDeviceBuildId()); 147 148 // get system build id and build flavor before booting into fastboot 149 String systemBuildId = device.getBuildId(); 150 String systemBuildFlavor = device.getBuildFlavor(); 151 152 device.rebootIntoBootloader(); 153 154 downloadFlashingResources(device, deviceBuild); 155 preFlashSetup(device, deviceBuild); 156 if (device instanceof IManagedTestDevice) { 157 String fastbootVersion = ((IManagedTestDevice) device).getFastbootVersion(); 158 if (fastbootVersion != null) { 159 deviceBuild.addBuildAttribute(FASTBOOT_VERSION, fastbootVersion); 160 } 161 } 162 handleUserDataFlashing(device, deviceBuild); 163 checkAndFlashBootloader(device, deviceBuild); 164 checkAndFlashBaseband(device, deviceBuild); 165 flashExtraImages(device, deviceBuild); 166 checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild); 167 } 168 buildFastbootCommand(String action, boolean skipReboot, String... args)169 private String[] buildFastbootCommand(String action, boolean skipReboot, String... args) { 170 List<String> cmdArgs = new ArrayList<>(); 171 if ("flash".equals(action) || "update".equals(action)) { 172 if (skipReboot) { 173 // need to skip reboot if flashing root ramdisk, because this will be typically 174 // used together with flashing of user build, and 175 cmdArgs.add(SKIP_REBOOT_PARAM); 176 } 177 cmdArgs.addAll(mFlashOptions); 178 } 179 cmdArgs.add(action); 180 cmdArgs.addAll(Arrays.asList(args)); 181 return cmdArgs.toArray(new String[cmdArgs.size()]); 182 } 183 184 /** 185 * Perform any additional pre-flashing setup required. No-op unless overridden. 186 * 187 * @param device the {@link ITestDevice} to prepare 188 * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files 189 * @throws DeviceNotAvailableException 190 * @throws TargetSetupError 191 */ preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)192 protected void preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild) 193 throws DeviceNotAvailableException, TargetSetupError {} 194 195 /** 196 * Handle flashing of userdata/cache partition 197 * 198 * @param device the {@link ITestDevice} to flash 199 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 200 * @throws DeviceNotAvailableException 201 * @throws TargetSetupError 202 */ handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)203 protected void handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild) 204 throws DeviceNotAvailableException, TargetSetupError { 205 if (UserDataFlashOption.FORCE_WIPE.equals(mUserDataFlashOption) || 206 UserDataFlashOption.WIPE.equals(mUserDataFlashOption)) { 207 CommandResult result = device.executeFastbootCommand(mWipeTimeout, "-w"); 208 handleFastbootResult(device, result, "-w"); 209 } else { 210 flashUserData(device, deviceBuild); 211 wipeCache(device); 212 } 213 } 214 215 /** 216 * Flash an individual partition of a device 217 * 218 * @param device the {@link ITestDevice} to flash 219 * @param imgFile a {@link File} pointing to the image to be flashed 220 * @param partition the name of the partition to be flashed 221 */ flashPartition(ITestDevice device, File imgFile, String partition)222 protected void flashPartition(ITestDevice device, File imgFile, String partition) 223 throws DeviceNotAvailableException, TargetSetupError { 224 CLog.d("fastboot flash %s %s", partition, imgFile.getAbsolutePath()); 225 executeLongFastbootCmd( 226 device, 227 buildFastbootCommand( 228 "flash", mShouldFlashRamdisk, partition, imgFile.getAbsolutePath())); 229 } 230 231 /** 232 * Wipe the specified partition with `fastboot erase <name>` 233 * 234 * @param device the {@link ITestDevice} to operate on 235 * @param partition the name of the partition to be wiped 236 */ wipePartition(ITestDevice device, String partition)237 protected void wipePartition(ITestDevice device, String partition) 238 throws DeviceNotAvailableException, TargetSetupError { 239 String wipeMethod = device.getUseFastbootErase() ? "erase" : "format"; 240 CLog.d("fastboot %s %s", wipeMethod, partition); 241 CommandResult result = device.fastbootWipePartition(partition); 242 handleFastbootResult(device, result, wipeMethod, partition); 243 } 244 245 /** 246 * Checks with the bootloader if the specified partition exists or not 247 * 248 * @param device the {@link ITestDevice} to operate on 249 * @param partition the name of the partition to be checked 250 */ hasPartition(ITestDevice device, String partition)251 protected boolean hasPartition(ITestDevice device, String partition) 252 throws DeviceNotAvailableException { 253 String partitionType = String.format("partition-type:%s", partition); 254 CommandResult result = device.executeFastbootCommand("getvar", partitionType); 255 if (!CommandStatus.SUCCESS.equals(result.getStatus()) 256 || result.getStderr().contains("FAILED")) { 257 return false; 258 } 259 Pattern regex = Pattern.compile(String.format("^%s:\\s*\\S+$", partitionType), 260 Pattern.MULTILINE); 261 return regex.matcher(result.getStderr()).find(); 262 } 263 264 /** 265 * Downloads extra flashing image files needed 266 * 267 * @param device the {@link ITestDevice} to download resources for 268 * @param localBuild the {@link IDeviceBuildInfo} to populate. Assumes device image file is 269 * already set 270 * @throws DeviceNotAvailableException if device is not available 271 * @throws TargetSetupError if failed to retrieve resources 272 */ downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)273 protected void downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild) 274 throws TargetSetupError, DeviceNotAvailableException { 275 IFlashingResourcesParser resourceParser = createFlashingResourcesParser(localBuild, 276 device.getDeviceDescriptor()); 277 278 if (resourceParser.getRequiredBoards() == null) { 279 throw new TargetSetupError(String.format("Build %s is missing required board info.", 280 localBuild.getDeviceBuildId()), device.getDeviceDescriptor()); 281 } 282 String deviceProductType = device.getProductType(); 283 if (deviceProductType == null) { 284 // treat this as a fatal device error 285 throw new DeviceNotAvailableException( 286 String.format( 287 "Could not determine product type for device %s", 288 device.getSerialNumber()), 289 device.getSerialNumber(), 290 DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE); 291 } 292 verifyRequiredBoards(device, resourceParser, deviceProductType); 293 294 String bootloaderVersion = resourceParser.getRequiredBootloaderVersion(); 295 // only set bootloader image if this build doesn't have one already 296 // TODO: move this logic to the BuildProvider step 297 if (bootloaderVersion != null && localBuild.getBootloaderImageFile() == null) { 298 localBuild.setBootloaderImageFile( 299 getFlashingResourcesRetriever() 300 .retrieveFile(getBootloaderFilePrefix(device), bootloaderVersion), 301 bootloaderVersion); 302 } 303 String basebandVersion = resourceParser.getRequiredBasebandVersion(); 304 // only set baseband image if this build doesn't have one already 305 if (basebandVersion != null && localBuild.getBasebandImageFile() == null) { 306 localBuild.setBasebandImage(getFlashingResourcesRetriever().retrieveFile( 307 BASEBAND_IMAGE_NAME, basebandVersion), basebandVersion); 308 } 309 downloadExtraImageFiles(resourceParser, getFlashingResourcesRetriever(), localBuild); 310 } 311 312 /** 313 * Verify that the device's product type supports the build-to-be-flashed. 314 * 315 * <p>The base implementation will verify that the deviceProductType is included in the {@link 316 * IFlashingResourcesParser#getRequiredBoards()} collection. Subclasses may override as desired. 317 * 318 * @param device the {@link ITestDevice} to be flashed 319 * @param resourceParser the {@link IFlashingResourcesParser} 320 * @param deviceProductType the <var>device</var>'s product type 321 * @throws TargetSetupError if the build's required board info did not match the device 322 */ verifyRequiredBoards( ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType)323 protected void verifyRequiredBoards( 324 ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType) 325 throws TargetSetupError { 326 if (!containsIgnoreCase(resourceParser.getRequiredBoards(), deviceProductType)) { 327 throw new TargetSetupError(String.format("Device %s is %s. Expected %s", 328 device.getSerialNumber(), deviceProductType, 329 resourceParser.getRequiredBoards()), device.getDeviceDescriptor()); 330 } 331 } 332 containsIgnoreCase(Collection<String> stringList, String anotherString)333 private static boolean containsIgnoreCase(Collection<String> stringList, String anotherString) { 334 for (String aString : stringList) { 335 if (aString != null && aString.equalsIgnoreCase(anotherString)) { 336 return true; 337 } 338 } 339 return false; 340 } 341 342 /** 343 * Hook to allow subclasses to download extra custom image files if needed. 344 * 345 * @param resourceParser the {@link IFlashingResourcesParser} 346 * @param retriever the {@link IFlashingResourcesRetriever} 347 * @param localBuild the {@link IDeviceBuildInfo} 348 * @throws TargetSetupError 349 */ downloadExtraImageFiles(IFlashingResourcesParser resourceParser, IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)350 protected void downloadExtraImageFiles(IFlashingResourcesParser resourceParser, 351 IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild) 352 throws TargetSetupError { 353 } 354 355 /** 356 * Factory method for creating a {@link IFlashingResourcesParser}. 357 * <p/> 358 * Exposed for unit testing. 359 * 360 * @param localBuild the {@link IDeviceBuildInfo} to parse 361 * @param descriptor the descriptor of the device being flashed. 362 * @return a {@link IFlashingResourcesParser} created by the factory method. 363 * @throws TargetSetupError 364 */ createFlashingResourcesParser(IDeviceBuildInfo localBuild, DeviceDescriptor descriptor)365 protected IFlashingResourcesParser createFlashingResourcesParser(IDeviceBuildInfo localBuild, 366 DeviceDescriptor descriptor) throws TargetSetupError { 367 try { 368 return new FlashingResourcesParser(localBuild.getDeviceImageFile()); 369 } catch (TargetSetupError e) { 370 // Rethrow with descriptor since FlashingResourceParser doesn't have it. 371 throw new TargetSetupError(e.getMessage(), e, descriptor); 372 } 373 } 374 375 /** 376 * If needed, flash the bootloader image on device. 377 * <p/> 378 * Will only flash bootloader if current version on device != required version. 379 * 380 * @param device the {@link ITestDevice} to flash 381 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the bootloader image to flash 382 * @return <code>true</code> if bootloader was flashed, <code>false</code> if it was skipped 383 * @throws DeviceNotAvailableException if device is not available 384 * @throws TargetSetupError if failed to flash bootloader 385 */ checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)386 protected boolean checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild) 387 throws DeviceNotAvailableException, TargetSetupError { 388 String currentBootloaderVersion = getImageVersion(device, "bootloader"); 389 if (deviceBuild.getBootloaderVersion() != null && 390 !deviceBuild.getBootloaderVersion().equals(currentBootloaderVersion)) { 391 CLog.i("Flashing bootloader %s", deviceBuild.getBootloaderVersion()); 392 flashBootloader(device, deviceBuild.getBootloaderImageFile()); 393 return true; 394 } else { 395 CLog.i("Bootloader is already version %s, skipping flashing", currentBootloaderVersion); 396 return false; 397 } 398 } 399 400 /** 401 * Flashes the given bootloader image and reboots back into bootloader 402 * 403 * @param device the {@link ITestDevice} to flash 404 * @param bootloaderImageFile the bootloader image {@link File} 405 * @throws DeviceNotAvailableException if device is not available 406 * @throws TargetSetupError if failed to flash 407 */ flashBootloader(ITestDevice device, File bootloaderImageFile)408 protected void flashBootloader(ITestDevice device, File bootloaderImageFile) 409 throws DeviceNotAvailableException, TargetSetupError { 410 // bootloader images are small, and flash quickly. so use the 'normal' timeout 411 executeFastbootCmd( 412 device, 413 buildFastbootCommand( 414 "flash", 415 mShouldFlashRamdisk, 416 getBootPartitionName(), 417 bootloaderImageFile.getAbsolutePath())); 418 device.rebootIntoBootloader(); 419 } 420 421 /** 422 * Get the boot partition name for this device flasher. 423 * 424 * <p>Defaults to 'bootloader'. Subclasses should override if necessary. 425 */ getBootPartitionName()426 protected String getBootPartitionName() { 427 return "bootloader"; 428 } 429 430 /** 431 * Get the bootloader file prefix. 432 * <p/> 433 * Defaults to {@link #getBootPartitionName()}. Subclasses should override if necessary. 434 * 435 * @param device the {@link ITestDevice} to flash 436 * @throws DeviceNotAvailableException if device is not available 437 * @throws TargetSetupError if failed to get prefix 438 */ getBootloaderFilePrefix(ITestDevice device)439 protected String getBootloaderFilePrefix(ITestDevice device) throws TargetSetupError, 440 DeviceNotAvailableException { 441 return getBootPartitionName(); 442 } 443 444 /** 445 * If needed, flash the baseband image on device. Will only flash baseband if current version on 446 * device != required version 447 * 448 * @param device the {@link ITestDevice} to flash 449 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to flash 450 * @throws DeviceNotAvailableException if device is not available 451 * @throws TargetSetupError if failed to flash baseband 452 */ checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)453 protected void checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild) 454 throws DeviceNotAvailableException, TargetSetupError { 455 String currentBasebandVersion = getImageVersion(device, "baseband"); 456 if (checkShouldFlashBaseband(device, deviceBuild)) { 457 CLog.i("Flashing baseband %s", deviceBuild.getBasebandVersion()); 458 flashBaseband(device, deviceBuild.getBasebandImageFile()); 459 } else { 460 CLog.i("Baseband is already version %s, skipping flashing", currentBasebandVersion); 461 } 462 } 463 464 /** 465 * Check if the baseband on the provided device needs to be flashed. 466 * 467 * @param device the {@link ITestDevice} to check 468 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to check 469 * @throws DeviceNotAvailableException if device is not available 470 * @throws TargetSetupError if failed to flash baseband 471 */ checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)472 protected boolean checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild) 473 throws DeviceNotAvailableException, TargetSetupError { 474 String currentBasebandVersion = getImageVersion(device, "baseband"); 475 return (deviceBuild.getBasebandVersion() != null && 476 !deviceBuild.getBasebandVersion().equals(currentBasebandVersion)); 477 } 478 479 /** 480 * Flashes the given baseband image and reboot back into bootloader 481 * 482 * @param device the {@link ITestDevice} to flash 483 * @param basebandImageFile the baseband image {@link File} 484 * @throws DeviceNotAvailableException if device is not available 485 * @throws TargetSetupError if failed to flash baseband 486 */ flashBaseband(ITestDevice device, File basebandImageFile)487 protected void flashBaseband(ITestDevice device, File basebandImageFile) 488 throws DeviceNotAvailableException, TargetSetupError { 489 flashPartition(device, basebandImageFile, BASEBAND_IMAGE_NAME); 490 device.rebootIntoBootloader(); 491 } 492 493 /** 494 * Wipe the cache partition on device. 495 * 496 * @param device the {@link ITestDevice} to flash 497 * @throws DeviceNotAvailableException if device is not available 498 * @throws TargetSetupError if failed to flash cache 499 */ wipeCache(ITestDevice device)500 protected void wipeCache(ITestDevice device) throws DeviceNotAvailableException, 501 TargetSetupError { 502 // only wipe cache if user data is being wiped 503 if (!mUserDataFlashOption.equals(UserDataFlashOption.RETAIN)) { 504 CLog.i("Wiping cache on %s", device.getSerialNumber()); 505 String partition = "cache"; 506 if (hasPartition(device, partition)) { 507 wipePartition(device, partition); 508 } 509 } else { 510 CLog.d("Skipping cache wipe on %s", device.getSerialNumber()); 511 } 512 } 513 514 /** 515 * Flash userdata partition on device. 516 * 517 * @param device the {@link ITestDevice} to flash 518 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 519 * @throws DeviceNotAvailableException if device is not available 520 * @throws TargetSetupError if failed to flash user data 521 */ flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)522 protected void flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild) 523 throws DeviceNotAvailableException, TargetSetupError { 524 switch (mUserDataFlashOption) { 525 case FLASH: 526 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), 527 deviceBuild.getUserDataImageFile().getAbsolutePath()); 528 flashPartition(device, deviceBuild.getUserDataImageFile(), "userdata"); 529 break; 530 case FLASH_IMG_ZIP: 531 flashUserDataFromDeviceImageFile(device, deviceBuild); 532 break; 533 case FORCE_WIPE: // intentional fallthrough 534 case WIPE: 535 CLog.i("Wiping userdata %s", device.getSerialNumber()); 536 wipePartition(device, "userdata"); 537 break; 538 539 case TESTS_ZIP: 540 device.rebootUntilOnline(); // required to install tests 541 if (device.isEncryptionSupported() && device.isDeviceEncrypted()) { 542 device.unlockDevice(); 543 } 544 getTestsZipInstaller().pushTestsZipOntoData(device, deviceBuild); 545 // Reboot into bootloader to continue the flashing process 546 device.rebootIntoBootloader(); 547 break; 548 549 case WIPE_RM: 550 device.rebootUntilOnline(); // required to install tests 551 getTestsZipInstaller().deleteData(device); 552 // Reboot into bootloader to continue the flashing process 553 device.rebootIntoBootloader(); 554 break; 555 556 default: 557 CLog.d("Skipping userdata flash for %s", device.getSerialNumber()); 558 } 559 } 560 561 /** 562 * Extracts the userdata.img from device image file and flashes it onto device 563 * 564 * @param device the {@link ITestDevice} to flash 565 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash 566 * @throws DeviceNotAvailableException if device is not available 567 * @throws TargetSetupError if failed to extract or flash user data 568 */ flashUserDataFromDeviceImageFile( ITestDevice device, IDeviceBuildInfo deviceBuild)569 protected void flashUserDataFromDeviceImageFile( 570 ITestDevice device, IDeviceBuildInfo deviceBuild) 571 throws DeviceNotAvailableException, TargetSetupError { 572 File userdataImg = null; 573 try { 574 try (ZipFile zip = new ZipFile(deviceBuild.getDeviceImageFile())) { 575 userdataImg = ZipUtil2.extractFileFromZip(zip, "userdata.img"); 576 } catch (IOException ioe) { 577 throw new TargetSetupError("failed to extract userdata.img from image file", ioe, 578 device.getDeviceDescriptor()); 579 } 580 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), userdataImg); 581 flashPartition(device, userdataImg, "userdata"); 582 } finally { 583 FileUtil.deleteFile(userdataImg); 584 } 585 } 586 587 /** 588 * Flash any device specific partitions before flashing system and rebooting. No-op unless 589 * overridden. 590 * 591 * @param device the {@link ITestDevice} to flash 592 * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files 593 * @throws DeviceNotAvailableException 594 * @throws TargetSetupError 595 */ flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)596 protected void flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild) 597 throws DeviceNotAvailableException, TargetSetupError {} 598 599 /** 600 * If needed, flash the system image on device. 601 * 602 * <p>Please look at {@link #shouldFlashSystem(String, String, IDeviceBuildInfo)} 603 * 604 * <p>Regardless of path chosen, after method execution device should be booting into userspace. 605 * 606 * @param device the {@link ITestDevice} to flash 607 * @param systemBuildId the current build id running on the device 608 * @param systemBuildFlavor the current build flavor running on the device 609 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash 610 * @return <code>true</code> if system was flashed, <code>false</code> if it was skipped 611 * @throws DeviceNotAvailableException if device is not available 612 * @throws TargetSetupError if failed to flash bootloader 613 */ checkAndFlashSystem( ITestDevice device, String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)614 protected boolean checkAndFlashSystem( 615 ITestDevice device, 616 String systemBuildId, 617 String systemBuildFlavor, 618 IDeviceBuildInfo deviceBuild) 619 throws DeviceNotAvailableException, TargetSetupError { 620 if (shouldFlashSystem(systemBuildId, systemBuildFlavor, deviceBuild)) { 621 CLog.i("Flashing system %s", deviceBuild.getDeviceBuildId()); 622 flashSystem(device, deviceBuild); 623 return true; 624 } 625 CLog.i( 626 "System is already version %s and build flavor %s, skipping flashing", 627 systemBuildId, systemBuildFlavor); 628 if (mShouldFlashRamdisk) { 629 // even if we don't flash system, still flash ramdisk just in case: because the fact 630 // that the system had a different ramdisk won't be captured by a simple build check 631 flashRamdiskIfNeeded(device, deviceBuild); 632 CLog.i("Flashed ramdisk anyways per flasher settings."); 633 } 634 // reboot 635 device.rebootUntilOnline(); 636 return false; 637 } 638 639 /** 640 * Helper method used to determine if we need to flash the system image. 641 * 642 * @param systemBuildId the current build id running on the device 643 * @param systemBuildFlavor the current build flavor running on the device 644 * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash 645 * @return <code>true</code> if we should flash the system, <code>false</code> otherwise. 646 */ shouldFlashSystem(String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)647 boolean shouldFlashSystem(String systemBuildId, String systemBuildFlavor, 648 IDeviceBuildInfo deviceBuild) { 649 if (mForceSystemFlash) { 650 // Flag overrides all logic. 651 return true; 652 } 653 // Err on the side of caution, if we failed to get the build id or build flavor, force a 654 // flash of the system. 655 if (systemBuildFlavor == null || systemBuildId == null) { 656 return true; 657 } 658 // If we have the same build id and build flavor we don't need to flash it. 659 if (systemBuildId.equals(deviceBuild.getDeviceBuildId()) && 660 systemBuildFlavor.equalsIgnoreCase(deviceBuild.getBuildFlavor())) { 661 return false; 662 } 663 return true; 664 } 665 666 /** 667 * Flash the system image on device. 668 * 669 * @param device the {@link ITestDevice} to flash 670 * @param deviceBuild the {@link IDeviceBuildInfo} to flash 671 * @throws DeviceNotAvailableException if device is not available 672 * @throws TargetSetupError if fastboot command fails 673 */ flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)674 protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild) 675 throws DeviceNotAvailableException, TargetSetupError { 676 CLog.i("Flashing %s with update %s", device.getSerialNumber(), 677 deviceBuild.getDeviceImageFile().getAbsolutePath()); 678 // give extra time to the update cmd 679 try { 680 executeLongFastbootCmd( 681 device, 682 buildFastbootCommand( 683 "update", 684 mShouldFlashRamdisk, 685 deviceBuild.getDeviceImageFile().getAbsolutePath())); 686 flashRamdiskIfNeeded(device, deviceBuild); 687 // only transfer last fastboot command status over to system flash status after having 688 // flashing the system partitions 689 mSystemFlashStatus = mFbCmdStatus; 690 } finally { 691 // if system flash status is still null here, an exception has happened 692 if (mSystemFlashStatus == null) { 693 mSystemFlashStatus = CommandStatus.EXCEPTION; 694 } 695 } 696 } 697 698 /** 699 * Helper method to get the current image version on device. 700 * 701 * @param device the {@link ITestDevice} to execute command on 702 * @param imageName the name of image to get. 703 * @return String the stdout output from command 704 * @throws DeviceNotAvailableException if device is not available 705 * @throws TargetSetupError if fastboot command fails or version could not be determined 706 */ getImageVersion(ITestDevice device, String imageName)707 protected String getImageVersion(ITestDevice device, String imageName) 708 throws DeviceNotAvailableException, TargetSetupError { 709 int attempts = 0; 710 String versionQuery = String.format("version-%s", imageName); 711 String patternString = String.format("%s:\\s(.*)\\s", versionQuery); 712 Pattern versionOutputPattern = Pattern.compile(patternString); 713 714 while (attempts < MAX_RETRY_ATTEMPTS) { 715 String queryOutput = executeFastbootCmd(device, "getvar", versionQuery); 716 Matcher matcher = versionOutputPattern.matcher(queryOutput); 717 if (matcher.find()) { 718 return matcher.group(1); 719 } else { 720 attempts++; 721 CLog.w( 722 "Could not find version for '%s'. Output '%s', retrying.", 723 imageName, queryOutput); 724 getRunUtil().sleep(RETRY_SLEEP * (attempts - 1) 725 + new Random(System.currentTimeMillis()).nextInt(RETRY_SLEEP)); 726 continue; 727 } 728 } 729 throw new TargetSetupError(String.format( 730 "Could not find version for '%s' after %d retry attempts", imageName, attempts), 731 device.getDeviceDescriptor()); 732 } 733 734 /** 735 * Helper method to retrieve the current slot (for A/B capable devices). 736 * 737 * @param device the {@link ITestDevice} to execute command on. 738 * @return "a", "b" or null (if device is not A/B capable) 739 * @throws DeviceNotAvailableException 740 * @throws TargetSetupError 741 */ getCurrentSlot(ITestDevice device)742 protected String getCurrentSlot(ITestDevice device) 743 throws DeviceNotAvailableException, TargetSetupError { 744 Matcher matcher; 745 if (device.getDeviceState().equals(TestDeviceState.FASTBOOT)) { 746 String queryOutput = executeFastbootCmd(device, "getvar", SLOT_VAR); 747 Pattern outputPattern = Pattern.compile(String.format("^%s: _?([ab])", SLOT_VAR)); 748 matcher = outputPattern.matcher(queryOutput); 749 } else { 750 String queryOutput = device.executeShellCommand(String.format("getprop %s", SLOT_PROP)); 751 Pattern outputPattern = 752 Pattern.compile(String.format("^\\[%s\\]: \\[_?([ab])\\]", SLOT_PROP)); 753 matcher = outputPattern.matcher(queryOutput); 754 } 755 if (matcher.find()) { 756 return matcher.group(1); 757 } else { 758 return null; 759 } 760 } 761 762 /** Exposed for testing. */ getRunUtil()763 protected IRunUtil getRunUtil() { 764 return RunUtil.getDefault(); 765 } 766 767 /** 768 * Helper method to execute fastboot command. 769 * 770 * @param device the {@link ITestDevice} to execute command on 771 * @param cmdArgs the arguments to provide to fastboot 772 * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some 773 * fastboot commands are weird in that they dump output to stderr on success case 774 * @throws DeviceNotAvailableException if device is not available 775 * @throws TargetSetupError if fastboot command fails 776 */ executeFastbootCmd(ITestDevice device, String... cmdArgs)777 protected String executeFastbootCmd(ITestDevice device, String... cmdArgs) 778 throws DeviceNotAvailableException, TargetSetupError { 779 CLog.v("Executing short fastboot command %s", java.util.Arrays.toString(cmdArgs)); 780 CommandResult result = device.executeFastbootCommand(cmdArgs); 781 return handleFastbootResult(device, result, cmdArgs); 782 } 783 784 /** 785 * Helper method to execute a long-running fastboot command. 786 * 787 * <p>Note: Most fastboot commands normally execute within the timeout allowed by {@link 788 * ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are flashing 789 * devices at once, fastboot commands can take much longer than normal. 790 * 791 * @param device the {@link ITestDevice} to execute command on 792 * @param cmdArgs the arguments to provide to fastboot 793 * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some 794 * fastboot commands are weird in that they dump output to stderr on success case 795 * @throws DeviceNotAvailableException if device is not available 796 * @throws TargetSetupError if fastboot command fails 797 */ executeLongFastbootCmd(ITestDevice device, String... cmdArgs)798 protected String executeLongFastbootCmd(ITestDevice device, String... cmdArgs) 799 throws DeviceNotAvailableException, TargetSetupError { 800 CommandResult result = device.executeLongFastbootCommand(cmdArgs); 801 return handleFastbootResult(device, result, cmdArgs); 802 } 803 804 /** 805 * Interpret the result of a fastboot command 806 * 807 * @param device 808 * @param result 809 * @param cmdArgs 810 * @return the stderr output from command if non-empty. Otherwise returns the stdout 811 * @throws TargetSetupError 812 */ handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)813 private String handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs) 814 throws TargetSetupError { 815 CLog.v("fastboot stdout: " + result.getStdout()); 816 CLog.v("fastboot stderr: " + result.getStderr()); 817 mFbCmdStatus = result.getStatus(); 818 if (result.getStderr().contains("FAILED")) { 819 // if output contains "FAILED", just override to failure 820 mFbCmdStatus = CommandStatus.FAILED; 821 } 822 if (mFbCmdStatus != CommandStatus.SUCCESS) { 823 throw new TargetSetupError( 824 String.format( 825 "fastboot command %s failed in device %s. stdout: %s, stderr: %s", 826 cmdArgs[0], 827 device.getSerialNumber(), 828 result.getStdout(), 829 result.getStderr()), 830 device.getDeviceDescriptor(), 831 DeviceErrorIdentifier.ERROR_AFTER_FLASHING); 832 } 833 if (result.getStderr().length() > 0) { 834 return result.getStderr(); 835 } else { 836 return result.getStdout(); 837 } 838 } 839 840 /** 841 * {@inheritDoc} 842 */ 843 @Override overrideDeviceOptions(ITestDevice device)844 public void overrideDeviceOptions(ITestDevice device) { 845 // ignore 846 } 847 848 /** 849 * {@inheritDoc} 850 */ 851 @Override setForceSystemFlash(boolean forceSystemFlash)852 public void setForceSystemFlash(boolean forceSystemFlash) { 853 mForceSystemFlash = forceSystemFlash; 854 } 855 856 /** 857 * {@inheritDoc} 858 */ 859 @Override setDataWipeSkipList(Collection<String> dataWipeSkipList)860 public void setDataWipeSkipList(Collection<String> dataWipeSkipList) { 861 if (dataWipeSkipList == null) { 862 dataWipeSkipList = new ArrayList<String>(); 863 } 864 if (dataWipeSkipList.isEmpty()) { 865 // To maintain backwards compatibility. 866 // TODO: deprecate and remove. 867 dataWipeSkipList.add("media"); 868 } 869 mDataWipeSkipList = dataWipeSkipList; 870 } 871 872 /** 873 * {@inheritDoc} 874 */ 875 @Override setWipeTimeout(long timeout)876 public void setWipeTimeout(long timeout) { 877 mWipeTimeout = timeout; 878 } 879 880 /** 881 * {@inheritDoc} 882 */ 883 @Override getSystemFlashingStatus()884 public CommandStatus getSystemFlashingStatus() { 885 return mSystemFlashStatus; 886 } 887 888 /** {@inheritDoc} */ 889 @Override setShouldFlashRamdisk(boolean shouldFlashRamdisk)890 public void setShouldFlashRamdisk(boolean shouldFlashRamdisk) { 891 mShouldFlashRamdisk = shouldFlashRamdisk; 892 } 893 894 /** {@inheritDoc} */ 895 @Override shouldFlashRamdisk()896 public boolean shouldFlashRamdisk() { 897 return mShouldFlashRamdisk; 898 } 899 flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild)900 protected void flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild) 901 throws TargetSetupError, DeviceNotAvailableException { 902 if (mShouldFlashRamdisk) { 903 executeLongFastbootCmd( 904 device, "flash", "boot", deviceBuild.getRamdiskFile().getAbsolutePath()); 905 device.reboot(); 906 } 907 } 908 } 909