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