1 /*
2  * Copyright (C) 2011 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.ddmlib.Log;
20 import com.android.tradefed.build.IDeviceBuildInfo;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.device.ITestDevice;
23 import com.android.tradefed.util.FileUtil;
24 import com.android.tradefed.util.IRunUtil;
25 import com.android.tradefed.util.RunUtil;
26 import com.android.tradefed.util.ZipUtil2;
27 
28 import org.apache.commons.compress.archivers.zip.ZipFile;
29 
30 import java.io.File;
31 import java.io.IOException;
32 
33 /**
34  * A class that flashes an image on a physical Android device with a CDMA radio.
35  * <p />
36  * This class is required because a special flashing sequence is needed to properly update the
37  * radio baseband, since it is typically the case that the radio and bootloader can't communicate
38  * directly.  Typically, they use the RIL (which runs in userspace) as a proxy.
39  */
40 public class CdmaDeviceFlasher extends FastbootDeviceFlasher {
41     private static final String LOG_TAG = "CdmaDeviceFlasher";
42 
43     private boolean mShouldFlashBaseband = false;
44 
45     /** Time to allow for baseband to flash (in recovery mode), in ms */
46     protected static final int BASEBAND_FLASH_TIMEOUT = 10*60*1000;
47 
48     /**
49      * {@inheritDoc}
50      */
51     @Override
getBootPartitionName()52     protected String getBootPartitionName() {
53         return "bootloader";
54     }
55 
56     /**
57      * {@inheritDoc}
58      * <p />
59      * If the baseband is up-to-date, this flasher behaves identically to the DeviceFlasher
60      * superclass.  If the baseband needs to be updated, it does the following:
61      * <ol>
62      *   <li>Flash the bootloader as normal</li>
63      *   <li>Unpack the updater.zip</li>
64      *   <li>Flash the new baseband, but <emph>don't reboot afterward</emph></li>
65      *   <li>Flash the boot, recovery, and system partitions</li>
66      *   <li>Reboot (device comes up in Recovery to actually flash baseband)</li>
67      *   <li>Reboot again</li>
68      *   <li>Flash userdata</li>
69      *   <li>Reboot into userspace</li>
70      * </ol>
71      */
72     @Override
flash(ITestDevice device, IDeviceBuildInfo deviceBuild)73     public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild) throws TargetSetupError,
74             DeviceNotAvailableException {
75 
76         Log.i(LOG_TAG, String.format("Flashing device %s with build %s",
77                 device.getSerialNumber(), deviceBuild.getBuildId()));
78 
79         // get system build id and build flavor before booting into fastboot
80         String systemBuildId = device.getBuildId();
81         String systemBuildFlavor = device.getBuildFlavor();
82 
83         device.rebootIntoBootloader();
84 
85         downloadFlashingResources(device, deviceBuild);
86 
87         checkAndFlashBootloader(device, deviceBuild);
88         if (checkShouldFlashBaseband(device, deviceBuild)) {
89             Log.i(LOG_TAG, "Performing special CDMA baseband update flash procedure");
90             // We need to flash these partitions: userdata, system, boot, radio, recovery
91             // Flash userdata. system, boot, radio, recovery remain
92             flashUserData(device, deviceBuild);
93             wipeCache(device);
94 
95             // Flash baseband. system, boot, recovery remain
96             mShouldFlashBaseband = true;
97             checkAndFlashBaseband(device, deviceBuild);
98 
99             // Flash system, boot, recovery.  Will reboot the device before returning.  After these
100             // are flashed, all partitions are up-to-date.
101             checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild);
102             // flashSystem will leave the device in fastboot; reboot into userspace
103             device.reboot();
104         } else {
105             // Do the standard thing
106             flashUserData(device, deviceBuild);
107             wipeCache(device);
108             checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild);
109             device.reboot();
110         }
111     }
112 
113     /**
114      * Flashes the given baseband image and <emph>does not reboot the device afterward</emph>.
115      *
116      * @param device the {@link ITestDevice} to flash
117      * @param basebandImageFile the baseband image {@link File}
118      * @throws DeviceNotAvailableException if device is not available
119      * @throws TargetSetupError if failed to flash baseband
120      */
121     @Override
flashBaseband(ITestDevice device, File basebandImageFile)122     protected void flashBaseband(ITestDevice device, File basebandImageFile)
123             throws DeviceNotAvailableException, TargetSetupError {
124         executeLongFastbootCmd(device, "flash", BASEBAND_IMAGE_NAME,
125                 basebandImageFile.getAbsolutePath());
126     }
127 
128     /**
129      * Flash an individual partition
130      */
flashNamedPartition(ITestDevice device, File dir, String partition)131     private void flashNamedPartition(ITestDevice device, File dir, String partition)
132             throws DeviceNotAvailableException, TargetSetupError {
133         File imgFile = new File(dir, partition + ".img");
134         flashPartition(device, imgFile, partition);
135     }
136 
137     /**
138      * Extract the updater zip to a directory and return the path of that directory
139      * <p />
140      * Exposed for unit testing
141      */
extractSystemZip(IDeviceBuildInfo deviceBuild)142     protected File extractSystemZip(IDeviceBuildInfo deviceBuild) throws IOException {
143         File updateDir = FileUtil.createTempDir(LOG_TAG);
144         ZipFile updater = new ZipFile(deviceBuild.getDeviceImageFile().getAbsolutePath());
145         ZipUtil2.extractZip(updater, updateDir);
146         return updateDir;
147     }
148 
149     /**
150      * {@inheritDoc}
151      */
152     @Override
flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)153     protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)
154             throws DeviceNotAvailableException, TargetSetupError {
155         if (mShouldFlashBaseband) {
156             // Unpack updater zip and flash partitions manually
157             Log.i(LOG_TAG, String.format("MANUALLY flashing individual partitions on %s.",
158                     device.getSerialNumber()));
159             File updateDir = null;
160             try {
161                 // unzip
162                 updateDir = extractSystemZip(deviceBuild);
163 
164                 // Expect updateDir to contain boot.img, recovery.img, system.img
165                 flashNamedPartition(device, updateDir, "boot");
166                 flashNamedPartition(device, updateDir, "recovery");
167                 flashNamedPartition(device, updateDir, "system");
168             } catch (IOException e) {
169                 throw new TargetSetupError(String.format("Got IOException: %s", e.getMessage()),
170                         device.getDeviceDescriptor());
171             } finally {
172                 if (updateDir != null) {
173                     FileUtil.recursiveDelete(updateDir);
174                     updateDir = null;
175                 }
176             }
177 
178             // Do the fancy double-reboot
179             // Don't use device.reboot() the first time because radio flash can take 5+ minutes
180             device.executeFastbootCommand("reboot");
181             device.waitForDeviceOnline(BASEBAND_FLASH_TIMEOUT);
182             device.waitForDeviceAvailable();
183             // Wait for radio version updater to do its thing
184             getRunUtil().sleep(5000);
185             // Reboot again.
186             device.reboot();
187             // Wait for radio version updater to do its thing again
188             getRunUtil().sleep(5000);
189             // Hopefully, that should be it
190             device.rebootIntoBootloader();
191 
192         } else {
193             super.flashSystem(device, deviceBuild);
194             device.waitForDeviceOnline();
195             device.rebootIntoBootloader();
196         }
197     }
198 
199     /**
200      * Get the {@link RunUtil} instance to use.
201      * <p/>
202      * Exposed for unit testing.
203      */
204     @Override
getRunUtil()205     protected IRunUtil getRunUtil() {
206         return RunUtil.getDefault();
207     }
208 }
209