• 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.annotations.VisibleForTesting;
20 import com.android.tradefed.build.IDeviceBuildInfo;
21 import com.android.tradefed.command.remote.DeviceDescriptor;
22 import com.android.tradefed.config.GlobalConfiguration;
23 import com.android.tradefed.device.DeviceNotAvailableException;
24 import com.android.tradefed.device.IManagedTestDevice;
25 import com.android.tradefed.device.ITestDevice;
26 import com.android.tradefed.device.TestDeviceState;
27 import com.android.tradefed.error.HarnessRuntimeException;
28 import com.android.tradefed.host.IHostOptions;
29 import com.android.tradefed.host.IHostOptions.PermitLimitType;
30 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
31 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
32 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
33 import com.android.tradefed.log.ITestLogger;
34 import com.android.tradefed.log.LogUtil.CLog;
35 import com.android.tradefed.result.InputStreamSource;
36 import com.android.tradefed.result.LogDataType;
37 import com.android.tradefed.result.error.DeviceErrorIdentifier;
38 import com.android.tradefed.result.error.ErrorIdentifier;
39 import com.android.tradefed.result.error.InfraErrorIdentifier;
40 import com.android.tradefed.util.CommandResult;
41 import com.android.tradefed.util.CommandStatus;
42 import com.android.tradefed.util.FileUtil;
43 import com.android.tradefed.util.FuseUtil;
44 import com.android.tradefed.util.IRunUtil;
45 import com.android.tradefed.util.RunUtil;
46 import com.android.tradefed.util.ZipUtil2;
47 import com.android.tradefed.util.image.DeviceImageTracker;
48 import com.android.tradefed.util.image.DeviceImageTracker.FileCacheTracker;
49 import com.android.tradefed.util.image.IncrementalImageUtil;
50 
51 import com.google.common.collect.ImmutableSet;
52 
53 import org.apache.commons.compress.archivers.zip.ZipFile;
54 
55 import java.io.File;
56 import java.io.IOException;
57 import java.util.ArrayList;
58 import java.util.Arrays;
59 import java.util.Collection;
60 import java.util.HashMap;
61 import java.util.List;
62 import java.util.Map;
63 import java.util.Random;
64 import java.util.concurrent.TimeUnit;
65 import java.util.regex.Matcher;
66 import java.util.regex.Pattern;
67 import java.util.stream.Collectors;
68 
69 /** A class that relies on fastboot to flash an image on physical Android hardware. */
70 public class FastbootDeviceFlasher implements IDeviceFlasher {
71     public static final String BASEBAND_IMAGE_NAME = "radio";
72 
73     private static final String FASTBOOT_VERSION = "fastboot_version";
74     private static final int MAX_RETRY_ATTEMPTS = 3;
75     private static final int RETRY_SLEEP = 2 * 1000; // 2s sleep between retries
76 
77     private static final String SLOT_PROP = "ro.boot.slot_suffix";
78     private static final String SLOT_VAR = "current-slot";
79     private static final String SKIP_REBOOT_PARAM = "--skip-reboot";
80     private static final ImmutableSet<String> DISK_SPACE_ERRORS =
81         ImmutableSet.of("No space left on device", "failed to create temporary file");
82 
83     private long mWipeTimeout = 4 * 60 * 1000;
84 
85     private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH;
86 
87     private IFlashingResourcesRetriever mResourceRetriever;
88 
89     private ITestsZipInstaller mTestsZipInstaller = null;
90 
91     private Collection<String> mFlashOptions = new ArrayList<>();
92 
93     private Collection<String> mDataWipeSkipList = null;
94 
95     private boolean mForceSystemFlash;
96 
97     private CommandStatus mFbCmdStatus;
98 
99     private CommandStatus mSystemFlashStatus;
100 
101     private boolean mShouldFlashRamdisk = false;
102 
103     private String mRamdiskPartition = "root";
104 
105     private String mSystemBuildId = null;
106     private String mSystemBuildFlavor = null;
107 
108     private IncrementalImageUtil mIncrementalFlashing = null;
109     private ITestLogger mTestLogger = null;
110 
111     @VisibleForTesting
getFuseUtil()112     protected FuseUtil getFuseUtil() {
113         return new FuseUtil();
114     }
115 
116     /**
117      * {@inheritDoc}
118      */
119     @Override
setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever)120     public void setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever) {
121         mResourceRetriever = retriever;
122     }
123 
getFlashingResourcesRetriever()124     protected IFlashingResourcesRetriever getFlashingResourcesRetriever() {
125         return mResourceRetriever;
126     }
127 
setTestLogger(ITestLogger logger)128     void setTestLogger(ITestLogger logger) {
129         mTestLogger = logger;
130     }
131 
132     /**
133      * {@inheritDoc}
134      */
135     @Override
setUserDataFlashOption(UserDataFlashOption flashOption)136     public void setUserDataFlashOption(UserDataFlashOption flashOption) {
137         mUserDataFlashOption = flashOption;
138     }
139 
140     /**
141      * {@inheritDoc}
142      */
143     @Override
getUserDataFlashOption()144     public UserDataFlashOption getUserDataFlashOption() {
145         return mUserDataFlashOption;
146     }
147 
setTestsZipInstaller(ITestsZipInstaller testsZipInstaller)148     void setTestsZipInstaller(ITestsZipInstaller testsZipInstaller) {
149         mTestsZipInstaller = testsZipInstaller;
150     }
151 
getTestsZipInstaller()152     ITestsZipInstaller getTestsZipInstaller() {
153         // Lazily initialize the TestZipInstaller.
154         if (mTestsZipInstaller == null) {
155             if (mDataWipeSkipList == null) {
156                 mDataWipeSkipList = new ArrayList<String>();
157             }
158             if (mDataWipeSkipList.isEmpty()) {
159                 // To maintain backwards compatibility. Keep media by default.
160                 // TODO: deprecate and remove this.
161                 mDataWipeSkipList.add("media");
162             }
163             mTestsZipInstaller = new DefaultTestsZipInstaller(mDataWipeSkipList);
164         }
165         return mTestsZipInstaller;
166     }
167 
168     /**
169      * Sets a list of options to pass with flash/update commands.
170      *
171      * @param flashOptions
172      */
setFlashOptions(Collection<String> flashOptions)173     public void setFlashOptions(Collection<String> flashOptions) {
174         // HACK: To workaround TF's command line parsing, options starting with a dash
175         // needs to be prepended with a whitespace and trimmed before they are used.
176         mFlashOptions = flashOptions.stream().map(String::trim).collect(Collectors.toList());
177     }
178 
setIncrementalFlashing(IncrementalImageUtil incrementalUtil)179     public void setIncrementalFlashing(IncrementalImageUtil incrementalUtil) {
180         mIncrementalFlashing = incrementalUtil;
181     }
182 
183     /** {@inheritDoc} */
184     @Override
preFlashOperations(ITestDevice device, IDeviceBuildInfo deviceBuild)185     public void preFlashOperations(ITestDevice device, IDeviceBuildInfo deviceBuild)
186             throws TargetSetupError, DeviceNotAvailableException {
187         boolean initialStateFastbootD =
188                 supportsFlashingInFastbootD() &&
189                 TestDeviceState.FASTBOOTD.equals(device.getDeviceState());
190         if (initialStateFastbootD) {
191             CLog.i("Using flashing from fastbootd");
192             InvocationMetricLogger.addInvocationMetrics(
193                     InvocationMetricKey.FLASHING_FROM_FASTBOOTD, 1);
194         }
195 
196         CLog.i("Flashing device %s with build %s", device.getSerialNumber(),
197                 deviceBuild.getDeviceBuildId());
198 
199         // Get system build id and build flavor before booting into fastboot
200         if (TestDeviceState.ONLINE.equals(device.getDeviceState())) {
201             setSystemBuildInfo(device.getBuildId(), device.getBuildFlavor());
202         }
203         downloadFlashingResources(device, deviceBuild);
204         if (device instanceof IManagedTestDevice) {
205             String fastbootVersion = ((IManagedTestDevice) device).getFastbootVersion();
206             if (fastbootVersion != null) {
207                 deviceBuild.addBuildAttribute(FASTBOOT_VERSION, fastbootVersion);
208             }
209         }
210 
211         if (mIncrementalFlashing != null
212                 && mIncrementalFlashing.useUpdatedFlow()
213                 && shouldFlashSystem(mSystemBuildId, mSystemBuildFlavor, deviceBuild)) {
214             try {
215                 mIncrementalFlashing.updateDeviceWithNewFlow(
216                         deviceBuild.getBootloaderImageFile(), deviceBuild.getBasebandImageFile());
217             } catch (TargetSetupError e) {
218                 mIncrementalFlashing = null;
219                 // In case of TargetSetupError for incremental flashing,
220                 // fallback to full flashing.
221                 CLog.e(e);
222                 DeviceImageTracker.getDefaultCache().invalidateTracking(device.getSerialNumber());
223                 if (mTestLogger != null) {
224                     try (InputStreamSource source = device.getLogcatDump()) {
225                         mTestLogger.testLog(
226                                 "apply-update-logcat-failure", LogDataType.LOGCAT, source);
227                     }
228                 }
229                 if (TestDeviceState.ONLINE.equals(device.getDeviceState())) {
230                     device.rebootIntoBootloader();
231                 }
232             }
233         } else {
234             if (!initialStateFastbootD) {
235                 device.rebootIntoBootloader();
236             }
237         }
238         preFlashSetup(device, deviceBuild);
239     }
240 
241     /** {@inheritDoc} */
242     @Override
flash(ITestDevice device, IDeviceBuildInfo deviceBuild)243     public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild)
244             throws TargetSetupError, DeviceNotAvailableException {
245         if (mIncrementalFlashing != null && mIncrementalFlashing.updateCompleted()) {
246             return;
247         }
248         handleUserDataFlashing(device, deviceBuild);
249         checkAndFlashBootloader(device, deviceBuild);
250         checkAndFlashBaseband(device, deviceBuild);
251         flashExtraImages(device, deviceBuild);
252         checkAndFlashSystem(device, mSystemBuildId, mSystemBuildFlavor, deviceBuild);
253     }
254 
buildFastbootCommand(String action, boolean skipReboot, String... args)255     private String[] buildFastbootCommand(String action, boolean skipReboot, String... args) {
256         List<String> cmdArgs = new ArrayList<>();
257         if ("flash".equals(action) || "update".equals(action) || "flashall".equals(action)) {
258             if (skipReboot) {
259                 // need to skip reboot if flashing root ramdisk, because this will be typically
260                 // used together with flashing of user build, and
261                 cmdArgs.add(SKIP_REBOOT_PARAM);
262             }
263             cmdArgs.addAll(mFlashOptions);
264         }
265         cmdArgs.add(action);
266         cmdArgs.addAll(Arrays.asList(args));
267         return cmdArgs.toArray(new String[cmdArgs.size()]);
268     }
269 
270     /**
271      * Perform any additional pre-flashing setup required. No-op unless overridden.
272      *
273      * @param device the {@link ITestDevice} to prepare
274      * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files
275      * @throws DeviceNotAvailableException
276      * @throws TargetSetupError
277      */
preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)278     protected void preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)
279             throws DeviceNotAvailableException, TargetSetupError {}
280 
281     /**
282      * Handle flashing of userdata/cache partition
283      *
284      * @param device the {@link ITestDevice} to flash
285      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
286      * @throws DeviceNotAvailableException
287      * @throws TargetSetupError
288      */
handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)289     protected void handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)
290             throws DeviceNotAvailableException, TargetSetupError {
291         if (UserDataFlashOption.FORCE_WIPE.equals(mUserDataFlashOption) ||
292                 UserDataFlashOption.WIPE.equals(mUserDataFlashOption)) {
293             CommandResult result = device.executeFastbootCommand(mWipeTimeout, "-w");
294             handleFastbootResult(device, result, "-w");
295         } else {
296             flashUserData(device, deviceBuild);
297             wipeCache(device);
298         }
299     }
300 
301     /**
302      * Flash an individual partition of a device
303      *
304      * @param device the {@link ITestDevice} to flash
305      * @param imgFile a {@link File} pointing to the image to be flashed
306      * @param partition the name of the partition to be flashed
307      */
flashPartition(ITestDevice device, File imgFile, String partition)308     protected void flashPartition(ITestDevice device, File imgFile, String partition)
309             throws DeviceNotAvailableException, TargetSetupError {
310         CLog.d(
311                 "fastboot flash %s %s [size=%d]",
312                 partition, imgFile.getAbsolutePath(), imgFile.length());
313         executeLongFastbootCmd(
314                 device,
315                 buildFastbootCommand(
316                         "flash", mShouldFlashRamdisk, partition, imgFile.getAbsolutePath()));
317     }
318 
319     /**
320      * Wipe the specified partition with `fastboot erase &lt;name&gt;`
321      *
322      * @param device the {@link ITestDevice} to operate on
323      * @param partition the name of the partition to be wiped
324      */
wipePartition(ITestDevice device, String partition)325     protected void wipePartition(ITestDevice device, String partition)
326             throws DeviceNotAvailableException, TargetSetupError {
327         String wipeMethod = device.getUseFastbootErase() ? "erase" : "format";
328         CLog.d("fastboot %s %s", wipeMethod, partition);
329         CommandResult result = device.fastbootWipePartition(partition);
330         handleFastbootResult(device, result, wipeMethod, partition);
331     }
332 
333     /**
334      * Checks with the bootloader if the specified partition exists or not
335      *
336      * @param device the {@link ITestDevice} to operate on
337      * @param partition the name of the partition to be checked
338      */
hasPartition(ITestDevice device, String partition)339     protected boolean hasPartition(ITestDevice device, String partition)
340             throws DeviceNotAvailableException {
341         String partitionType = String.format("partition-type:%s", partition);
342         CommandResult result = device.executeFastbootCommand("getvar", partitionType);
343         if (!CommandStatus.SUCCESS.equals(result.getStatus())
344                 || result.getStderr().contains("FAILED")) {
345             return false;
346         }
347         Pattern regex = Pattern.compile(String.format("^%s:\\s*\\S+$", partitionType),
348                 Pattern.MULTILINE);
349         return regex.matcher(result.getStderr()).find();
350     }
351 
352     /**
353      * Downloads extra flashing image files needed
354      *
355      * @param device the {@link ITestDevice} to download resources for
356      * @param localBuild the {@link IDeviceBuildInfo} to populate. Assumes device image file is
357      *     already set
358      * @throws DeviceNotAvailableException if device is not available
359      * @throws TargetSetupError if failed to retrieve resources
360      */
downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)361     protected void downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)
362             throws TargetSetupError, DeviceNotAvailableException {
363         IFlashingResourcesParser resourceParser = createFlashingResourcesParser(localBuild,
364                 device.getDeviceDescriptor());
365 
366         if (resourceParser.getRequiredBoards() == null) {
367             throw new TargetSetupError(String.format("Build %s is missing required board info.",
368                     localBuild.getDeviceBuildId()), device.getDeviceDescriptor());
369         }
370         String deviceProductType = device.getProductType();
371         if (deviceProductType == null) {
372             // treat this as a fatal device error
373             throw new DeviceNotAvailableException(
374                     String.format(
375                             "Could not determine product type for device %s",
376                             device.getSerialNumber()),
377                     device.getSerialNumber(),
378                     DeviceErrorIdentifier.DEVICE_UNEXPECTED_RESPONSE);
379         }
380         verifyRequiredBoards(device, resourceParser, deviceProductType);
381 
382         String bootloaderVersion = resourceParser.getRequiredBootloaderVersion();
383         // only set bootloader image if this build doesn't have one already
384         // TODO: move this logic to the BuildProvider step
385         if (bootloaderVersion != null && localBuild.getBootloaderImageFile() == null) {
386             CLog.v("Bootloader image was not included in the build artifacts (%s, %s), "
387                 + "fetching from blob service instead.",
388                 localBuild.getDeviceBuildId(), localBuild.getDeviceBuildFlavor());
389             localBuild.setBootloaderImageFile(
390                     getFlashingResourcesRetriever()
391                             .retrieveFile(getBootloaderFilePrefix(device), bootloaderVersion),
392                     bootloaderVersion);
393         }
394         String basebandVersion = resourceParser.getRequiredBasebandVersion();
395         // only set baseband image if this build doesn't have one already
396         if (basebandVersion != null && localBuild.getBasebandImageFile() == null) {
397             CLog.v("Baseband image was not included in the build artifacts (%s, %s), "
398                 + "fetching from blob service instead.",
399                 localBuild.getDeviceBuildId(), localBuild.getDeviceBuildFlavor());
400             localBuild.setBasebandImage(getFlashingResourcesRetriever().retrieveFile(
401                     BASEBAND_IMAGE_NAME, basebandVersion), basebandVersion);
402         }
403         downloadExtraImageFiles(resourceParser, getFlashingResourcesRetriever(), localBuild);
404     }
405 
406     /**
407      * Verify that the device's product type supports the build-to-be-flashed.
408      *
409      * <p>The base implementation will verify that the deviceProductType is included in the {@link
410      * IFlashingResourcesParser#getRequiredBoards()} collection. Subclasses may override as desired.
411      *
412      * @param device the {@link ITestDevice} to be flashed
413      * @param resourceParser the {@link IFlashingResourcesParser}
414      * @param deviceProductType the <var>device</var>'s product type
415      * @throws TargetSetupError if the build's required board info did not match the device
416      */
verifyRequiredBoards( ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType)417     protected void verifyRequiredBoards(
418             ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType)
419             throws TargetSetupError {
420         if (!containsIgnoreCase(resourceParser.getRequiredBoards(), deviceProductType)) {
421             throw new TargetSetupError(
422                     String.format(
423                             "Device %s is %s. Expected %s",
424                             device.getSerialNumber(),
425                             deviceProductType,
426                             resourceParser.getRequiredBoards()),
427                     device.getDeviceDescriptor(),
428                     InfraErrorIdentifier.UNEXPECTED_DEVICE_CONFIGURED);
429         }
430     }
431 
containsIgnoreCase(Collection<String> stringList, String anotherString)432     private static boolean containsIgnoreCase(Collection<String> stringList, String anotherString) {
433         for (String aString : stringList) {
434             if (aString != null && aString.equalsIgnoreCase(anotherString)) {
435                 return true;
436             }
437         }
438         return false;
439     }
440 
441     /**
442      * Hook to allow subclasses to download extra custom image files if needed.
443      *
444      * @param resourceParser the {@link IFlashingResourcesParser}
445      * @param retriever the {@link IFlashingResourcesRetriever}
446      * @param localBuild the {@link IDeviceBuildInfo}
447      * @throws TargetSetupError
448      */
downloadExtraImageFiles(IFlashingResourcesParser resourceParser, IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)449     protected void downloadExtraImageFiles(IFlashingResourcesParser resourceParser,
450             IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)
451             throws TargetSetupError {
452     }
453 
454     /**
455      * Factory method for creating a {@link IFlashingResourcesParser}.
456      * <p/>
457      * Exposed for unit testing.
458      *
459      * @param localBuild the {@link IDeviceBuildInfo} to parse
460      * @param descriptor the descriptor of the device being flashed.
461      * @return a {@link IFlashingResourcesParser} created by the factory method.
462      * @throws TargetSetupError
463      */
createFlashingResourcesParser(IDeviceBuildInfo localBuild, DeviceDescriptor descriptor)464     protected IFlashingResourcesParser createFlashingResourcesParser(IDeviceBuildInfo localBuild,
465             DeviceDescriptor descriptor) throws TargetSetupError {
466         try {
467             return new FlashingResourcesParser(localBuild.getDeviceImageFile());
468         } catch (TargetSetupError e) {
469             // Rethrow with descriptor since FlashingResourceParser doesn't have it.
470             throw new TargetSetupError(e.getMessage(), e, descriptor);
471         }
472     }
473 
474     /**
475      * If needed, flash the bootloader image on device.
476      *
477      * <p>Will only flash bootloader if current version on device != required version.
478      *
479      * @param device the {@link ITestDevice} to flash
480      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the bootloader image to flash
481      * @return <code>true</code> if bootloader was flashed, <code>false</code> if it was skipped
482      * @throws DeviceNotAvailableException if device is not available
483      * @throws TargetSetupError if failed to flash bootloader
484      */
checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)485     protected boolean checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)
486             throws DeviceNotAvailableException, TargetSetupError {
487         String currentBootloaderVersion = getImageVersion(device, "bootloader");
488         if (deviceBuild.getBootloaderVersion() != null &&
489                 !deviceBuild.getBootloaderVersion().equals(currentBootloaderVersion)) {
490             CLog.i("Flashing bootloader %s", deviceBuild.getBootloaderVersion());
491             flashBootloader(device, deviceBuild.getBootloaderImageFile());
492             if (mIncrementalFlashing != null) {
493                 mIncrementalFlashing.notifyBootloaderNeedsRevert();
494             }
495             return true;
496         } else {
497             CLog.i("Bootloader is already version %s, skipping flashing", currentBootloaderVersion);
498             return false;
499         }
500     }
501 
502     /**
503      * Flashes the given bootloader image and reboots back into bootloader
504      *
505      * @param device the {@link ITestDevice} to flash
506      * @param bootloaderImageFile the bootloader image {@link File}
507      * @throws DeviceNotAvailableException if device is not available
508      * @throws TargetSetupError if failed to flash
509      */
flashBootloader(ITestDevice device, File bootloaderImageFile)510     protected void flashBootloader(ITestDevice device, File bootloaderImageFile)
511             throws DeviceNotAvailableException, TargetSetupError {
512         // bootloader images are small, and flash quickly. so use the 'normal' timeout
513         executeFastbootCmd(
514                 device,
515                 buildFastbootCommand(
516                         "flash",
517                         mShouldFlashRamdisk,
518                         getBootPartitionName(),
519                         bootloaderImageFile.getAbsolutePath()));
520         device.rebootIntoBootloader();
521     }
522 
523     /**
524      * Get the boot partition name for this device flasher.
525      *
526      * <p>Defaults to 'bootloader'. Subclasses should override if necessary.
527      */
getBootPartitionName()528     protected String getBootPartitionName() {
529         return "bootloader";
530     }
531 
532     /**
533      * Get the bootloader file prefix.
534      * <p/>
535      * Defaults to {@link #getBootPartitionName()}. Subclasses should override if necessary.
536      *
537      * @param device the {@link ITestDevice} to flash
538      * @throws DeviceNotAvailableException if device is not available
539      * @throws TargetSetupError if failed to get prefix
540      */
getBootloaderFilePrefix(ITestDevice device)541     protected String getBootloaderFilePrefix(ITestDevice device) throws TargetSetupError,
542             DeviceNotAvailableException {
543         return getBootPartitionName();
544     }
545 
546     /**
547      * If needed, flash the baseband image on device. Will only flash baseband if current version on
548      * device != required version
549      *
550      * @param device the {@link ITestDevice} to flash
551      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to flash
552      * @throws DeviceNotAvailableException if device is not available
553      * @throws TargetSetupError if failed to flash baseband
554      */
checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)555     protected void checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)
556             throws DeviceNotAvailableException, TargetSetupError {
557         if (checkShouldFlashBaseband(device, deviceBuild)) {
558             CLog.i("Flashing baseband %s", deviceBuild.getBasebandVersion());
559             flashBaseband(device, deviceBuild.getBasebandImageFile());
560             if (mIncrementalFlashing != null) {
561                 mIncrementalFlashing.notifyBasebadNeedsRevert();
562             }
563         }
564     }
565 
566     /**
567      * Check if the baseband on the provided device needs to be flashed.
568      *
569      * @param device the {@link ITestDevice} to check
570      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to check
571      * @throws DeviceNotAvailableException if device is not available
572      * @throws TargetSetupError if failed to flash baseband
573      */
checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)574     protected boolean checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)
575             throws DeviceNotAvailableException, TargetSetupError {
576         String currentBasebandVersion = getImageVersion(device, "baseband");
577         boolean shouldFlash =
578                 (deviceBuild.getBasebandVersion() != null
579                         && !deviceBuild.getBasebandVersion().equals(currentBasebandVersion));
580         if (!shouldFlash) {
581             CLog.i("Baseband is already version %s, skipping flashing", currentBasebandVersion);
582         }
583         return shouldFlash;
584     }
585 
586     /**
587      * Flashes the given baseband image and reboot back into bootloader
588      *
589      * @param device the {@link ITestDevice} to flash
590      * @param basebandImageFile the baseband image {@link File}
591      * @throws DeviceNotAvailableException if device is not available
592      * @throws TargetSetupError if failed to flash baseband
593      */
flashBaseband(ITestDevice device, File basebandImageFile)594     protected void flashBaseband(ITestDevice device, File basebandImageFile)
595             throws DeviceNotAvailableException, TargetSetupError {
596         flashPartition(device, basebandImageFile, BASEBAND_IMAGE_NAME);
597         device.rebootIntoBootloader();
598     }
599 
600     /**
601      * Wipe the cache partition on device.
602      *
603      * @param device the {@link ITestDevice} to flash
604      * @throws DeviceNotAvailableException if device is not available
605      * @throws TargetSetupError if failed to flash cache
606      */
wipeCache(ITestDevice device)607     protected void wipeCache(ITestDevice device) throws DeviceNotAvailableException,
608             TargetSetupError {
609         // only wipe cache if user data is being wiped
610         if (!mUserDataFlashOption.equals(UserDataFlashOption.RETAIN)) {
611             CLog.i("Wiping cache on %s", device.getSerialNumber());
612             String partition = "cache";
613             if (hasPartition(device, partition)) {
614                 wipePartition(device, partition);
615             }
616         } else {
617             CLog.d("Skipping cache wipe on %s", device.getSerialNumber());
618         }
619     }
620 
621     /**
622      * Flash userdata partition on device.
623      *
624      * @param device the {@link ITestDevice} to flash
625      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
626      * @throws DeviceNotAvailableException if device is not available
627      * @throws TargetSetupError if failed to flash user data
628      */
flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)629     protected void flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)
630             throws DeviceNotAvailableException, TargetSetupError {
631         switch (mUserDataFlashOption) {
632             case FLASH:
633                 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(),
634                         deviceBuild.getUserDataImageFile().getAbsolutePath());
635                 flashPartition(device, deviceBuild.getUserDataImageFile(), "userdata");
636                 break;
637             case FLASH_IMG_ZIP:
638                 flashUserDataFromDeviceImageFile(device, deviceBuild);
639                 break;
640             case FORCE_WIPE: // intentional fallthrough
641             case WIPE:
642                 CLog.i("Wiping userdata %s", device.getSerialNumber());
643                 wipePartition(device, "userdata");
644                 break;
645 
646             case TESTS_ZIP:
647                 device.rebootUntilOnline(); // required to install tests
648                 if (device.isEncryptionSupported() && device.isDeviceEncrypted()) {
649                     device.unlockDevice();
650                 }
651                 getTestsZipInstaller().pushTestsZipOntoData(device, deviceBuild);
652                 // Reboot into bootloader to continue the flashing process
653                 device.rebootIntoBootloader();
654                 break;
655 
656             case WIPE_RM:
657                 device.rebootUntilOnline(); // required to install tests
658                 getTestsZipInstaller().deleteData(device);
659                 // Reboot into bootloader to continue the flashing process
660                 device.rebootIntoBootloader();
661                 break;
662 
663             default:
664                 CLog.d("Skipping userdata flash for %s", device.getSerialNumber());
665         }
666     }
667 
668     /**
669      * Extracts the userdata.img from device image file and flashes it onto device
670      *
671      * @param device the {@link ITestDevice} to flash
672      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
673      * @throws DeviceNotAvailableException if device is not available
674      * @throws TargetSetupError if failed to extract or flash user data
675      */
flashUserDataFromDeviceImageFile( ITestDevice device, IDeviceBuildInfo deviceBuild)676     protected void flashUserDataFromDeviceImageFile(
677             ITestDevice device, IDeviceBuildInfo deviceBuild)
678             throws DeviceNotAvailableException, TargetSetupError {
679         File userdataImg = null;
680         try {
681             try (ZipFile zip = new ZipFile(deviceBuild.getDeviceImageFile())) {
682                 userdataImg = ZipUtil2.extractFileFromZip(zip, "userdata.img");
683             } catch (IOException ioe) {
684                 throw new TargetSetupError("failed to extract userdata.img from image file", ioe,
685                         device.getDeviceDescriptor());
686             }
687             CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), userdataImg);
688             flashPartition(device, userdataImg, "userdata");
689         } finally {
690             FileUtil.deleteFile(userdataImg);
691         }
692     }
693 
694     /**
695      * Flash any device specific partitions before flashing system and rebooting. No-op unless
696      * overridden.
697      *
698      * @param device the {@link ITestDevice} to flash
699      * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files
700      * @throws DeviceNotAvailableException
701      * @throws TargetSetupError
702      */
flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)703     protected void flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)
704             throws DeviceNotAvailableException, TargetSetupError {}
705 
706     /**
707      * If needed, flash the system image on device.
708      *
709      * <p>Please look at {@link #shouldFlashSystem(String, String, IDeviceBuildInfo)}
710      *
711      * <p>Regardless of path chosen, after method execution device should be booting into userspace.
712      *
713      * @param device the {@link ITestDevice} to flash
714      * @param systemBuildId the current build id running on the device
715      * @param systemBuildFlavor the current build flavor running on the device
716      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash
717      * @return <code>true</code> if system was flashed, <code>false</code> if it was skipped
718      * @throws DeviceNotAvailableException if device is not available
719      * @throws TargetSetupError if failed to flash bootloader
720      */
checkAndFlashSystem( ITestDevice device, String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)721     protected boolean checkAndFlashSystem(
722             ITestDevice device,
723             String systemBuildId,
724             String systemBuildFlavor,
725             IDeviceBuildInfo deviceBuild)
726             throws DeviceNotAvailableException, TargetSetupError {
727         if (shouldFlashSystem(systemBuildId, systemBuildFlavor, deviceBuild)) {
728             CLog.i("Flashing system %s", deviceBuild.getDeviceBuildId());
729             flashSystem(device, deviceBuild);
730             return true;
731         }
732         CLog.i(
733                 "System is already version %s and build flavor %s, skipping flashing",
734                 systemBuildId, systemBuildFlavor);
735         if (mShouldFlashRamdisk) {
736             // even if we don't flash system, still flash ramdisk just in case: because the fact
737             // that the system had a different ramdisk won't be captured by a simple build check
738             flashRamdiskIfNeeded(device, deviceBuild);
739             CLog.i("Flashed ramdisk anyways per flasher settings.");
740         }
741         // reboot
742         device.rebootUntilOnline();
743         return false;
744     }
745 
746     /**
747      * Helper method used to determine if we need to flash the system image.
748      *
749      * @param systemBuildId the current build id running on the device
750      * @param systemBuildFlavor the current build flavor running on the device
751      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash
752      * @return <code>true</code> if we should flash the system, <code>false</code> otherwise.
753      */
shouldFlashSystem(String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)754     boolean shouldFlashSystem(String systemBuildId, String systemBuildFlavor,
755             IDeviceBuildInfo deviceBuild) {
756         if (mForceSystemFlash) {
757             // Flag overrides all logic.
758             return true;
759         }
760         // Err on the side of caution, if we failed to get the build id or build flavor, force a
761         // flash of the system.
762         if (systemBuildFlavor == null || systemBuildId == null) {
763             return true;
764         }
765         // If we have the same build id and build flavor we don't need to flash it.
766         if (systemBuildId.equals(deviceBuild.getDeviceBuildId())) {
767             FileCacheTracker tracker =
768                     DeviceImageTracker.getDefaultCache()
769                             .getBaselineDeviceImage(deviceBuild.getDeviceSerial());
770             if (tracker != null
771                     && tracker.buildId.equals(systemBuildId)
772                     && tracker.flavor.equals(deviceBuild.getBuildFlavor())) {
773                 if (mIncrementalFlashing != null
774                         && mIncrementalFlashing.isSameBuildFlashingAllowed()) {
775                     CLog.d("Same build incremental flashing is allowed");
776                     return true;
777                 }
778                 return false;
779             }
780             if (systemBuildFlavor.equalsIgnoreCase(deviceBuild.getBuildFlavor())) {
781                 return false;
782             }
783         }
784         return true;
785     }
786 
787     /**
788      * Flash the system image on device.
789      *
790      * @param device the {@link ITestDevice} to flash
791      * @param deviceBuild the {@link IDeviceBuildInfo} to flash
792      * @throws DeviceNotAvailableException if device is not available
793      * @throws TargetSetupError if fastboot command fails
794      */
flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)795     protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)
796             throws DeviceNotAvailableException, TargetSetupError {
797         CLog.i(
798                 "Flashing device %s with image %s",
799                 device.getSerialNumber(), deviceBuild.getDeviceImageFile().getAbsolutePath());
800         // give extra time to the update cmd
801         boolean tookPermit = false;
802         try (CloseableTraceScope ignored = new CloseableTraceScope("flash_system")) {
803             boolean shouldFlash = true;
804             if (mIncrementalFlashing != null) {
805                 try {
806                     mIncrementalFlashing.updateDevice(
807                             deviceBuild.getBootloaderImageFile(),
808                             deviceBuild.getBasebandImageFile());
809                     shouldFlash = false;
810                 } catch (TargetSetupError e) {
811                     // In case of TargetSetupError for incremental flashing,
812                     // fallback to full flashing.
813                     CLog.e(e);
814                     DeviceImageTracker.getDefaultCache()
815                             .invalidateTracking(device.getSerialNumber());
816                     if (TestDeviceState.ONLINE.equals(device.getDeviceState())) {
817                         device.rebootIntoBootloader();
818                     }
819                 }
820             }
821             long startWait = System.currentTimeMillis();
822             if (shouldFlash && mIncrementalFlashing != null) {
823                 // Take the permit in case of fallback from incremental
824                 try (CloseableTraceScope waitFor =
825                         new CloseableTraceScope("wait_for_flashing_permit")) {
826                     // Only #flash is included in the critical section
827                     getHostOptions().takePermit(PermitLimitType.CONCURRENT_FLASHER);
828                     tookPermit = true;
829                     long queueTime = System.currentTimeMillis() - startWait;
830                     CLog.v(
831                             "Flashing permit obtained after %ds",
832                             TimeUnit.MILLISECONDS.toSeconds(queueTime));
833                     InvocationMetricLogger.addInvocationMetrics(
834                             InvocationMetricKey.FLASHING_PERMIT_LATENCY, queueTime);
835                 }
836             }
837             if (shouldFlash) {
838                 if (deviceBuild.getDeviceImageFile().isDirectory()) {
839                     InvocationMetricLogger.addInvocationMetrics(
840                             InvocationMetricKey.FLASHING_METHOD,
841                             FlashingMethod.FASTBOOT_FLASH_ALL.toString());
842                     flashWithAll(device, deviceBuild);
843                 } else if (getHostOptions().shouldFlashWithFuseZip()
844                         && getFuseUtil().canMountZip()) {
845                     InvocationMetricLogger.addInvocationMetrics(
846                             InvocationMetricKey.FLASHING_METHOD,
847                             FlashingMethod.FASTBOOT_FLASH_ALL_FUSE_ZIP.toString());
848                     flashWithFuseZip(device, deviceBuild);
849                 } else {
850                     InvocationMetricLogger.addInvocationMetrics(
851                             InvocationMetricKey.FLASHING_METHOD,
852                             FlashingMethod.FASTBOOT_UPDATE.toString());
853                     flashWithUpdateCommand(device, deviceBuild);
854                 }
855             }
856             flashRamdiskIfNeeded(device, deviceBuild);
857             // only transfer last fastboot command status over to system flash status after having
858             // flashing the system partitions
859             mSystemFlashStatus = mFbCmdStatus;
860         } finally {
861             // if system flash status is still null here, an exception has happened
862             if (mSystemFlashStatus == null) {
863                 mSystemFlashStatus = CommandStatus.EXCEPTION;
864             }
865             if (tookPermit) {
866                 getHostOptions().returnPermit(PermitLimitType.CONCURRENT_FLASHER);
867             }
868         }
869     }
870 
871     /**
872      * Flash the system image on device by using an image directory with fastboot flashall command.
873      *
874      * @param device the {@link ITestDevice} to flash
875      * @param deviceBuild the {@link IDeviceBuildInfo} to flash
876      * @throws DeviceNotAvailableException if device is not available
877      * @throws TargetSetupError if fastboot command fails
878      */
flashWithAll(ITestDevice device, IDeviceBuildInfo deviceBuild)879     private void flashWithAll(ITestDevice device, IDeviceBuildInfo deviceBuild)
880             throws DeviceNotAvailableException, TargetSetupError {
881         try {
882             Map<String, String> systemVarMap = new HashMap<>();
883             systemVarMap.put(
884                     "ANDROID_PRODUCT_OUT", deviceBuild.getDeviceImageFile().getAbsolutePath());
885             String[] fastbootArgs = buildFastbootCommand("flashall", mShouldFlashRamdisk);
886             executeLongFastbootCmd(device, systemVarMap, fastbootArgs);
887         } catch (DeviceNotAvailableException e) {
888             // We wrap the exception from recovery if it fails to provide a clear message
889             throw new DeviceNotAvailableException(
890                     "Device became unavailable during fastboot 'flashall'. Please verify that "
891                             + "the image you are flashing can boot properly.",
892                     e,
893                     device.getSerialNumber());
894         }
895     }
896 
897     /**
898      * Flash the system image on device by using fuse-zip mounting with fastboot flashall command.
899      *
900      * @param device the {@link ITestDevice} to flash
901      * @param deviceBuild the {@link IDeviceBuildInfo} to flash
902      * @throws DeviceNotAvailableException if device is not available
903      * @throws TargetSetupError if fastboot command fails
904      */
flashWithFuseZip(ITestDevice device, IDeviceBuildInfo deviceBuild)905     private void flashWithFuseZip(ITestDevice device, IDeviceBuildInfo deviceBuild)
906             throws DeviceNotAvailableException, TargetSetupError {
907         FuseUtil fuseUtil = getFuseUtil();
908         File mountPoint = null;
909         Throwable exception = null;
910         try {
911             mountPoint = FileUtil.createTempDir("FlashAllMountPoint");
912             fuseUtil.mountZip(deviceBuild.getDeviceImageFile().getAbsoluteFile(), mountPoint);
913             Map<String, String> systemVarMap = new HashMap<>();
914             systemVarMap.put("ANDROID_PRODUCT_OUT", mountPoint.getAbsolutePath());
915             String[] fastbootArgs = buildFastbootCommand("flashall", mShouldFlashRamdisk);
916             executeLongFastbootCmd(device, systemVarMap, fastbootArgs);
917         } catch (DeviceNotAvailableException e) {
918             // We wrap the exception from recovery if it fails to provide a clear message
919             exception = e;
920             throw new DeviceNotAvailableException(
921                     "Device became unavailable during fastboot 'flashall'. Please verify that "
922                             + "the image you are flashing can boot properly.",
923                     e,
924                     device.getSerialNumber());
925         } catch (IOException e) {
926             exception = e;
927             throw new TargetSetupError(
928                     String.format(
929                             "Unable to create a temp dir for fuse zip to mount on, error: %s",
930                             e.getMessage()),
931                     InfraErrorIdentifier.FAIL_TO_CREATE_FILE);
932         } finally {
933             if (mountPoint != null) {
934                 fuseUtil.unmountZip(mountPoint);
935                 FileUtil.recursiveDelete(mountPoint);
936             }
937             // In case the unmount operation fails, deleting the mount point will fail as well.
938             if (mountPoint.exists()) {
939                 String mountErrorMsg =
940                         String.format(
941                                 "Failed to delete mount point %s, unmount operation might failed.",
942                                 mountPoint);
943                 if (exception != null) {
944                     // If a previous exception happened, surface the previous exception only
945                     CLog.e(mountErrorMsg);
946                 } else {
947                     throw new HarnessRuntimeException(
948                             mountErrorMsg, InfraErrorIdentifier.LAB_HOST_FILESYSTEM_ERROR);
949                 }
950             }
951         }
952     }
953 
954     /**
955      * Flash the system image on device by using fastboot update command.
956      *
957      * @param device the {@link ITestDevice} to flash
958      * @param deviceBuild the {@link IDeviceBuildInfo} to flash
959      * @throws DeviceNotAvailableException if device is not available
960      * @throws TargetSetupError if fastboot command fails
961      */
flashWithUpdateCommand(ITestDevice device, IDeviceBuildInfo deviceBuild)962     private void flashWithUpdateCommand(ITestDevice device, IDeviceBuildInfo deviceBuild)
963             throws DeviceNotAvailableException, TargetSetupError {
964         try {
965             executeLongFastbootCmd(
966                     device,
967                     buildFastbootCommand(
968                             "update",
969                             mShouldFlashRamdisk,
970                             deviceBuild.getDeviceImageFile().getAbsolutePath()));
971         } catch (DeviceNotAvailableException e) {
972             // We wrap the exception from recovery if it fails to provide a clear message
973             throw new DeviceNotAvailableException(
974                     "Device became unavailable during fastboot 'update'. Please verify that "
975                             + "the image you are flashing can boot properly.",
976                     e,
977                     device.getSerialNumber());
978         }
979     }
980 
getImageVersion(ITestDevice device, String imageName)981     protected String getImageVersion(ITestDevice device, String imageName)
982             throws DeviceNotAvailableException, TargetSetupError {
983         return fetchImageVersion(getRunUtil(), device, imageName);
984     }
985 
986     /**
987      * Helper method to get the current image version on device.
988      *
989      * @param device the {@link ITestDevice} to execute command on
990      * @param imageName the name of image to get.
991      * @return String the stdout output from command
992      * @throws DeviceNotAvailableException if device is not available
993      * @throws TargetSetupError if fastboot command fails or version could not be determined
994      */
fetchImageVersion(IRunUtil runUtil, ITestDevice device, String imageName)995     public static String fetchImageVersion(IRunUtil runUtil, ITestDevice device, String imageName)
996             throws DeviceNotAvailableException, TargetSetupError {
997         int attempts = 0;
998         String versionQuery = String.format("version-%s", imageName);
999         String patternString = String.format("%s:\\s(.*)\\s", versionQuery);
1000         Pattern versionOutputPattern = Pattern.compile(patternString);
1001 
1002         while (attempts < MAX_RETRY_ATTEMPTS) {
1003             CLog.v("Executing short fastboot command 'getvar %s'", versionQuery);
1004             CommandResult result = device.executeFastbootCommand("getvar", versionQuery);
1005             String queryOutput =
1006                     internalHandleFastbootResult(device, result, "getvar", versionQuery);
1007             Matcher matcher = versionOutputPattern.matcher(queryOutput);
1008             if (matcher.find()) {
1009                 return matcher.group(1);
1010             } else {
1011                 attempts++;
1012                 CLog.w(
1013                         "Could not find version for '%s'. Output '%s', retrying.",
1014                         imageName, queryOutput);
1015                 runUtil.sleep(
1016                         RETRY_SLEEP * (attempts - 1)
1017                                 + new Random(System.currentTimeMillis()).nextInt(RETRY_SLEEP));
1018                 continue;
1019             }
1020         }
1021         throw new TargetSetupError(String.format(
1022                 "Could not find version for '%s' after %d retry attempts", imageName, attempts),
1023                 device.getDeviceDescriptor());
1024     }
1025 
1026     /**
1027      * Helper method to retrieve the current slot (for A/B capable devices).
1028      *
1029      * @param device the {@link ITestDevice} to execute command on.
1030      * @return "a", "b" or null (if device is not A/B capable)
1031      * @throws DeviceNotAvailableException
1032      * @throws TargetSetupError
1033      */
getCurrentSlot(ITestDevice device)1034     protected String getCurrentSlot(ITestDevice device)
1035             throws DeviceNotAvailableException, TargetSetupError {
1036         Matcher matcher;
1037         if (device.getDeviceState().equals(TestDeviceState.FASTBOOT)) {
1038             String queryOutput = executeFastbootCmd(device, "getvar", SLOT_VAR);
1039             Pattern outputPattern = Pattern.compile(String.format("^%s: _?([ab])", SLOT_VAR));
1040             matcher = outputPattern.matcher(queryOutput);
1041         } else {
1042             String queryOutput = device.executeShellCommand(String.format("getprop %s", SLOT_PROP));
1043             Pattern outputPattern =
1044                     Pattern.compile(String.format("^\\[%s\\]: \\[_?([ab])\\]", SLOT_PROP));
1045             matcher = outputPattern.matcher(queryOutput);
1046         }
1047         if (matcher.find()) {
1048             return matcher.group(1);
1049         } else {
1050             return null;
1051         }
1052     }
1053 
1054     /** Exposed for testing. */
getRunUtil()1055     protected IRunUtil getRunUtil() {
1056         return RunUtil.getDefault();
1057     }
1058 
1059     /**
1060      * Helper method to execute fastboot command.
1061      *
1062      * @param device the {@link ITestDevice} to execute command on
1063      * @param cmdArgs the arguments to provide to fastboot
1064      * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some
1065      *     fastboot commands are weird in that they dump output to stderr on success case
1066      * @throws DeviceNotAvailableException if device is not available
1067      * @throws TargetSetupError if fastboot command fails
1068      */
executeFastbootCmd(ITestDevice device, String... cmdArgs)1069     protected String executeFastbootCmd(ITestDevice device, String... cmdArgs)
1070             throws DeviceNotAvailableException, TargetSetupError {
1071         CLog.v("Executing short fastboot command %s", java.util.Arrays.toString(cmdArgs));
1072         CommandResult result = device.executeFastbootCommand(cmdArgs);
1073         return handleFastbootResult(device, result, cmdArgs);
1074     }
1075 
1076     /**
1077      * Helper method to execute a long-running fastboot command.
1078      *
1079      * <p>Note: Most fastboot commands normally execute within the timeout allowed by {@link
1080      * ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are flashing
1081      * devices at once, fastboot commands can take much longer than normal.
1082      *
1083      * @param device the {@link ITestDevice} to execute command on
1084      * @param cmdArgs the arguments to provide to fastboot
1085      * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some
1086      *     fastboot commands are weird in that they dump output to stderr on success case
1087      * @throws DeviceNotAvailableException if device is not available
1088      * @throws TargetSetupError if fastboot command fails
1089      */
executeLongFastbootCmd(ITestDevice device, String... cmdArgs)1090     protected String executeLongFastbootCmd(ITestDevice device, String... cmdArgs)
1091             throws DeviceNotAvailableException, TargetSetupError {
1092         return executeLongFastbootCmd(device, new HashMap<>(), cmdArgs);
1093     }
1094 
1095     /**
1096      * Helper method to execute a long-running fastboot command with environment variables.
1097      *
1098      * <p>Note: Most fastboot commands normally execute within the timeout allowed by {@link
1099      * ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are flashing
1100      * devices at once, fastboot commands can take much longer than normal.
1101      *
1102      * @param device the {@link ITestDevice} to execute command on
1103      * @param envVarMap the map which carries environment variables which need to be set before
1104      *     running the fastboot command
1105      * @param cmdArgs the arguments to provide to fastboot
1106      * @return String the stderr output from command if non-empty. Otherwise returns the stdout Some
1107      *     fastboot commands are weird in that they dump output to stderr on success case
1108      * @throws DeviceNotAvailableException if device is not available
1109      * @throws TargetSetupError if fastboot command fails
1110      */
executeLongFastbootCmd( ITestDevice device, Map<String, String> envVarMap, String... cmdArgs)1111     protected String executeLongFastbootCmd(
1112             ITestDevice device, Map<String, String> envVarMap, String... cmdArgs)
1113             throws DeviceNotAvailableException, TargetSetupError {
1114         CommandResult result = device.executeLongFastbootCommand(envVarMap, cmdArgs);
1115         return handleFastbootResult(device, result, cmdArgs);
1116     }
1117 
1118     /**
1119      * Interpret the result of a fastboot command
1120      *
1121      * @param device
1122      * @param result
1123      * @param cmdArgs
1124      * @return the stderr output from command if non-empty. Otherwise returns the stdout
1125      * @throws TargetSetupError
1126      */
1127     @VisibleForTesting
handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)1128     String handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)
1129             throws TargetSetupError {
1130         try {
1131             String res = internalHandleFastbootResult(device, result, cmdArgs);
1132             mFbCmdStatus = CommandStatus.SUCCESS;
1133             return res;
1134         } catch (TargetSetupError e) {
1135             mFbCmdStatus = CommandStatus.FAILED;
1136             throw e;
1137         }
1138     }
1139 
internalHandleFastbootResult( ITestDevice device, CommandResult result, String... cmdArgs)1140     private static String internalHandleFastbootResult(
1141             ITestDevice device, CommandResult result, String... cmdArgs) throws TargetSetupError {
1142         CLog.v("fastboot stdout: " + result.getStdout());
1143         CLog.v("fastboot stderr: " + result.getStderr());
1144         CommandStatus status = result.getStatus();
1145         ErrorIdentifier errorIdentifier = null;
1146         boolean diskErrorIdentified = false;
1147         for (String diskError : DISK_SPACE_ERRORS) {
1148             if (result.getStderr().contains(diskError)) {
1149                 errorIdentifier = InfraErrorIdentifier.NO_DISK_SPACE;
1150                 status = CommandStatus.FAILED;
1151                 diskErrorIdentified = true;
1152                 break;
1153             }
1154         }
1155 
1156         if (!diskErrorIdentified && result.getStderr().contains("FAILED")) {
1157             // if output contains "FAILED", just override to failure
1158             status = CommandStatus.FAILED;
1159         }
1160         if (status != CommandStatus.SUCCESS) {
1161             if (errorIdentifier == null) {
1162                 errorIdentifier = DeviceErrorIdentifier.ERROR_AFTER_FLASHING;
1163             }
1164             throw new TargetSetupError(
1165                     String.format(
1166                             "fastboot command %s failed in device %s. stdout: %s, stderr: %s",
1167                             cmdArgs[0],
1168                             device.getSerialNumber(),
1169                             result.getStdout(),
1170                             result.getStderr()),
1171                     device.getDeviceDescriptor(),
1172                     errorIdentifier);
1173         }
1174         if (result.getStderr().length() > 0) {
1175             return result.getStderr();
1176         } else {
1177             return result.getStdout();
1178         }
1179     }
1180 
1181     /**
1182      * {@inheritDoc}
1183      */
1184     @Override
overrideDeviceOptions(ITestDevice device)1185     public void overrideDeviceOptions(ITestDevice device) {
1186         // ignore
1187     }
1188 
1189     /**
1190      * {@inheritDoc}
1191      */
1192     @Override
setForceSystemFlash(boolean forceSystemFlash)1193     public void setForceSystemFlash(boolean forceSystemFlash) {
1194         mForceSystemFlash = forceSystemFlash;
1195     }
1196 
1197     /**
1198      * {@inheritDoc}
1199      */
1200     @Override
setDataWipeSkipList(Collection<String> dataWipeSkipList)1201     public void setDataWipeSkipList(Collection<String> dataWipeSkipList) {
1202         if (dataWipeSkipList == null) {
1203             dataWipeSkipList = new ArrayList<String>();
1204         }
1205         if (dataWipeSkipList.isEmpty()) {
1206             // To maintain backwards compatibility.
1207             // TODO: deprecate and remove.
1208             dataWipeSkipList.add("media");
1209         }
1210         mDataWipeSkipList = dataWipeSkipList;
1211     }
1212 
1213     /**
1214      * {@inheritDoc}
1215      */
1216     @Override
setWipeTimeout(long timeout)1217     public void setWipeTimeout(long timeout) {
1218         mWipeTimeout = timeout;
1219     }
1220 
1221     /**
1222      * {@inheritDoc}
1223      */
1224     @Override
getSystemFlashingStatus()1225     public CommandStatus getSystemFlashingStatus() {
1226         return mSystemFlashStatus;
1227     }
1228 
1229     /** {@inheritDoc} */
1230     @Override
setShouldFlashRamdisk(boolean shouldFlashRamdisk)1231     public void setShouldFlashRamdisk(boolean shouldFlashRamdisk) {
1232         mShouldFlashRamdisk = shouldFlashRamdisk;
1233     }
1234 
1235     /** {@inheritDoc} */
1236     @Override
setRamdiskPartition(String ramdiskPartition)1237     public void setRamdiskPartition(String ramdiskPartition) {
1238         mRamdiskPartition = ramdiskPartition;
1239     }
1240 
1241     /** {@inheritDoc} */
1242     @Override
shouldFlashRamdisk()1243     public boolean shouldFlashRamdisk() {
1244         return mShouldFlashRamdisk;
1245     }
1246 
flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild)1247     protected void flashRamdiskIfNeeded(ITestDevice device, IDeviceBuildInfo deviceBuild)
1248             throws TargetSetupError, DeviceNotAvailableException {
1249         if (mShouldFlashRamdisk) {
1250             // Flash ramdisk in bootloader
1251             device.rebootIntoBootloader();
1252             executeLongFastbootCmd(
1253                     device,
1254                     "flash",
1255                     mRamdiskPartition,
1256                     deviceBuild.getRamdiskFile().getAbsolutePath());
1257             device.reboot();
1258         }
1259     }
1260 
setSystemBuildInfo(String systemBuildId, String systemBuildFlavor)1261     protected void setSystemBuildInfo(String systemBuildId, String systemBuildFlavor) {
1262         mSystemBuildId = systemBuildId;
1263         mSystemBuildFlavor = systemBuildFlavor;
1264     }
1265 
1266     /** Gets the {@link IHostOptions} instance to use. */
1267     @VisibleForTesting
getHostOptions()1268     IHostOptions getHostOptions() {
1269         return GlobalConfiguration.getInstance().getHostOptions();
1270     }
1271 }
1272