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