• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.tradefed.build.IBuildInfo;
20 import com.android.tradefed.build.IDeviceBuildInfo;
21 import com.android.tradefed.config.GlobalConfiguration;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.device.DeviceUnresponsiveException;
25 import com.android.tradefed.device.IDeviceManager;
26 import com.android.tradefed.device.ITestDevice;
27 import com.android.tradefed.device.ITestDevice.RecoveryMode;
28 import com.android.tradefed.host.IHostOptions;
29 import com.android.tradefed.log.LogUtil.CLog;
30 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
31 import com.android.tradefed.util.CommandStatus;
32 import com.android.tradefed.util.IRunUtil;
33 import com.android.tradefed.util.RunUtil;
34 
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.concurrent.TimeUnit;
38 
39 /** A {@link ITargetPreparer} that flashes an image on physical Android hardware. */
40 public abstract class DeviceFlashPreparer extends BaseTargetPreparer implements ITargetCleaner {
41 
42     /**
43      * Enum of options for handling the encryption of userdata image
44      */
45     public static enum EncryptionOptions {ENCRYPT, IGNORE}
46 
47     private static final int BOOT_POLL_TIME_MS = 5 * 1000;
48 
49     @Option(
50         name = "device-boot-time",
51         description = "max time to wait for device to boot.",
52         isTimeVal = true
53     )
54     private long mDeviceBootTime = 5 * 60 * 1000;
55 
56     @Option(name = "userdata-flash", description =
57         "specify handling of userdata partition.")
58     private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH;
59 
60     @Option(name = "encrypt-userdata", description = "specify if userdata partition should be "
61             + "encrypted; defaults to IGNORE, where no actions will be taken.")
62     private EncryptionOptions mEncryptUserData = EncryptionOptions.IGNORE;
63 
64     @Option(name = "force-system-flash", description =
65         "specify if system should always be flashed even if already running desired build.")
66     private boolean mForceSystemFlash = false;
67 
68     /*
69      * A temporary workaround for special builds. Should be removed after changes from build team.
70      * Bug: 18078421
71      */
72     @Option(name = "skip-post-flash-flavor-check", description =
73             "specify if system flavor should not be checked after flash")
74     private boolean mSkipPostFlashFlavorCheck = false;
75 
76     /*
77      * Used for update testing
78      */
79     @Option(name = "skip-post-flash-build-id-check", description =
80             "specify if build ID should not be checked after flash")
81     private boolean mSkipPostFlashBuildIdCheck = false;
82 
83     @Option(name = "wipe-skip-list", description =
84         "list of /data subdirectories to NOT wipe when doing UserDataFlashOption.TESTS_ZIP")
85     private Collection<String> mDataWipeSkipList = new ArrayList<>();
86 
87     /**
88      * @deprecated use host-options:concurrent-flasher-limit.
89      */
90     @Deprecated
91     @Option(name = "concurrent-flasher-limit", description =
92         "No-op, do not use. Left for backwards compatibility.")
93     private Integer mConcurrentFlasherLimit = null;
94 
95     @Option(name = "skip-post-flashing-setup",
96             description = "whether or not to skip post-flashing setup steps")
97     private boolean mSkipPostFlashingSetup = false;
98 
99     @Option(name = "wipe-timeout",
100             description = "the timeout for the command of wiping user data.", isTimeVal = true)
101     private long mWipeTimeout = 4 * 60 * 1000;
102 
103     @Option(
104         name = "fastboot-flash-option",
105         description = "additional options to pass with fastboot flash/update command."
106     )
107     private Collection<String> mFastbootFlashOptions = new ArrayList<>();
108 
109     /**
110      * Sets the device boot time
111      * <p/>
112      * Exposed for unit testing
113      */
setDeviceBootTime(long bootTime)114     void setDeviceBootTime(long bootTime) {
115         mDeviceBootTime = bootTime;
116     }
117 
118     /**
119      * Gets the interval between device boot poll attempts.
120      * <p/>
121      * Exposed for unit testing
122      */
getDeviceBootPollTimeMs()123     int getDeviceBootPollTimeMs() {
124         return BOOT_POLL_TIME_MS;
125     }
126 
127     /**
128      * Gets the {@link IRunUtil} instance to use.
129      * <p/>
130      * Exposed for unit testing
131      */
getRunUtil()132     IRunUtil getRunUtil() {
133         return RunUtil.getDefault();
134     }
135 
136     /**
137      * Getg a reference to the {@link IDeviceManager}
138      *
139      * Exposed for unit testing
140      *
141      * @return the {@link IDeviceManager} to use
142      */
getDeviceManager()143     IDeviceManager getDeviceManager() {
144         return GlobalConfiguration.getDeviceManagerInstance();
145     }
146 
147     /**
148      * Gets the {@link IHostOptions} instance to use.
149      * <p/>
150      * Exposed for unit testing
151      */
getHostOptions()152     protected IHostOptions getHostOptions() {
153         return GlobalConfiguration.getInstance().getHostOptions();
154     }
155 
156     /**
157      * Set the userdata-flash option
158      *
159      * @param flashOption
160      */
setUserDataFlashOption(UserDataFlashOption flashOption)161     public void setUserDataFlashOption(UserDataFlashOption flashOption) {
162         mUserDataFlashOption = flashOption;
163     }
164 
165     /**
166      * {@inheritDoc}
167      */
168     @Override
setUp(ITestDevice device, IBuildInfo buildInfo)169     public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
170             DeviceNotAvailableException, BuildError {
171         if (isDisabled()) {
172             CLog.i("Skipping device flashing.");
173             return;
174         }
175         CLog.i("Performing setup on %s", device.getSerialNumber());
176         if (!(buildInfo instanceof IDeviceBuildInfo)) {
177             throw new IllegalArgumentException("Provided buildInfo is not a IDeviceBuildInfo");
178         }
179         // don't allow interruptions during flashing operations.
180         getRunUtil().allowInterrupt(false);
181         IDeviceManager deviceManager = getDeviceManager();
182         long queueTime = -1;
183         long flashingTime = -1;
184         long start = -1;
185         try {
186             IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo)buildInfo;
187             checkDeviceProductType(device, deviceBuild);
188             device.setRecoveryMode(RecoveryMode.ONLINE);
189             IDeviceFlasher flasher = createFlasher(device);
190             flasher.setWipeTimeout(mWipeTimeout);
191             // only surround fastboot related operations with flashing permit restriction
192             try {
193                 start = System.currentTimeMillis();
194                 deviceManager.takeFlashingPermit();
195                 queueTime = System.currentTimeMillis() - start;
196                 CLog.v("Flashing permit obtained after %ds",
197                         TimeUnit.MILLISECONDS.toSeconds((queueTime)));
198 
199                 flasher.overrideDeviceOptions(device);
200                 flasher.setUserDataFlashOption(mUserDataFlashOption);
201                 flasher.setForceSystemFlash(mForceSystemFlash);
202                 flasher.setDataWipeSkipList(mDataWipeSkipList);
203                 if (flasher instanceof FastbootDeviceFlasher) {
204                     ((FastbootDeviceFlasher) flasher).setFlashOptions(mFastbootFlashOptions);
205                 }
206                 preEncryptDevice(device, flasher);
207                 start = System.currentTimeMillis();
208                 flasher.flash(device, deviceBuild);
209             } finally {
210                 flashingTime = System.currentTimeMillis() - start;
211                 deviceManager.returnFlashingPermit();
212                 // report flashing status
213                 CommandStatus status = flasher.getSystemFlashingStatus();
214                 if (status == null) {
215                     CLog.i("Skipped reporting metrics because system partitions were not flashed.");
216                 } else {
217                     reportFlashMetrics(buildInfo.getBuildBranch(), buildInfo.getBuildFlavor(),
218                             buildInfo.getBuildId(), device.getSerialNumber(), queueTime,
219                             flashingTime, status);
220                 }
221             }
222             // only want logcat captured for current build, delete any accumulated log data
223             device.clearLogcat();
224             if (mSkipPostFlashingSetup) {
225                 return;
226             }
227             // Temporary re-enable interruptable since the critical flashing operation is over.
228             getRunUtil().allowInterrupt(true);
229             device.waitForDeviceOnline();
230             // device may lose date setting if wiped, update with host side date in case anything on
231             // device side malfunction with an invalid date
232             if (device.enableAdbRoot()) {
233                 device.setDate(null);
234             }
235             // Disable interrupt for encryption operation.
236             getRunUtil().allowInterrupt(false);
237             checkBuild(device, deviceBuild);
238             postEncryptDevice(device, flasher);
239             // Once critical operation is done, we re-enable interruptable
240             getRunUtil().allowInterrupt(true);
241             try {
242                 device.setRecoveryMode(RecoveryMode.AVAILABLE);
243                 device.waitForDeviceAvailable(mDeviceBootTime);
244             } catch (DeviceUnresponsiveException e) {
245                 // assume this is a build problem
246                 throw new DeviceFailedToBootError(String.format(
247                         "Device %s did not become available after flashing %s",
248                         device.getSerialNumber(), deviceBuild.getDeviceBuildId()),
249                         device.getDeviceDescriptor());
250             }
251             device.postBootSetup();
252         } finally {
253             // Allow interruption at the end no matter what.
254             getRunUtil().allowInterrupt(true);
255         }
256     }
257 
258     /**
259      * Possible check before flashing to ensure the device is as expected compare to the build info.
260      *
261      * @param device the {@link ITestDevice} to flash.
262      * @param deviceBuild the {@link IDeviceBuildInfo} used to flash.
263      * @throws BuildError
264      * @throws DeviceNotAvailableException
265      */
checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)266     protected void checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)
267             throws BuildError, DeviceNotAvailableException {
268         // empty of purpose
269     }
270 
271     /**
272      * Verifies the expected build matches the actual build on device after flashing
273      * @throws DeviceNotAvailableException
274      */
checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)275     private void checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)
276             throws DeviceNotAvailableException {
277         // Need to use deviceBuild.getDeviceBuildId instead of getBuildId because the build info
278         // could be an AppBuildInfo and return app build id. Need to be more explicit that we
279         // check for the device build here.
280         if (!mSkipPostFlashBuildIdCheck) {
281             checkBuildAttribute(deviceBuild.getDeviceBuildId(), device.getBuildId(),
282                     device.getSerialNumber());
283         }
284         if (!mSkipPostFlashFlavorCheck) {
285             checkBuildAttribute(deviceBuild.getDeviceBuildFlavor(), device.getBuildFlavor(),
286                     device.getSerialNumber());
287         }
288         // TODO: check bootloader and baseband versions too
289     }
290 
checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, String serial)291     private void checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr,
292             String serial) throws DeviceNotAvailableException {
293         if (expectedBuildAttr == null || actualBuildAttr == null ||
294                 !expectedBuildAttr.equals(actualBuildAttr)) {
295             // throw DNAE - assume device hardware problem - we think flash was successful but
296             // device is not running right bits
297             throw new DeviceNotAvailableException(
298                     String.format("Unexpected build after flashing. Expected %s, actual %s",
299                             expectedBuildAttr, actualBuildAttr), serial);
300         }
301     }
302 
303     /**
304      * Create {@link IDeviceFlasher} to use. Subclasses can override
305      * @throws DeviceNotAvailableException
306      */
createFlasher(ITestDevice device)307     protected abstract IDeviceFlasher createFlasher(ITestDevice device)
308             throws DeviceNotAvailableException;
309 
310     /**
311      * Handle encrypting of the device pre-flash.
312      *
313      * @see #postEncryptDevice(ITestDevice, IDeviceFlasher)
314      * @param device
315      * @throws DeviceNotAvailableException
316      * @throws TargetSetupError if the device could not be encrypted or unlocked.
317      */
preEncryptDevice(ITestDevice device, IDeviceFlasher flasher)318     private void preEncryptDevice(ITestDevice device, IDeviceFlasher flasher)
319             throws DeviceNotAvailableException, TargetSetupError {
320         switch (mEncryptUserData) {
321             case IGNORE:
322                 return;
323             case ENCRYPT:
324                 if (!device.isEncryptionSupported()) {
325                     throw new TargetSetupError("Encryption is not supported",
326                             device.getDeviceDescriptor());
327                 }
328                 if (!device.isDeviceEncrypted()) {
329                     switch(flasher.getUserDataFlashOption()) {
330                         case TESTS_ZIP: // Intentional fall through.
331                         case WIPE_RM:
332                             // a new filesystem will not be created by the flasher, but the userdata
333                             // partition is expected to be cleared anyway, so we encrypt the device
334                             // with wipe
335                             if (!device.encryptDevice(false)) {
336                                 throw new TargetSetupError("Failed to encrypt device",
337                                         device.getDeviceDescriptor());
338                             }
339                             if (!device.unlockDevice()) {
340                                 throw new TargetSetupError("Failed to unlock device",
341                                         device.getDeviceDescriptor());
342                             }
343                             break;
344                         case RETAIN:
345                             // original filesystem must be retained, so we encrypt in place
346                             if (!device.encryptDevice(true)) {
347                                 throw new TargetSetupError("Failed to encrypt device",
348                                         device.getDeviceDescriptor());
349                             }
350                             if (!device.unlockDevice()) {
351                                 throw new TargetSetupError("Failed to unlock device",
352                                         device.getDeviceDescriptor());
353                             }
354                             break;
355                         default:
356                             // Do nothing, userdata will be encrypted post-flash.
357                     }
358                 }
359                 break;
360             default:
361                 // should not get here
362                 return;
363         }
364     }
365 
366     /**
367      * Handle encrypting of the device post-flash.
368      * <p>
369      * This method handles encrypting the device after a flash in cases where a flash would undo any
370      * encryption pre-flash, such as when the device is flashed or wiped.
371      * </p>
372      *
373      * @see #preEncryptDevice(ITestDevice, IDeviceFlasher)
374      * @param device
375      * @throws DeviceNotAvailableException
376      * @throws TargetSetupError If the device could not be encrypted or unlocked.
377      */
postEncryptDevice(ITestDevice device, IDeviceFlasher flasher)378     private void postEncryptDevice(ITestDevice device, IDeviceFlasher flasher)
379             throws DeviceNotAvailableException, TargetSetupError {
380         switch (mEncryptUserData) {
381             case IGNORE:
382                 return;
383             case ENCRYPT:
384                 if (!device.isEncryptionSupported()) {
385                     throw new TargetSetupError("Encryption is not supported",
386                             device.getDeviceDescriptor());
387                 }
388                 switch(flasher.getUserDataFlashOption()) {
389                     case FLASH:
390                         if (!device.encryptDevice(true)) {
391                             throw new TargetSetupError("Failed to encrypt device",
392                                     device.getDeviceDescriptor());
393                         }
394                         break;
395                     case WIPE: // Intentional fall through.
396                     case FORCE_WIPE:
397                         // since the device was just wiped, encrypt with wipe
398                         if (!device.encryptDevice(false)) {
399                             throw new TargetSetupError("Failed to encrypt device",
400                                     device.getDeviceDescriptor());
401                         }
402                         break;
403                     default:
404                         // Do nothing, userdata was encrypted pre-flash.
405                 }
406                 if (!device.unlockDevice()) {
407                     throw new TargetSetupError("Failed to unlock device",
408                             device.getDeviceDescriptor());
409                 }
410                 break;
411             default:
412                 // should not get here
413                 return;
414         }
415     }
416 
417     @Override
tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)418     public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable e)
419             throws DeviceNotAvailableException {
420         if (isDisabled()) {
421             CLog.i("Skipping device flashing tearDown.");
422             return;
423         }
424         if (mEncryptUserData == EncryptionOptions.ENCRYPT
425                 && mUserDataFlashOption != UserDataFlashOption.RETAIN) {
426             if (e instanceof DeviceNotAvailableException) {
427                 CLog.e("Device was encrypted but now unavailable. may need manual cleanup");
428             } else if (device.isDeviceEncrypted()) {
429                 if (!device.unencryptDevice()) {
430                     throw new RuntimeException("Failed to unencrypt device");
431                 }
432             }
433         }
434     }
435 
436     /**
437      * Reports device flashing timing data to metrics backend
438      * @param branch the branch where the device build originated from
439      * @param buildFlavor the build flavor of the device build
440      * @param buildId the build number of the device build
441      * @param serial the serial number of device
442      * @param queueTime the time spent waiting for a flashing limit to become available
443      * @param flashingTime the time spent in flashing device image zip
444      * @param flashingStatus the execution status of flashing command
445      */
reportFlashMetrics(String branch, String buildFlavor, String buildId, String serial, long queueTime, long flashingTime, CommandStatus flashingStatus)446     protected void reportFlashMetrics(String branch, String buildFlavor, String buildId,
447             String serial, long queueTime, long flashingTime, CommandStatus flashingStatus) {
448         // no-op as default implementation
449     }
450 }
451