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