blob: 6e74389594477d88c0092aac8f8413b36de354b5 [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tradefed.targetprep;
import com.android.ddmlib.Log;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.build.IDeviceBuildInfo;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.DeviceUnresponsiveException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.device.ITestDevice.RecoveryMode;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
import com.android.tradefed.util.IRunUtil;
import com.android.tradefed.util.RunUtil;
/**
* A {@link ITargetPreparer} that flashes an image on physical Android hardware.
*/
public abstract class DeviceFlashPreparer implements ITargetPreparer {
private static final String LOG_TAG = "DeviceFlashPreparer";
private static final int BOOT_POLL_TIME_MS = 5 * 1000;
@Option(name="device-boot-time", description="max time in ms to wait for device to boot.")
private long mDeviceBootTime = 5 * 60 * 1000;
@Option(name="userdata-flash", description=
"specify handling of userdata partition. One of FLASH, TESTS_ZIP, WIPE, WIPE_RM, SKIP.")
private String mUserDataFlashString = UserDataFlashOption.FLASH.toString();
@Option(name="encrypt-userdata", description=
"specify if userdata partition should be encrypted")
private boolean mEncryptUserData = false;
/**
* Sets the device boot time
* <p/>
* Exposed for unit testing
*/
void setDeviceBootTime(long bootTime) {
mDeviceBootTime = bootTime;
}
/**
* Gets the interval between device boot poll attempts.
* <p/>
* Exposed for unit testing
*/
int getDeviceBootPollTimeMs() {
return BOOT_POLL_TIME_MS;
}
/**
* Gets the {@link IRunUtil} instance to use.
* <p/>
* Exposed for unit testing
*/
IRunUtil getRunUtil() {
return RunUtil.getDefault();
}
/**
* Set the userdata-flash option
*
* @param flashOption
*/
public void setUserDataFlashOption(String flashOption) {
mUserDataFlashString = flashOption;
}
/**
* {@inheritDoc}
*/
public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
DeviceNotAvailableException, BuildError {
Log.i(LOG_TAG, String.format("Performing setup on %s", device.getSerialNumber()));
if (!(buildInfo instanceof IDeviceBuildInfo)) {
throw new IllegalArgumentException("Provided buildInfo is not a IDeviceBuildInfo");
}
IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo)buildInfo;
device.setRecoveryMode(RecoveryMode.ONLINE);
IDeviceFlasher flasher = createFlasher(device);
flasher.setUserDataFlashOption(UserDataFlashOption.valueOf(mUserDataFlashString));
preEncryptDevice(device, flasher);
flasher.flash(device, deviceBuild);
device.waitForDeviceOnline();
postEncryptDevice(device, flasher);
// only want logcat captured for current build, delete any accumulated log data
device.clearLogcat();
try {
device.setRecoveryMode(RecoveryMode.AVAILABLE);
device.waitForDeviceAvailable(mDeviceBootTime);
} catch (DeviceUnresponsiveException e) {
// assume this is a build problem
throw new BuildError(String.format(
"Device %s did not become available after flashing %s",
device.getSerialNumber(), buildInfo.getBuildId()));
}
device.postBootSetup();
}
/**
* Create {@link IDeviceFlasher} to use. Subclasses can override
* @throws DeviceNotAvailableException
*/
protected abstract IDeviceFlasher createFlasher(ITestDevice device)
throws DeviceNotAvailableException;
/**
* Handle encrypting or unencrypting of the device pre-flash.
*
* @see #postEncryptDevice(ITestDevice, IDeviceFlasher)
* @param device
* @throws DeviceNotAvailableException
* @throws TargetSetupError if the device should be unencrypted but the
* {@link IDeviceFlasher.UserDataFlashOption#RETAIN} flash option is used.
*/
private void preEncryptDevice(ITestDevice device, IDeviceFlasher flasher)
throws DeviceNotAvailableException, TargetSetupError {
if (!device.isEncryptionSupported()) {
if (mEncryptUserData) {
CLog.e("Encryption on %s is not supported", device.getSerialNumber());
}
return;
}
// Need to unencrypt device
if (!mEncryptUserData && device.isDeviceEncrypted()) {
if (flasher.getUserDataFlashOption() == UserDataFlashOption.RETAIN) {
throw new TargetSetupError(String.format("not possible to go from encrypted "
+ "userdata partition to unencrypted with %s",
flasher.getUserDataFlashOption()));
}
device.unencryptDevice();
}
// Need to encrypt device
if (mEncryptUserData && !device.isDeviceEncrypted()) {
switch(flasher.getUserDataFlashOption()) {
case TESTS_ZIP: // Intentional fall through.
case WIPE_RM:
device.encryptDevice(false);
device.unlockDevice();
break;
case RETAIN:
device.encryptDevice(true);
device.unlockDevice();
break;
default:
// Do nothing, userdata will be encrypted post-flash.
}
}
}
/**
* Handle encrypting of the device post-flash.
* <p>
* This method handles encrypting the device after a flash in cases where a flash would undo any
* encryption pre-flash, such as when the device is flashed or wiped.
* </p>
*
* @see #preEncryptDevice(ITestDevice, IDeviceFlasher)
* @param device
* @throws DeviceNotAvailableException
*/
private void postEncryptDevice(ITestDevice device, IDeviceFlasher flasher)
throws DeviceNotAvailableException {
if (!device.isEncryptionSupported()) {
if (mEncryptUserData) {
CLog.e("Encryption on %s is not supported", device.getSerialNumber());
}
return;
}
if (mEncryptUserData) {
switch(flasher.getUserDataFlashOption()) {
case FLASH:
device.encryptDevice(true);
break;
case WIPE: // Intentional fall through.
case FORCE_WIPE:
device.encryptDevice(false);
break;
default:
// Do nothing, userdata was encrypted pre-flash.
}
device.unlockDevice();
}
}
}