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