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(String.format( 268 "Device %s did not become available after flashing %s", 269 device.getSerialNumber(), deviceBuild.getDeviceBuildId()), 270 device.getDeviceDescriptor()); 271 } 272 device.postBootSetup(); 273 } finally { 274 // Allow interruption at the end no matter what. 275 getRunUtil().allowInterrupt(true); 276 } 277 } 278 279 /** 280 * Possible check before flashing to ensure the device is as expected compare to the build info. 281 * 282 * @param device the {@link ITestDevice} to flash. 283 * @param deviceBuild the {@link IDeviceBuildInfo} used to flash. 284 * @throws BuildError 285 * @throws DeviceNotAvailableException 286 */ checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)287 protected void checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild) 288 throws BuildError, DeviceNotAvailableException { 289 // empty of purpose 290 } 291 292 /** 293 * Verifies the expected build matches the actual build on device after flashing 294 * @throws DeviceNotAvailableException 295 */ checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)296 private void checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild) 297 throws DeviceNotAvailableException { 298 // Need to use deviceBuild.getDeviceBuildId instead of getBuildId because the build info 299 // could be an AppBuildInfo and return app build id. Need to be more explicit that we 300 // check for the device build here. 301 if (!mSkipPostFlashBuildIdCheck) { 302 checkBuildAttribute(deviceBuild.getDeviceBuildId(), device.getBuildId(), 303 device.getSerialNumber()); 304 } 305 if (!mSkipPostFlashFlavorCheck) { 306 checkBuildAttribute(deviceBuild.getDeviceBuildFlavor(), device.getBuildFlavor(), 307 device.getSerialNumber()); 308 } 309 // TODO: check bootloader and baseband versions too 310 } 311 checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, String serial)312 private void checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, 313 String serial) throws DeviceNotAvailableException { 314 if (expectedBuildAttr == null || actualBuildAttr == null || 315 !expectedBuildAttr.equals(actualBuildAttr)) { 316 // throw DNAE - assume device hardware problem - we think flash was successful but 317 // device is not running right bits 318 throw new DeviceNotAvailableException( 319 String.format( 320 "Unexpected build after flashing. Expected %s, actual %s", 321 expectedBuildAttr, actualBuildAttr), 322 serial, 323 DeviceErrorIdentifier.ERROR_AFTER_FLASHING); 324 } 325 } 326 327 /** 328 * Create {@link IDeviceFlasher} to use. Subclasses can override 329 * @throws DeviceNotAvailableException 330 */ createFlasher(ITestDevice device)331 protected abstract IDeviceFlasher createFlasher(ITestDevice device) 332 throws DeviceNotAvailableException; 333 334 /** 335 * Handle encrypting of the device pre-flash. 336 * 337 * @see #postEncryptDevice(ITestDevice, IDeviceFlasher) 338 * @param device 339 * @throws DeviceNotAvailableException 340 * @throws TargetSetupError if the device could not be encrypted or unlocked. 341 */ preEncryptDevice(ITestDevice device, IDeviceFlasher flasher)342 private void preEncryptDevice(ITestDevice device, IDeviceFlasher flasher) 343 throws DeviceNotAvailableException, TargetSetupError { 344 switch (mEncryptUserData) { 345 case IGNORE: 346 return; 347 case ENCRYPT: 348 if (!device.isEncryptionSupported()) { 349 throw new TargetSetupError("Encryption is not supported", 350 device.getDeviceDescriptor()); 351 } 352 if (!device.isDeviceEncrypted()) { 353 switch(flasher.getUserDataFlashOption()) { 354 case TESTS_ZIP: // Intentional fall through. 355 case WIPE_RM: 356 // a new filesystem will not be created by the flasher, but the userdata 357 // partition is expected to be cleared anyway, so we encrypt the device 358 // with wipe 359 if (!device.encryptDevice(false)) { 360 throw new TargetSetupError("Failed to encrypt device", 361 device.getDeviceDescriptor()); 362 } 363 if (!device.unlockDevice()) { 364 throw new TargetSetupError("Failed to unlock device", 365 device.getDeviceDescriptor()); 366 } 367 break; 368 case RETAIN: 369 // original filesystem must be retained, so we encrypt in place 370 if (!device.encryptDevice(true)) { 371 throw new TargetSetupError("Failed to encrypt device", 372 device.getDeviceDescriptor()); 373 } 374 if (!device.unlockDevice()) { 375 throw new TargetSetupError("Failed to unlock device", 376 device.getDeviceDescriptor()); 377 } 378 break; 379 default: 380 // Do nothing, userdata will be encrypted post-flash. 381 } 382 } 383 break; 384 default: 385 // should not get here 386 return; 387 } 388 } 389 390 /** 391 * Handle encrypting of the device post-flash. 392 * <p> 393 * This method handles encrypting the device after a flash in cases where a flash would undo any 394 * encryption pre-flash, such as when the device is flashed or wiped. 395 * </p> 396 * 397 * @see #preEncryptDevice(ITestDevice, IDeviceFlasher) 398 * @param device 399 * @throws DeviceNotAvailableException 400 * @throws TargetSetupError If the device could not be encrypted or unlocked. 401 */ postEncryptDevice(ITestDevice device, IDeviceFlasher flasher)402 private void postEncryptDevice(ITestDevice device, IDeviceFlasher flasher) 403 throws DeviceNotAvailableException, TargetSetupError { 404 switch (mEncryptUserData) { 405 case IGNORE: 406 return; 407 case ENCRYPT: 408 if (!device.isEncryptionSupported()) { 409 throw new TargetSetupError("Encryption is not supported", 410 device.getDeviceDescriptor()); 411 } 412 switch(flasher.getUserDataFlashOption()) { 413 case FLASH: 414 if (!device.encryptDevice(true)) { 415 throw new TargetSetupError("Failed to encrypt device", 416 device.getDeviceDescriptor()); 417 } 418 break; 419 case WIPE: // Intentional fall through. 420 case FORCE_WIPE: 421 // since the device was just wiped, encrypt with wipe 422 if (!device.encryptDevice(false)) { 423 throw new TargetSetupError("Failed to encrypt device", 424 device.getDeviceDescriptor()); 425 } 426 break; 427 default: 428 // Do nothing, userdata was encrypted pre-flash. 429 } 430 if (!device.unlockDevice()) { 431 throw new TargetSetupError("Failed to unlock device", 432 device.getDeviceDescriptor()); 433 } 434 break; 435 default: 436 // should not get here 437 return; 438 } 439 } 440 441 @Override tearDown(TestInformation testInfo, Throwable e)442 public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException { 443 if (isDisabled()) { 444 CLog.i("Skipping device flashing tearDown."); 445 return; 446 } 447 ITestDevice device = testInfo.getDevice(); 448 if (mEncryptUserData == EncryptionOptions.ENCRYPT 449 && mUserDataFlashOption != UserDataFlashOption.RETAIN) { 450 if (e instanceof DeviceNotAvailableException) { 451 CLog.e("Device was encrypted but now unavailable. may need manual cleanup"); 452 } else if (device.isDeviceEncrypted()) { 453 if (!device.unencryptDevice()) { 454 throw new RuntimeException("Failed to unencrypt device"); 455 } 456 } 457 } 458 } 459 460 /** 461 * Reports device flashing timing data to metrics backend 462 * @param branch the branch where the device build originated from 463 * @param buildFlavor the build flavor of the device build 464 * @param buildId the build number of the device build 465 * @param serial the serial number of device 466 * @param queueTime the time spent waiting for a flashing limit to become available 467 * @param flashingTime the time spent in flashing device image zip 468 * @param flashingStatus the execution status of flashing command 469 */ reportFlashMetrics(String branch, String buildFlavor, String buildId, String serial, long queueTime, long flashingTime, CommandStatus flashingStatus)470 protected void reportFlashMetrics(String branch, String buildFlavor, String buildId, 471 String serial, long queueTime, long flashingTime, CommandStatus flashingStatus) { 472 // no-op as default implementation 473 } 474 475 /** 476 * Sets the option for whether ramdisk should be flashed 477 * 478 * @param shouldFlashRamdisk 479 */ 480 @VisibleForTesting setShouldFlashRamdisk(boolean shouldFlashRamdisk)481 void setShouldFlashRamdisk(boolean shouldFlashRamdisk) { 482 mShouldFlashRamdisk = shouldFlashRamdisk; 483 } 484 setSkipPostFlashFlavorCheck(boolean skipPostFlashFlavorCheck)485 protected void setSkipPostFlashFlavorCheck(boolean skipPostFlashFlavorCheck) { 486 mSkipPostFlashFlavorCheck = skipPostFlashFlavorCheck; 487 } 488 setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck)489 protected void setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck) { 490 mSkipPostFlashBuildIdCheck = skipPostFlashBuildIdCheck; 491 } 492 } 493