| /* |
| * 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.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.") |
| private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH; |
| |
| @Option(name="encrypt-userdata", description= |
| "specify if userdata partition should be encrypted") |
| private boolean mEncryptUserData = false; |
| |
| @Option(name="force-system-flash", description= |
| "specify if system should always be flashed even if already running desired build.") |
| private boolean mForceSystemFlash = 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(UserDataFlashOption flashOption) { |
| mUserDataFlashOption = flashOption; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| 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.overrideDeviceOptions(device); |
| flasher.setUserDataFlashOption(mUserDataFlashOption); |
| flasher.setForceSystemFlash(mForceSystemFlash); |
| 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 DeviceFailedToBootError(String.format( |
| "Device %s did not become available after flashing %s", |
| device.getSerialNumber(), deviceBuild.getDeviceBuildId())); |
| } |
| 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, or if the device |
| * could not be encrypted, unencrypted, or unlocked. |
| */ |
| private void preEncryptDevice(ITestDevice device, IDeviceFlasher flasher) |
| throws DeviceNotAvailableException, TargetSetupError { |
| if (!device.isEncryptionSupported()) { |
| if (mEncryptUserData) { |
| throw new TargetSetupError("Encryption is not supported"); |
| } |
| 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())); |
| } |
| if (!device.unencryptDevice()) { |
| throw new TargetSetupError("Failed to unencrypt device"); |
| } |
| } |
| |
| // Need to encrypt device |
| if (mEncryptUserData && !device.isDeviceEncrypted()) { |
| switch(flasher.getUserDataFlashOption()) { |
| case TESTS_ZIP: // Intentional fall through. |
| case WIPE_RM: |
| if (!device.encryptDevice(false)) { |
| throw new TargetSetupError("Failed to encrypt device"); |
| } |
| if (!device.unlockDevice()) { |
| throw new TargetSetupError("Failed to unlock device"); |
| } |
| break; |
| case RETAIN: |
| if (!device.encryptDevice(true)) { |
| throw new TargetSetupError("Failed to encrypt device"); |
| } |
| if (!device.unlockDevice()) { |
| throw new TargetSetupError("Failed to unlock device"); |
| } |
| 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 |
| * @throws TargetSetupError If the device could not be encrypted or unlocked. |
| */ |
| private void postEncryptDevice(ITestDevice device, IDeviceFlasher flasher) |
| throws DeviceNotAvailableException, TargetSetupError { |
| if (!device.isEncryptionSupported()) { |
| if (mEncryptUserData) { |
| throw new TargetSetupError("Encryption is not supported"); |
| } |
| return; |
| } |
| |
| if (mEncryptUserData) { |
| switch(flasher.getUserDataFlashOption()) { |
| case FLASH: |
| if (!device.encryptDevice(true)) { |
| throw new TargetSetupError("Failed to encrypt device"); |
| } |
| break; |
| case WIPE: // Intentional fall through. |
| case FORCE_WIPE: |
| if (!device.encryptDevice(false)) { |
| throw new TargetSetupError("Failed to encrypt device"); |
| } |
| break; |
| default: |
| // Do nothing, userdata was encrypted pre-flash. |
| } |
| if (!device.unlockDevice()) { |
| throw new TargetSetupError("Failed to unlock device"); |
| } |
| } |
| } |
| } |