aboutsummaryrefslogtreecommitdiff
path: root/src/com/android/tradefed/targetprep/CdmaDeviceFlasher.java
blob: db2d14663f34587d7179cf819c75bd6b65a105d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
 * 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 com.android.tradefed.util.ZipUtil;

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 = 10*60*1000;

    /**
     * {@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 and build flavor before booting into fastboot
        String systemBuildId = device.getBuildId();
        String systemBuildFlavor = device.getBuildFlavor();

        device.rebootIntoBootloader();

        downloadFlashingResources(device, deviceBuild);

        checkAndFlashBootloader(device, deviceBuild);
        if (checkShouldFlashBaseband(device, deviceBuild)) {
            Log.i(LOG_TAG, "Performing special CDMA baseband update flash procedure");
            // We need to flash these partitions: userdata, system, boot, radio, recovery
            // Flash userdata. system, boot, radio, recovery remain
            flashUserData(device, deviceBuild);
            wipeCache(device);

            // Flash baseband. system, boot, recovery remain
            mShouldFlashBaseband = true;
            checkAndFlashBaseband(device, deviceBuild);

            // Flash system, boot, recovery.  Will reboot the device before returning.  After these
            // are flashed, all partitions are up-to-date.
            checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild);
            // flashSystem will leave the device in fastboot; reboot into userspace
            device.reboot();
        } else {
            // Do the standard thing
            flashUserData(device, deviceBuild);
            wipeCache(device);
            checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild);
            device.reboot();
        }
    }

    /**
     * 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());
        ZipUtil.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()),
                        device.getDeviceDescriptor());
            } 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);
            device.waitForDeviceOnline();
            device.rebootIntoBootloader();
        }
    }

    /**
     * Get the {@link RunUtil} instance to use.
     * <p/>
     * Exposed for unit testing.
     */
    @Override
    protected IRunUtil getRunUtil() {
        return RunUtil.getDefault();
    }
}