• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.targetprep;
18 
19 import com.android.tradefed.build.IDeviceBuildInfo;
20 import com.android.tradefed.command.remote.DeviceDescriptor;
21 import com.android.tradefed.device.DeviceNotAvailableException;
22 import com.android.tradefed.device.IManagedTestDevice;
23 import com.android.tradefed.device.ITestDevice;
24 import com.android.tradefed.device.TestDeviceState;
25 import com.android.tradefed.log.LogUtil.CLog;
26 import com.android.tradefed.util.CommandResult;
27 import com.android.tradefed.util.CommandStatus;
28 import com.android.tradefed.util.FileUtil;
29 import com.android.tradefed.util.IRunUtil;
30 import com.android.tradefed.util.RunUtil;
31 import com.android.tradefed.util.ZipUtil2;
32 
33 import org.apache.commons.compress.archivers.zip.ZipFile;
34 
35 import java.io.File;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.List;
41 import java.util.Random;
42 import java.util.regex.Matcher;
43 import java.util.regex.Pattern;
44 import java.util.stream.Collectors;
45 
46 /**
47  * A class that relies on fastboot to flash an image on physical Android hardware.
48  */
49 public class FastbootDeviceFlasher implements IDeviceFlasher  {
50     public static final String BASEBAND_IMAGE_NAME = "radio";
51 
52     private static final String FASTBOOT_VERSION = "fastboot_version";
53     private static final int MAX_RETRY_ATTEMPTS = 3;
54     private static final int RETRY_SLEEP = 2 * 1000; // 2s sleep between retries
55 
56     private static final String SLOT_PROP = "ro.boot.slot_suffix";
57     private static final String SLOT_VAR = "current-slot";
58 
59     private long mWipeTimeout = 4 * 60 * 1000;
60 
61     private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH;
62 
63     private IFlashingResourcesRetriever mResourceRetriever;
64 
65     private ITestsZipInstaller mTestsZipInstaller = null;
66 
67     private Collection<String> mFlashOptions = new ArrayList<>();
68 
69     private Collection<String> mDataWipeSkipList = null;
70 
71     private boolean mForceSystemFlash;
72 
73     private CommandStatus mFbCmdStatus;
74 
75     private CommandStatus mSystemFlashStatus;
76 
77     /**
78      * {@inheritDoc}
79      */
80     @Override
setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever)81     public void setFlashingResourcesRetriever(IFlashingResourcesRetriever retriever) {
82         mResourceRetriever = retriever;
83     }
84 
getFlashingResourcesRetriever()85     protected IFlashingResourcesRetriever getFlashingResourcesRetriever() {
86         return mResourceRetriever;
87     }
88 
89     /**
90      * {@inheritDoc}
91      */
92     @Override
setUserDataFlashOption(UserDataFlashOption flashOption)93     public void setUserDataFlashOption(UserDataFlashOption flashOption) {
94         mUserDataFlashOption = flashOption;
95     }
96 
97     /**
98      * {@inheritDoc}
99      */
100     @Override
getUserDataFlashOption()101     public UserDataFlashOption getUserDataFlashOption() {
102         return mUserDataFlashOption;
103     }
104 
setTestsZipInstaller(ITestsZipInstaller testsZipInstaller)105     void setTestsZipInstaller(ITestsZipInstaller testsZipInstaller) {
106         mTestsZipInstaller = testsZipInstaller;
107     }
108 
getTestsZipInstaller()109     ITestsZipInstaller getTestsZipInstaller() {
110         // Lazily initialize the TestZipInstaller.
111         if (mTestsZipInstaller == null) {
112             if (mDataWipeSkipList == null) {
113                 mDataWipeSkipList = new ArrayList<String> ();
114             }
115             if (mDataWipeSkipList.isEmpty()) {
116                 // To maintain backwards compatibility. Keep media by default.
117                 // TODO: deprecate and remove this.
118                 mDataWipeSkipList.add("media");
119             }
120             mTestsZipInstaller = new DefaultTestsZipInstaller(mDataWipeSkipList);
121         }
122         return mTestsZipInstaller;
123     }
124 
125     /**
126      * Sets a list of options to pass with flash/update commands.
127      *
128      * @param flashOptions
129      */
setFlashOptions(Collection<String> flashOptions)130     public void setFlashOptions(Collection<String> flashOptions) {
131         // HACK: To workaround TF's command line parsing, options starting with a dash
132         // needs to be prepended with a whitespace and trimmed before they are used.
133         mFlashOptions = flashOptions.stream().map(String::trim).collect(Collectors.toList());
134     }
135 
136     /**
137      * {@inheritDoc}
138      */
139     @Override
flash(ITestDevice device, IDeviceBuildInfo deviceBuild)140     public void flash(ITestDevice device, IDeviceBuildInfo deviceBuild) throws TargetSetupError,
141             DeviceNotAvailableException {
142 
143         CLog.i("Flashing device %s with build %s", device.getSerialNumber(),
144                 deviceBuild.getDeviceBuildId());
145 
146         // get system build id and build flavor before booting into fastboot
147         String systemBuildId = device.getBuildId();
148         String systemBuildFlavor = device.getBuildFlavor();
149 
150         device.rebootIntoBootloader();
151 
152         downloadFlashingResources(device, deviceBuild);
153         preFlashSetup(device, deviceBuild);
154         if (device instanceof IManagedTestDevice) {
155             String fastbootVersion = ((IManagedTestDevice) device).getFastbootVersion();
156             if (fastbootVersion != null) {
157                 deviceBuild.addBuildAttribute(FASTBOOT_VERSION, fastbootVersion);
158             }
159         }
160         handleUserDataFlashing(device, deviceBuild);
161         checkAndFlashBootloader(device, deviceBuild);
162         checkAndFlashBaseband(device, deviceBuild);
163         flashExtraImages(device, deviceBuild);
164         checkAndFlashSystem(device, systemBuildId, systemBuildFlavor, deviceBuild);
165     }
166 
buildFastbootCommand(String action, String... args)167     private String[] buildFastbootCommand(String action, String... args) {
168         List<String> cmdArgs = new ArrayList<>();
169         if ("flash".equals(action) || "update".equals(action)) {
170             cmdArgs.addAll(mFlashOptions);
171         }
172         cmdArgs.add(action);
173         cmdArgs.addAll(Arrays.asList(args));
174         return cmdArgs.toArray(new String[cmdArgs.size()]);
175     }
176 
177     /**
178      * Perform any additional pre-flashing setup required. No-op unless overridden.
179      *
180      * @param device the {@link ITestDevice} to prepare
181      * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files
182      * @throws DeviceNotAvailableException
183      * @throws TargetSetupError
184      */
preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)185     protected void preFlashSetup(ITestDevice device, IDeviceBuildInfo deviceBuild)
186             throws DeviceNotAvailableException, TargetSetupError {}
187 
188     /**
189      * Handle flashing of userdata/cache partition
190      *
191      * @param device the {@link ITestDevice} to flash
192      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
193      * @throws DeviceNotAvailableException
194      * @throws TargetSetupError
195      */
handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)196     protected void handleUserDataFlashing(ITestDevice device, IDeviceBuildInfo deviceBuild)
197             throws DeviceNotAvailableException, TargetSetupError {
198         if (UserDataFlashOption.FORCE_WIPE.equals(mUserDataFlashOption) ||
199                 UserDataFlashOption.WIPE.equals(mUserDataFlashOption)) {
200             CommandResult result = device.executeFastbootCommand(mWipeTimeout, "-w");
201             handleFastbootResult(device, result, "-w");
202         } else {
203             flashUserData(device, deviceBuild);
204             wipeCache(device);
205         }
206     }
207 
208     /**
209      * Flash an individual partition of a device
210      *
211      * @param device the {@link ITestDevice} to flash
212      * @param imgFile a {@link File} pointing to the image to be flashed
213      * @param partition the name of the partition to be flashed
214      */
flashPartition(ITestDevice device, File imgFile, String partition)215     protected void flashPartition(ITestDevice device, File imgFile, String partition)
216             throws DeviceNotAvailableException, TargetSetupError {
217         CLog.d("fastboot flash %s %s", partition, imgFile.getAbsolutePath());
218         executeLongFastbootCmd(
219                 device, buildFastbootCommand("flash", partition, imgFile.getAbsolutePath()));
220     }
221 
222     /**
223      * Wipe the specified partition with `fastboot erase &lt;name&gt;`
224      *
225      * @param device the {@link ITestDevice} to operate on
226      * @param partition the name of the partition to be wiped
227      */
wipePartition(ITestDevice device, String partition)228     protected void wipePartition(ITestDevice device, String partition)
229             throws DeviceNotAvailableException, TargetSetupError {
230         String wipeMethod = device.getUseFastbootErase() ? "erase" : "format";
231         CLog.d("fastboot %s %s", wipeMethod, partition);
232         CommandResult result = device.fastbootWipePartition(partition);
233         handleFastbootResult(device, result, wipeMethod, partition);
234     }
235 
236     /**
237      * Checks with the bootloader if the specified partition exists or not
238      *
239      * @param device the {@link ITestDevice} to operate on
240      * @param partition the name of the partition to be checked
241      */
hasPartition(ITestDevice device, String partition)242     protected boolean hasPartition(ITestDevice device, String partition)
243             throws DeviceNotAvailableException {
244         String partitionType = String.format("partition-type:%s", partition);
245         CommandResult result = device.executeFastbootCommand("getvar", partitionType);
246         if (!CommandStatus.SUCCESS.equals(result.getStatus())
247                 || result.getStderr().contains("FAILED")) {
248             return false;
249         }
250         Pattern regex = Pattern.compile(String.format("^%s:\\s*\\S+$", partitionType),
251                 Pattern.MULTILINE);
252         return regex.matcher(result.getStderr()).find();
253     }
254 
255     /**
256      * Downloads extra flashing image files needed
257      *
258      * @param device the {@link ITestDevice} to download resources for
259      * @param localBuild the {@link IDeviceBuildInfo} to populate. Assumes device image file is
260      * already set
261      *
262      * @throws DeviceNotAvailableException if device is not available
263      * @throws TargetSetupError if failed to retrieve resources
264      */
downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)265     protected void downloadFlashingResources(ITestDevice device, IDeviceBuildInfo localBuild)
266             throws TargetSetupError, DeviceNotAvailableException {
267         IFlashingResourcesParser resourceParser = createFlashingResourcesParser(localBuild,
268                 device.getDeviceDescriptor());
269 
270         if (resourceParser.getRequiredBoards() == null) {
271             throw new TargetSetupError(String.format("Build %s is missing required board info.",
272                     localBuild.getDeviceBuildId()), device.getDeviceDescriptor());
273         }
274         String deviceProductType = device.getProductType();
275         if (deviceProductType == null) {
276             // treat this as a fatal device error
277             throw new DeviceNotAvailableException(String.format(
278                     "Could not determine product type for device %s", device.getSerialNumber()),
279                     device.getSerialNumber());
280         }
281         verifyRequiredBoards(device, resourceParser, deviceProductType);
282 
283         String bootloaderVersion = resourceParser.getRequiredBootloaderVersion();
284         // only set bootloader image if this build doesn't have one already
285         // TODO: move this logic to the BuildProvider step
286         if (bootloaderVersion != null && localBuild.getBootloaderImageFile() == null) {
287            localBuild.setBootloaderImageFile(getFlashingResourcesRetriever().retrieveFile(
288                    getBootloaderFilePrefix(device), bootloaderVersion), bootloaderVersion);
289         }
290         String basebandVersion = resourceParser.getRequiredBasebandVersion();
291         // only set baseband image if this build doesn't have one already
292         if (basebandVersion != null && localBuild.getBasebandImageFile() == null) {
293             localBuild.setBasebandImage(getFlashingResourcesRetriever().retrieveFile(
294                     BASEBAND_IMAGE_NAME, basebandVersion), basebandVersion);
295         }
296         downloadExtraImageFiles(resourceParser, getFlashingResourcesRetriever(), localBuild);
297     }
298 
299     /**
300      * Verify that the device's product type supports the build-to-be-flashed.
301      * <p/>
302      * The base implementation will verify that the deviceProductType is included in the
303      * {@link IFlashingResourcesParser#getRequiredBoards()} collection. Subclasses may override
304      * as desired.
305      *
306      * @param device the {@link ITestDevice} to be flashed
307      * @param resourceParser the {@link IFlashingResourcesParser}
308      * @param deviceProductType the <var>device</var>'s product type
309      * @throws TargetSetupError if the build's required board info did not match the device
310      */
verifyRequiredBoards(ITestDevice device, IFlashingResourcesParser resourceParser, String deviceProductType)311     protected void verifyRequiredBoards(ITestDevice device, IFlashingResourcesParser resourceParser,
312             String deviceProductType) throws TargetSetupError {
313         if (!containsIgnoreCase(resourceParser.getRequiredBoards(), deviceProductType)) {
314             throw new TargetSetupError(String.format("Device %s is %s. Expected %s",
315                     device.getSerialNumber(), deviceProductType,
316                     resourceParser.getRequiredBoards()), device.getDeviceDescriptor());
317         }
318     }
319 
containsIgnoreCase(Collection<String> stringList, String anotherString)320     private static boolean containsIgnoreCase(Collection<String> stringList, String anotherString) {
321         for (String aString : stringList) {
322             if (aString != null && aString.equalsIgnoreCase(anotherString)) {
323                 return true;
324             }
325         }
326         return false;
327     }
328 
329     /**
330      * Hook to allow subclasses to download extra custom image files if needed.
331      *
332      * @param resourceParser the {@link IFlashingResourcesParser}
333      * @param retriever the {@link IFlashingResourcesRetriever}
334      * @param localBuild the {@link IDeviceBuildInfo}
335      * @throws TargetSetupError
336      */
downloadExtraImageFiles(IFlashingResourcesParser resourceParser, IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)337     protected void downloadExtraImageFiles(IFlashingResourcesParser resourceParser,
338             IFlashingResourcesRetriever retriever, IDeviceBuildInfo localBuild)
339             throws TargetSetupError {
340     }
341 
342     /**
343      * Factory method for creating a {@link IFlashingResourcesParser}.
344      * <p/>
345      * Exposed for unit testing.
346      *
347      * @param localBuild the {@link IDeviceBuildInfo} to parse
348      * @param descriptor the descriptor of the device being flashed.
349      * @return a {@link IFlashingResourcesParser} created by the factory method.
350      * @throws TargetSetupError
351      */
createFlashingResourcesParser(IDeviceBuildInfo localBuild, DeviceDescriptor descriptor)352     protected IFlashingResourcesParser createFlashingResourcesParser(IDeviceBuildInfo localBuild,
353             DeviceDescriptor descriptor) throws TargetSetupError {
354         try {
355             return new FlashingResourcesParser(localBuild.getDeviceImageFile());
356         } catch (TargetSetupError e) {
357             // Rethrow with descriptor since FlashingResourceParser doesn't have it.
358             throw new TargetSetupError(e.getMessage(), e, descriptor);
359         }
360     }
361 
362     /**
363      * If needed, flash the bootloader image on device.
364      * <p/>
365      * Will only flash bootloader if current version on device != required version.
366      *
367      * @param device the {@link ITestDevice} to flash
368      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the bootloader image to flash
369      * @return <code>true</code> if bootloader was flashed, <code>false</code> if it was skipped
370      * @throws DeviceNotAvailableException if device is not available
371      * @throws TargetSetupError if failed to flash bootloader
372      */
checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)373     protected boolean checkAndFlashBootloader(ITestDevice device, IDeviceBuildInfo deviceBuild)
374             throws DeviceNotAvailableException, TargetSetupError {
375         String currentBootloaderVersion = getImageVersion(device, "bootloader");
376         if (deviceBuild.getBootloaderVersion() != null &&
377                 !deviceBuild.getBootloaderVersion().equals(currentBootloaderVersion)) {
378             CLog.i("Flashing bootloader %s", deviceBuild.getBootloaderVersion());
379             flashBootloader(device, deviceBuild.getBootloaderImageFile());
380             return true;
381         } else {
382             CLog.i("Bootloader is already version %s, skipping flashing", currentBootloaderVersion);
383             return false;
384         }
385     }
386 
387     /**
388      * Flashes the given bootloader image and reboots back into bootloader
389      *
390      * @param device the {@link ITestDevice} to flash
391      * @param bootloaderImageFile the bootloader image {@link File}
392      * @throws DeviceNotAvailableException if device is not available
393      * @throws TargetSetupError if failed to flash
394      */
flashBootloader(ITestDevice device, File bootloaderImageFile)395     protected void flashBootloader(ITestDevice device, File bootloaderImageFile)
396             throws DeviceNotAvailableException, TargetSetupError {
397         // bootloader images are small, and flash quickly. so use the 'normal' timeout
398         executeFastbootCmd(
399                 device,
400                 buildFastbootCommand(
401                         "flash", getBootPartitionName(), bootloaderImageFile.getAbsolutePath()));
402         device.rebootIntoBootloader();
403     }
404 
405     /**
406      * Get the boot partition name for this device flasher.
407      * <p/>
408      * Defaults to 'hboot'. Subclasses should override if necessary.
409      */
getBootPartitionName()410     protected String getBootPartitionName() {
411         return "hboot";
412     }
413 
414     /**
415      * Get the bootloader file prefix.
416      * <p/>
417      * Defaults to {@link #getBootPartitionName()}. Subclasses should override if necessary.
418      *
419      * @param device the {@link ITestDevice} to flash
420      * @throws DeviceNotAvailableException if device is not available
421      * @throws TargetSetupError if failed to get prefix
422      */
getBootloaderFilePrefix(ITestDevice device)423     protected String getBootloaderFilePrefix(ITestDevice device) throws TargetSetupError,
424             DeviceNotAvailableException {
425         return getBootPartitionName();
426     }
427 
428     /**
429      * If needed, flash the baseband image on device. Will only flash baseband if current version
430      * on device != required version
431      *
432      * @param device the {@link ITestDevice} to flash
433      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to flash
434      * @throws DeviceNotAvailableException if device is not available
435      * @throws TargetSetupError if failed to flash baseband
436      */
checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)437     protected void checkAndFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)
438             throws DeviceNotAvailableException, TargetSetupError {
439         String currentBasebandVersion = getImageVersion(device, "baseband");
440         if (checkShouldFlashBaseband(device, deviceBuild)) {
441             CLog.i("Flashing baseband %s", deviceBuild.getBasebandVersion());
442             flashBaseband(device, deviceBuild.getBasebandImageFile());
443         } else {
444             CLog.i("Baseband is already version %s, skipping flashing", currentBasebandVersion);
445         }
446     }
447 
448     /**
449      * Check if the baseband on the provided device needs to be flashed.
450      *
451      * @param device the {@link ITestDevice} to check
452      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the baseband image to check
453      * @throws DeviceNotAvailableException if device is not available
454      * @throws TargetSetupError if failed to flash baseband
455      */
checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)456     protected boolean checkShouldFlashBaseband(ITestDevice device, IDeviceBuildInfo deviceBuild)
457             throws DeviceNotAvailableException, TargetSetupError {
458         String currentBasebandVersion = getImageVersion(device, "baseband");
459         return (deviceBuild.getBasebandVersion() != null &&
460                 !deviceBuild.getBasebandVersion().equals(currentBasebandVersion));
461     }
462 
463     /**
464      * Flashes the given baseband image and reboot back into bootloader
465      *
466      * @param device the {@link ITestDevice} to flash
467      * @param basebandImageFile the baseband image {@link File}
468      * @throws DeviceNotAvailableException if device is not available
469      * @throws TargetSetupError if failed to flash baseband
470      */
flashBaseband(ITestDevice device, File basebandImageFile)471     protected void flashBaseband(ITestDevice device, File basebandImageFile)
472             throws DeviceNotAvailableException, TargetSetupError {
473         flashPartition(device, basebandImageFile, BASEBAND_IMAGE_NAME);
474         device.rebootIntoBootloader();
475     }
476 
477     /**
478      * Wipe the cache partition on device.
479      *
480      * @param device the {@link ITestDevice} to flash
481      * @throws DeviceNotAvailableException if device is not available
482      * @throws TargetSetupError if failed to flash cache
483      */
wipeCache(ITestDevice device)484     protected void wipeCache(ITestDevice device) throws DeviceNotAvailableException,
485             TargetSetupError {
486         // only wipe cache if user data is being wiped
487         if (!mUserDataFlashOption.equals(UserDataFlashOption.RETAIN)) {
488             CLog.i("Wiping cache on %s", device.getSerialNumber());
489             String partition = "cache";
490             if (hasPartition(device, partition)) {
491                 wipePartition(device, partition);
492             }
493         } else {
494             CLog.d("Skipping cache wipe on %s", device.getSerialNumber());
495         }
496     }
497 
498     /**
499      * Flash userdata partition on device.
500      *
501      * @param device the {@link ITestDevice} to flash
502      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
503      * @throws DeviceNotAvailableException if device is not available
504      * @throws TargetSetupError if failed to flash user data
505      */
flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)506     protected void flashUserData(ITestDevice device, IDeviceBuildInfo deviceBuild)
507             throws DeviceNotAvailableException, TargetSetupError {
508         switch (mUserDataFlashOption) {
509             case FLASH:
510                 CLog.i("Flashing %s with userdata %s", device.getSerialNumber(),
511                         deviceBuild.getUserDataImageFile().getAbsolutePath());
512                 flashPartition(device, deviceBuild.getUserDataImageFile(), "userdata");
513                 break;
514             case FLASH_IMG_ZIP:
515                 flashUserDataFromDeviceImageFile(device, deviceBuild);
516                 break;
517             case FORCE_WIPE:  // intentional fallthrough
518             case WIPE:
519                 CLog.i("Wiping userdata %s", device.getSerialNumber());
520                 wipePartition(device, "userdata");
521                 break;
522 
523             case TESTS_ZIP:
524                 device.rebootUntilOnline(); // required to install tests
525                 if (device.isEncryptionSupported() && device.isDeviceEncrypted()) {
526                     device.unlockDevice();
527                 }
528                 getTestsZipInstaller().pushTestsZipOntoData(device, deviceBuild);
529                 // Reboot into bootloader to continue the flashing process
530                 device.rebootIntoBootloader();
531                 break;
532 
533             case WIPE_RM:
534                 device.rebootUntilOnline(); // required to install tests
535                 getTestsZipInstaller().deleteData(device);
536                 // Reboot into bootloader to continue the flashing process
537                 device.rebootIntoBootloader();
538                 break;
539 
540             default:
541                 CLog.d("Skipping userdata flash for %s", device.getSerialNumber());
542         }
543     }
544 
545     /**
546      * Extracts the userdata.img from device image file and flashes it onto device
547      * @param device the {@link ITestDevice} to flash
548      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the files to flash
549      * @throws DeviceNotAvailableException if device is not available
550      * @throws TargetSetupError if failed to extract or flash user data
551      */
flashUserDataFromDeviceImageFile(ITestDevice device, IDeviceBuildInfo deviceBuild)552     protected void flashUserDataFromDeviceImageFile(ITestDevice device,
553             IDeviceBuildInfo deviceBuild) throws DeviceNotAvailableException, TargetSetupError {
554         File userdataImg = null;
555         try {
556             try (ZipFile zip = new ZipFile(deviceBuild.getDeviceImageFile())) {
557                 userdataImg = ZipUtil2.extractFileFromZip(zip, "userdata.img");
558             } catch (IOException ioe) {
559                 throw new TargetSetupError("failed to extract userdata.img from image file", ioe,
560                         device.getDeviceDescriptor());
561             }
562             CLog.i("Flashing %s with userdata %s", device.getSerialNumber(), userdataImg);
563             flashPartition(device, userdataImg, "userdata");
564         } finally {
565             FileUtil.deleteFile(userdataImg);
566         }
567     }
568 
569     /**
570      * Flash any device specific partitions before flashing system and rebooting. No-op unless
571      * overridden.
572      *
573      * @param device the {@link ITestDevice} to flash
574      * @param deviceBuild the {@link IDeviceBuildInfo} containing the build files
575      * @throws DeviceNotAvailableException
576      * @throws TargetSetupError
577      */
flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)578     protected void flashExtraImages(ITestDevice device, IDeviceBuildInfo deviceBuild)
579             throws DeviceNotAvailableException, TargetSetupError {}
580 
581     /**
582      * If needed, flash the system image on device.
583      *
584      * <p>Please look at {@link #shouldFlashSystem(String, String, IDeviceBuildInfo)}
585      *
586      * <p>Regardless of path chosen, after method execution device should be booting into userspace.
587      *
588      * @param device the {@link ITestDevice} to flash
589      * @param systemBuildId the current build id running on the device
590      * @param systemBuildFlavor the current build flavor running on the device
591      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash
592      * @return <code>true</code> if system was flashed, <code>false</code> if it was skipped
593      * @throws DeviceNotAvailableException if device is not available
594      * @throws TargetSetupError if failed to flash bootloader
595      */
checkAndFlashSystem( ITestDevice device, String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)596     protected boolean checkAndFlashSystem(
597             ITestDevice device,
598             String systemBuildId,
599             String systemBuildFlavor,
600             IDeviceBuildInfo deviceBuild)
601             throws DeviceNotAvailableException, TargetSetupError {
602        if (shouldFlashSystem(systemBuildId, systemBuildFlavor, deviceBuild)) {
603             CLog.i("Flashing system %s", deviceBuild.getDeviceBuildId());
604             flashSystem(device, deviceBuild);
605             return true;
606        }
607        CLog.i("System is already version %s and build flavor %s, skipping flashing",
608                systemBuildId, systemBuildFlavor);
609        // reboot
610        device.rebootUntilOnline();
611        return false;
612     }
613 
614     /**
615      * Helper method used to determine if we need to flash the system image.
616      *
617      * @param systemBuildId the current build id running on the device
618      * @param systemBuildFlavor the current build flavor running on the device
619      * @param deviceBuild the {@link IDeviceBuildInfo} that contains the system image to flash
620      * @return <code>true</code> if we should flash the system, <code>false</code> otherwise.
621      */
shouldFlashSystem(String systemBuildId, String systemBuildFlavor, IDeviceBuildInfo deviceBuild)622     boolean shouldFlashSystem(String systemBuildId, String systemBuildFlavor,
623             IDeviceBuildInfo deviceBuild) {
624         if (mForceSystemFlash) {
625             // Flag overrides all logic.
626             return true;
627         }
628         // Err on the side of caution, if we failed to get the build id or build flavor, force a
629         // flash of the system.
630         if (systemBuildFlavor == null || systemBuildId == null) {
631             return true;
632         }
633         // If we have the same build id and build flavor we don't need to flash it.
634         if (systemBuildId.equals(deviceBuild.getDeviceBuildId()) &&
635                 systemBuildFlavor.equalsIgnoreCase(deviceBuild.getBuildFlavor())) {
636             return false;
637         }
638         return true;
639     }
640 
641     /**
642      * Flash the system image on device.
643      *
644      * @param device the {@link ITestDevice} to flash
645      * @param deviceBuild the {@link IDeviceBuildInfo} to flash
646      * @throws DeviceNotAvailableException if device is not available
647      * @throws TargetSetupError if fastboot command fails
648      */
flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)649     protected void flashSystem(ITestDevice device, IDeviceBuildInfo deviceBuild)
650             throws DeviceNotAvailableException, TargetSetupError {
651         CLog.i("Flashing %s with update %s", device.getSerialNumber(),
652                 deviceBuild.getDeviceImageFile().getAbsolutePath());
653         // give extra time to the update cmd
654         try {
655             executeLongFastbootCmd(
656                     device,
657                     buildFastbootCommand(
658                             "update", deviceBuild.getDeviceImageFile().getAbsolutePath()));
659             // only transfer last fastboot command status over to system flash status after having
660             // flashing the system partitions
661             mSystemFlashStatus = mFbCmdStatus;
662         } finally {
663             // if system flash status is still null here, an exception has happened
664             if (mSystemFlashStatus == null) {
665                 mSystemFlashStatus = CommandStatus.EXCEPTION;
666             }
667         }
668     }
669 
670     /**
671      * Helper method to get the current image version on device.
672      *
673      * @param device the {@link ITestDevice} to execute command on
674      * @param imageName the name of image to get.
675      * @return String the stdout output from command
676      * @throws DeviceNotAvailableException if device is not available
677      * @throws TargetSetupError if fastboot command fails or version could not be determined
678      */
getImageVersion(ITestDevice device, String imageName)679     protected String getImageVersion(ITestDevice device, String imageName)
680             throws DeviceNotAvailableException, TargetSetupError {
681         int attempts = 0;
682         String versionQuery = String.format("version-%s", imageName);
683         String patternString = String.format("%s:\\s(.*)\\s", versionQuery);
684         Pattern versionOutputPattern = Pattern.compile(patternString);
685 
686         while (attempts < MAX_RETRY_ATTEMPTS) {
687             String queryOutput = executeFastbootCmd(device, "getvar", versionQuery);
688             Matcher matcher = versionOutputPattern.matcher(queryOutput);
689             if (matcher.find()) {
690                 return matcher.group(1);
691             } else {
692                 attempts++;
693                 CLog.w("Could not find version for '%s'. Output '%s', retrying.",
694                             imageName, queryOutput);
695                 getRunUtil().sleep(RETRY_SLEEP * (attempts - 1)
696                         + new Random(System.currentTimeMillis()).nextInt(RETRY_SLEEP));
697                 continue;
698             }
699         }
700         throw new TargetSetupError(String.format(
701                 "Could not find version for '%s' after %d retry attempts", imageName, attempts),
702                 device.getDeviceDescriptor());
703     }
704 
705     /**
706      * Helper method to retrieve the current slot (for A/B capable devices).
707      *
708      * @param device the {@link ITestDevice} to execute command on.
709      * @return "a", "b" or null (if device is not A/B capable)
710      * @throws DeviceNotAvailableException
711      * @throws TargetSetupError
712      */
getCurrentSlot(ITestDevice device)713     protected String getCurrentSlot(ITestDevice device)
714             throws DeviceNotAvailableException, TargetSetupError {
715         Matcher matcher;
716         if (device.getDeviceState().equals(TestDeviceState.FASTBOOT)) {
717             String queryOutput = executeFastbootCmd(device, "getvar", SLOT_VAR);
718             Pattern outputPattern = Pattern.compile(String.format("^%s: _?([ab])", SLOT_VAR));
719             matcher = outputPattern.matcher(queryOutput);
720         } else {
721             String queryOutput = device.executeShellCommand(String.format("getprop %s", SLOT_PROP));
722             Pattern outputPattern =
723                     Pattern.compile(String.format("^\\[%s\\]: \\[_?([ab])\\]", SLOT_PROP));
724             matcher = outputPattern.matcher(queryOutput);
725         }
726         if (matcher.find()) {
727             return matcher.group(1);
728         } else {
729             return null;
730         }
731     }
732 
733     /** Exposed for testing. */
getRunUtil()734     protected IRunUtil getRunUtil() {
735         return RunUtil.getDefault();
736     }
737 
738     /**
739      * Helper method to execute fastboot command.
740      *
741      * @param device the {@link ITestDevice} to execute command on
742      * @param cmdArgs the arguments to provide to fastboot
743      * @return String the stderr output from command if non-empty. Otherwise returns the stdout
744      * Some fastboot commands are weird in that they dump output to stderr on success case
745      *
746      * @throws DeviceNotAvailableException if device is not available
747      * @throws TargetSetupError if fastboot command fails
748      */
executeFastbootCmd(ITestDevice device, String... cmdArgs)749     protected String executeFastbootCmd(ITestDevice device, String... cmdArgs)
750             throws DeviceNotAvailableException, TargetSetupError {
751         CLog.v("Executing short fastboot command %s", java.util.Arrays.toString(cmdArgs));
752         CommandResult result = device.executeFastbootCommand(cmdArgs);
753         return handleFastbootResult(device, result, cmdArgs);
754     }
755 
756     /**
757      * Helper method to execute a long-running fastboot command.
758      * <p/>
759      * Note: Most fastboot commands normally execute within the timeout allowed by
760      * {@link ITestDevice#executeFastbootCommand(String...)}. However, when multiple devices are
761      * flashing devices at once, fastboot commands can take much longer than normal.
762      *
763      * @param device the {@link ITestDevice} to execute command on
764      * @param cmdArgs the arguments to provide to fastboot
765      * @return String the stderr output from command if non-empty. Otherwise returns the stdout
766      * Some fastboot commands are weird in that they dump output to stderr on success case
767      *
768      * @throws DeviceNotAvailableException if device is not available
769      * @throws TargetSetupError if fastboot command fails
770      */
executeLongFastbootCmd(ITestDevice device, String... cmdArgs)771     protected String executeLongFastbootCmd(ITestDevice device, String... cmdArgs)
772             throws DeviceNotAvailableException, TargetSetupError {
773         CommandResult result = device.executeLongFastbootCommand(cmdArgs);
774         return handleFastbootResult(device, result, cmdArgs);
775     }
776 
777     /**
778      * Interpret the result of a fastboot command
779      *
780      * @param device
781      * @param result
782      * @param cmdArgs
783      * @return the stderr output from command if non-empty. Otherwise returns the stdout
784      * @throws TargetSetupError
785      */
handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)786     private String handleFastbootResult(ITestDevice device, CommandResult result, String... cmdArgs)
787             throws TargetSetupError {
788         CLog.v("fastboot stdout: " + result.getStdout());
789         CLog.v("fastboot stderr: " + result.getStderr());
790         mFbCmdStatus = result.getStatus();
791         if (result.getStderr().contains("FAILED")) {
792             // if output contains "FAILED", just override to failure
793             mFbCmdStatus = CommandStatus.FAILED;
794         }
795         if (mFbCmdStatus != CommandStatus.SUCCESS) {
796             throw new TargetSetupError(String.format(
797                     "fastboot command %s failed in device %s. stdout: %s, stderr: %s", cmdArgs[0],
798                     device.getSerialNumber(), result.getStdout(), result.getStderr()),
799                     device.getDeviceDescriptor());
800         }
801         if (result.getStderr().length() > 0) {
802             return result.getStderr();
803         } else {
804             return result.getStdout();
805         }
806     }
807 
808     /**
809      * {@inheritDoc}
810      */
811     @Override
overrideDeviceOptions(ITestDevice device)812     public void overrideDeviceOptions(ITestDevice device) {
813         // ignore
814     }
815 
816     /**
817      * {@inheritDoc}
818      */
819     @Override
setForceSystemFlash(boolean forceSystemFlash)820     public void setForceSystemFlash(boolean forceSystemFlash) {
821         mForceSystemFlash = forceSystemFlash;
822     }
823 
824     /**
825      * {@inheritDoc}
826      */
827     @Override
setDataWipeSkipList(Collection<String> dataWipeSkipList)828     public void setDataWipeSkipList(Collection<String> dataWipeSkipList) {
829         if (dataWipeSkipList == null) {
830             dataWipeSkipList = new ArrayList<String> ();
831         }
832         if(dataWipeSkipList.isEmpty()) {
833             // To maintain backwards compatibility.
834             // TODO: deprecate and remove.
835             dataWipeSkipList.add("media");
836         }
837         mDataWipeSkipList = dataWipeSkipList;
838     }
839 
840     /**
841      * {@inheritDoc}
842      */
843     @Override
setWipeTimeout(long timeout)844     public void setWipeTimeout(long timeout) {
845         mWipeTimeout = timeout;
846     }
847 
848     /**
849      * {@inheritDoc}
850      */
851     @Override
getSystemFlashingStatus()852     public CommandStatus getSystemFlashingStatus() {
853         return mSystemFlashStatus;
854     }
855 }
856