| /* |
| * Copyright (C) 2011 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.IDeviceBuildInfo; |
| import com.android.tradefed.device.DeviceNotAvailableException; |
| import com.android.tradefed.device.ITestDevice; |
| import com.android.tradefed.util.FileUtil; |
| import com.android.tradefed.util.IRunUtil; |
| import com.android.tradefed.util.RunUtil; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.zip.ZipFile; |
| |
| /** |
| * A class that flashes an image on a physical Android device with a CDMA radio. |
| * <p /> |
| * This class is required because a special flashing sequence is needed to properly update the |
| * radio baseband, since it is typically the case that the radio and bootloader can't communicate |
| * directly. Typically, they use the RIL (which runs in userspace) as a proxy. |
| */ |
| public class CdmaDeviceFlasher extends FastbootDeviceFlasher { |
| private static final String LOG_TAG = "CdmaDeviceFlasher"; |
| |
| private boolean mShouldFlashBaseband = false; |
| |
| /** Time to allow for baseband to flash (in recovery mode), in ms */ |
| protected static final int BASEBAND_FLASH_TIMEOUT = 1000*60*10; |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected String getBootPartitionName() { |
| return "bootloader"; |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p /> |
| * If the baseband is up-to-date, this flasher behaves identically to the DeviceFlasher |
| * superclass. If the baseband needs to be updated, it does the following: |
| * <ol> |
| * <li>Flash the bootloader as normal</li> |
| * <li>Unpack the updater.zip</li> |
| * <li>Flash the new baseband, but <emph>don't reboot afterward</emph></li> |
| * <li>Flash the boot, recovery, and system partitions</li> |
| * <li>Reboot (device comes up in Recovery to actually flash baseband)</li> |
| * <li>Reboot again</li> |
| * <li>Flash userdata</li> |
| * <li>Reboot into userspace</li> |
| * </ol> |
| */ |
| @Override |
| public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild) throws TargetSetupError, |
| DeviceNotAvailableException { |
| |
| Log.i(LOG_TAG, String.format("Flashing device %s with build %s", |
| device.getSerialNumber(), deviceBuild.getBuildId())); |
| |
| // get system build id before booting into fastboot |
| String systemBuildId = device.getBuildId(); |
| |
| device.rebootIntoBootloader(); |
| |
| downloadFlashingResources(device, deviceBuild); |
| |
| checkAndFlashBootloader(device, deviceBuild); |
| if (checkShouldFlashBaseband(device, deviceBuild)) { |
| Log.i(LOG_TAG, "Performing special CDMA baseband update flash procedure"); |
| // Flash baseband; userdata and system are out-of-date now |
| mShouldFlashBaseband = true; |
| checkAndFlashBaseband(device, deviceBuild); |
| // Flash userdata and erase cache; system is out-of-date now |
| flashUserData(device, deviceBuild); |
| eraseCache(device); |
| // Flash system, which will cause a reboot. After system is flashed, all partitions are |
| // up-to-date |
| checkAndFlashSystem(device, systemBuildId, deviceBuild); |
| // flashSystem will leave the device in fastboot; reboot into userspace |
| device.reboot(); |
| } else { |
| // Do the standard thing |
| flashUserData(device, deviceBuild); |
| eraseCache(device); |
| checkAndFlashSystem(device, systemBuildId, deviceBuild); |
| } |
| } |
| |
| /** |
| * Flashes the given baseband image and <emph>does not reboot the device afterward</emph>. |
| * |
| * @param device the {@link ITestDevice} to flash |
| * @param basebandImageFile the baseband image {@link File} |
| * @throws DeviceNotAvailableException if device is not available |
| * @throws TargetSetupError if failed to flash baseband |
| */ |
| @Override |
| protected void flashBaseband(ITestDevice device, File basebandImageFile) |
| throws DeviceNotAvailableException, TargetSetupError { |
| executeLongFastbootCmd(device, "flash", BASEBAND_IMAGE_NAME, |
| basebandImageFile.getAbsolutePath()); |
| } |
| |
| /** |
| * Flash an individual partition |
| */ |
| private void flashNamedPartition(ITestDevice device, File dir, String partition) |
| throws DeviceNotAvailableException, TargetSetupError { |
| File imgFile = new File(dir, partition + ".img"); |
| flashPartition(device, imgFile, partition); |
| } |
| |
| /** |
| * Extract the updater zip to a directory and return the path of that directory |
| * <p /> |
| * Exposed for unit testing |
| */ |
| protected File extractSystemZip(IDeviceBuildInfo deviceBuild) throws IOException { |
| File updateDir = FileUtil.createTempDir(LOG_TAG); |
| ZipFile updater = new ZipFile(deviceBuild.getDeviceImageFile().getAbsolutePath()); |
| FileUtil.extractZip(updater, updateDir); |
| return updateDir; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild) |
| throws DeviceNotAvailableException, TargetSetupError { |
| if (mShouldFlashBaseband) { |
| // Unpack updater zip and flash partitions manually |
| Log.i(LOG_TAG, String.format("MANUALLY flashing individual partitions on %s.", |
| device.getSerialNumber())); |
| File updateDir = null; |
| try { |
| // unzip |
| updateDir = extractSystemZip(deviceBuild); |
| |
| // Expect updateDir to contain boot.img, recovery.img, system.img |
| flashNamedPartition(device, updateDir, "boot"); |
| flashNamedPartition(device, updateDir, "recovery"); |
| flashNamedPartition(device, updateDir, "system"); |
| } catch (IOException e) { |
| throw new TargetSetupError(String.format("Got IOException: %s", e.getMessage())); |
| } finally { |
| if (updateDir != null) { |
| FileUtil.recursiveDelete(updateDir); |
| updateDir = null; |
| } |
| } |
| |
| // Do the fancy double-reboot |
| // Don't use device.reboot() the first time because radio flash can take 5+ minutes |
| device.executeFastbootCommand("reboot"); |
| device.waitForDeviceOnline(BASEBAND_FLASH_TIMEOUT); |
| device.waitForDeviceAvailable(); |
| // Wait for radio version updater to do its thing |
| getRunUtil().sleep(5000); |
| // Reboot again. |
| device.reboot(); |
| // Wait for radio version updater to do its thing again |
| getRunUtil().sleep(5000); |
| // Hopefully, that should be it |
| device.rebootIntoBootloader(); |
| |
| } else { |
| super.flashSystem(device, deviceBuild); |
| } |
| } |
| |
| /** |
| * Get the {@link RunUtil} instance to use. |
| * <p/> |
| * Exposed for unit testing. |
| */ |
| protected IRunUtil getRunUtil() { |
| return RunUtil.getDefault(); |
| } |
| } |