1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.targetprep;
18 
19 import com.android.annotations.VisibleForTesting;
20 import com.android.tradefed.build.IBuildInfo;
21 import com.android.tradefed.build.IDeviceBuildInfo;
22 import com.android.tradefed.config.GlobalConfiguration;
23 import com.android.tradefed.config.IConfiguration;
24 import com.android.tradefed.config.IConfigurationReceiver;
25 import com.android.tradefed.config.IDeviceConfiguration;
26 import com.android.tradefed.config.Option;
27 import com.android.tradefed.device.DeviceNotAvailableException;
28 import com.android.tradefed.device.ITestDevice;
29 import com.android.tradefed.device.ITestDevice.RecoveryMode;
30 import com.android.tradefed.device.NullDevice;
31 import com.android.tradefed.device.SnapuserdWaitPhase;
32 import com.android.tradefed.device.TestDeviceState;
33 import com.android.tradefed.error.HarnessRuntimeException;
34 import com.android.tradefed.host.IHostOptions;
35 import com.android.tradefed.host.IHostOptions.PermitLimitType;
36 import com.android.tradefed.invoker.TestInformation;
37 import com.android.tradefed.invoker.logger.CurrentInvocation.IsolationGrade;
38 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
39 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
40 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
41 import com.android.tradefed.log.LogUtil.CLog;
42 import com.android.tradefed.result.error.DeviceErrorIdentifier;
43 import com.android.tradefed.result.error.InfraErrorIdentifier;
44 import com.android.tradefed.retry.BaseRetryDecision;
45 import com.android.tradefed.targetprep.IDeviceFlasher.UserDataFlashOption;
46 import com.android.tradefed.util.CommandResult;
47 import com.android.tradefed.util.CommandStatus;
48 import com.android.tradefed.util.FileUtil;
49 import com.android.tradefed.util.IRunUtil;
50 import com.android.tradefed.util.RunUtil;
51 import com.android.tradefed.util.image.DeviceImageTracker;
52 import com.android.tradefed.util.image.IncrementalImageUtil;
53 
54 import java.io.File;
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.concurrent.TimeUnit;
58 
59 /** A {@link ITargetPreparer} that flashes an image on physical Android hardware. */
60 public abstract class DeviceFlashPreparer extends BaseTargetPreparer
61         implements IConfigurationReceiver {
62 
63     private static final int BOOT_POLL_TIME_MS = 5 * 1000;
64     private static final long SNAPSHOT_CANCEL_TIMEOUT = 20000L;
65 
66     @Option(
67         name = "device-boot-time",
68         description = "max time to wait for device to boot.",
69         isTimeVal = true
70     )
71     private long mDeviceBootTime = 5 * 60 * 1000;
72 
73     @Option(name = "userdata-flash", description =
74         "specify handling of userdata partition.")
75     private UserDataFlashOption mUserDataFlashOption = UserDataFlashOption.FLASH;
76 
77     @Option(name = "force-system-flash", description =
78         "specify if system should always be flashed even if already running desired build.")
79     private boolean mForceSystemFlash = false;
80 
81     /*
82      * A temporary workaround for special builds. Should be removed after changes from build team.
83      * Bug: 18078421
84      */
85     @Deprecated
86     @Option(
87             name = "skip-post-flash-flavor-check",
88             description = "specify if system flavor should not be checked after flash")
89     private boolean mSkipPostFlashFlavorCheck = false;
90 
91     /*
92      * Used for update testing
93      */
94     @Option(name = "skip-post-flash-build-id-check", description =
95             "specify if build ID should not be checked after flash")
96     private boolean mSkipPostFlashBuildIdCheck = false;
97 
98     @Option(name = "wipe-skip-list", description =
99         "list of /data subdirectories to NOT wipe when doing UserDataFlashOption.TESTS_ZIP")
100     private Collection<String> mDataWipeSkipList = new ArrayList<>();
101 
102     /**
103      * @deprecated use host-options:concurrent-flasher-limit.
104      */
105     @Deprecated
106     @Option(name = "concurrent-flasher-limit", description =
107         "No-op, do not use. Left for backwards compatibility.")
108     private Integer mConcurrentFlasherLimit = null;
109 
110     @Option(name = "skip-post-flashing-setup",
111             description = "whether or not to skip post-flashing setup steps")
112     private boolean mSkipPostFlashingSetup = false;
113 
114     @Option(name = "wipe-timeout",
115             description = "the timeout for the command of wiping user data.", isTimeVal = true)
116     private long mWipeTimeout = 4 * 60 * 1000;
117 
118     @Option(
119         name = "fastboot-flash-option",
120         description = "additional options to pass with fastboot flash/update command."
121     )
122     private Collection<String> mFastbootFlashOptions = new ArrayList<>();
123 
124     @Option(
125             name = "flash-ramdisk",
126             description =
127                     "flashes ramdisk (usually on boot partition) in addition to "
128                             + "regular system image")
129     private boolean mShouldFlashRamdisk = false;
130 
131     @Option(
132             name = "ramdisk-partition",
133             description =
134                     "the partition (such as boot, vendor_boot) that ramdisk image "
135                             + "should be flashed to")
136     private String mRamdiskPartition = "boot";
137 
138     @Option(
139             name = "cancel-ota-snapshot",
140             description = "In case an OTA snapshot is in progress, cancel it.")
141     private boolean mCancelSnapshot = false;
142 
143     @Option(
144             name = "incremental-flashing",
145             description = "Leverage the incremental flashing feature for device update.")
146     private boolean mUseIncrementalFlashing = false;
147 
148     @Option(
149             name = "force-disable-incremental-flashing",
150             description = "Ignore HostOptions and disable the feature if true.")
151     private boolean mForceDisableIncrementalFlashing = false;
152 
153     @Option(
154             name = "create-snapshot-binary",
155             description = "Override the create_snapshot binary for incremental flashing.")
156     private File mCreateSnapshotBinary = null;
157 
158     @Option(
159             name = "allow-incremental-same-build",
160             description = "Allow doing incremental update on same build.")
161     private boolean mAllowIncrementalOnSameBuild = false;
162 
163     @Option(
164             name = "allow-incremental-cross-release",
165             description = "Allow doing incremental update across release build configs.")
166     private boolean mAllowIncrementalCrossRelease = false;
167 
168     @Option(
169             name = "apply-snapshot",
170             description =
171                     "Whether to apply the snapshot after mounting it. "
172                             + "This changes the baseline and does require reverting.")
173     private boolean mApplySnapshot = false;
174 
175     @Option(
176             name = "snapuserd-wait-phase",
177             description =
178                     "Only applicable to apply-snapshot, blocks snapuserd until a specified phase.")
179     private SnapuserdWaitPhase mWaitPhase = SnapuserdWaitPhase.BLOCK_BEFORE_RELEASING;
180 
181     @Option(
182             name = "allow-unzip-baseline",
183             description = "Whether to allow tracking the baseline as unzipped or not.")
184     private boolean mAllowUnzippedBaseline = false;
185 
186     @Option(
187             name = "enforce-snapshot-completed",
188             description = "Test mode was snapshot to ensure the logic was used and throw if not.")
189     private boolean mEnforceSnapshotCompleted = false;
190 
191     private IncrementalImageUtil mIncrementalImageUtil;
192     private IConfiguration mConfig;
193 
194     @Override
setConfiguration(IConfiguration configuration)195     public void setConfiguration(IConfiguration configuration) {
196         mConfig = configuration;
197     }
198 
199     /**
200      * Sets the device boot time
201      * <p/>
202      * Exposed for unit testing
203      */
setDeviceBootTime(long bootTime)204     void setDeviceBootTime(long bootTime) {
205         mDeviceBootTime = bootTime;
206     }
207 
208     /** Gets the device boot wait time */
getDeviceBootWaitTime()209     protected long getDeviceBootWaitTime() {
210         return mDeviceBootTime;
211     }
212 
213     /**
214      * Gets the interval between device boot poll attempts.
215      * <p/>
216      * Exposed for unit testing
217      */
getDeviceBootPollTimeMs()218     int getDeviceBootPollTimeMs() {
219         return BOOT_POLL_TIME_MS;
220     }
221 
222     /**
223      * Gets the {@link IRunUtil} instance to use.
224      * <p/>
225      * Exposed for unit testing
226      */
getRunUtil()227     IRunUtil getRunUtil() {
228         return RunUtil.getDefault();
229     }
230 
231     /**
232      * Gets the {@link IHostOptions} instance to use.
233      * <p/>
234      * Exposed for unit testing
235      */
getHostOptions()236     protected IHostOptions getHostOptions() {
237         return GlobalConfiguration.getInstance().getHostOptions();
238     }
239 
240     /**
241      * Set the userdata-flash option
242      *
243      * @param flashOption
244      */
setUserDataFlashOption(UserDataFlashOption flashOption)245     public void setUserDataFlashOption(UserDataFlashOption flashOption) {
246         mUserDataFlashOption = flashOption;
247     }
248 
249     /** Wrap the getBuildInfo so we have a change to override it for specific scenarios. */
getBuild(TestInformation testInfo)250     public IBuildInfo getBuild(TestInformation testInfo) {
251         return testInfo.getBuildInfo();
252     }
253 
254     /** {@inheritDoc} */
255     @Override
setUp(TestInformation testInfo)256     public void setUp(TestInformation testInfo)
257             throws TargetSetupError, DeviceNotAvailableException, BuildError {
258         if (testInfo.getDevice().getIDevice() instanceof NullDevice) {
259             CLog.i("Skipping device flashing, this is a null-device.");
260             return;
261         }
262         ITestDevice device = testInfo.getDevice();
263         IBuildInfo buildInfo = getBuild(testInfo);
264         CLog.i("Performing setup on %s", device.getSerialNumber());
265         if (!(buildInfo instanceof IDeviceBuildInfo)) {
266             throw new IllegalArgumentException("Provided buildInfo is not a IDeviceBuildInfo");
267         }
268         IDeviceBuildInfo deviceBuild = (IDeviceBuildInfo) buildInfo;
269         if (mShouldFlashRamdisk && deviceBuild.getRamdiskFile() == null) {
270             throw new HarnessRuntimeException(
271                     "ramdisk flashing enabled but no ramdisk file was found in build info",
272                     InfraErrorIdentifier.CONFIGURED_ARTIFACT_NOT_FOUND);
273         }
274         // For debugging: log the original build from the device
275         if (TestDeviceState.ONLINE.equals(testInfo.getDevice().getDeviceState())) {
276             buildInfo.addBuildAttribute(
277                     "original_build_fingerprint",
278                     device.getProperty("ro.product.build.fingerprint"));
279         }
280 
281         long queueTime = -1;
282         long flashingTime = -1;
283         long start = -1;
284         // HostOptions can force the incremental flashing to true.
285         if (getHostOptions().isIncrementalFlashingEnabled()) {
286             mUseIncrementalFlashing = true;
287         }
288         if (getHostOptions().isOptOutOfIncrementalFlashing()) {
289             mUseIncrementalFlashing = false;
290         }
291         if (mConfig != null) {
292             for (IDeviceConfiguration deviceConfig : mConfig.getDeviceConfig()) {
293                 for (ITargetPreparer p : deviceConfig.getTargetPreparers()) {
294                     if (p instanceof GkiDeviceFlashPreparer
295                             && !((GkiDeviceFlashPreparer) p).isDisabled()
296                             && !mApplySnapshot) {
297                         CLog.d(
298                                 "Force disabling incremental flashing due to"
299                                         + " GkiDeviceFlashPreparer.");
300                         mForceDisableIncrementalFlashing = true;
301                     }
302                 }
303             }
304         }
305         if (mForceDisableIncrementalFlashing) {
306             // The local option disable the feature, and skip tracking baseline
307             // for this run to avoid tracking a potentially bad baseline.
308             mUseIncrementalFlashing = false;
309             // Do not keep a cache when we are about to override it
310             DeviceImageTracker.getDefaultCache().invalidateTracking(device.getSerialNumber());
311         }
312         boolean useIncrementalFlashing = mUseIncrementalFlashing;
313         boolean reEntry = false;
314         if (useIncrementalFlashing) {
315             boolean isIsolated = false;
316             if (mConfig.getRetryDecision() instanceof BaseRetryDecision) {
317                 isIsolated =
318                         IsolationGrade.FULLY_ISOLATED.equals(
319                                 ((BaseRetryDecision) mConfig.getRetryDecision())
320                                         .getIsolationGrade());
321             }
322             if (mIncrementalImageUtil != null) {
323                 // Re-entry can occur during reset isolation.
324                 reEntry = true;
325             } else {
326                 mIncrementalImageUtil =
327                         IncrementalImageUtil.initialize(
328                                 device,
329                                 deviceBuild,
330                                 mCreateSnapshotBinary,
331                                 isIsolated,
332                                 mAllowIncrementalCrossRelease,
333                                 mApplySnapshot,
334                                 mWaitPhase);
335                 if (mIncrementalImageUtil == null) {
336                     useIncrementalFlashing = false;
337                 } else {
338                     if (mAllowIncrementalOnSameBuild) {
339                         mIncrementalImageUtil.allowSameBuildFlashing();
340                     }
341                     if (TestDeviceState.ONLINE.equals(device.getDeviceState())) {
342                         // No need to reboot yet, it will happen later in the sequence
343                         String verityOutput = device.executeAdbCommand("enable-verity");
344                         CLog.d("%s", verityOutput);
345                     }
346                 }
347             }
348         }
349         try {
350             checkDeviceProductType(device, deviceBuild);
351             device.setRecoveryMode(RecoveryMode.ONLINE);
352             IDeviceFlasher flasher = createFlasher(device);
353             flasher.setWipeTimeout(mWipeTimeout);
354             boolean tookPermit = false;
355             // only surround fastboot related operations with flashing permit restriction
356             try {
357                 flasher.overrideDeviceOptions(device);
358                 flasher.setUserDataFlashOption(mUserDataFlashOption);
359                 flasher.setForceSystemFlash(mForceSystemFlash);
360                 flasher.setDataWipeSkipList(mDataWipeSkipList);
361                 flasher.setShouldFlashRamdisk(mShouldFlashRamdisk);
362                 if (mShouldFlashRamdisk) {
363                     flasher.setRamdiskPartition(mRamdiskPartition);
364                 }
365                 if (flasher instanceof FastbootDeviceFlasher) {
366                     ((FastbootDeviceFlasher) flasher).setFlashOptions(mFastbootFlashOptions);
367                     if (!reEntry) {
368                         // Avoid using incremental during re-entry since it will just wipe
369                         ((FastbootDeviceFlasher) flasher)
370                                 .setIncrementalFlashing(mIncrementalImageUtil);
371                     }
372                 }
373                 start = System.currentTimeMillis();
374                 flasher.preFlashOperations(device, deviceBuild);
375                 // After preFlashOperations device should be in bootloader
376                 if (mCancelSnapshot && TestDeviceState.FASTBOOT.equals(device.getDeviceState())) {
377                     CommandResult res =
378                             device.executeFastbootCommand(
379                                     SNAPSHOT_CANCEL_TIMEOUT, "snapshot-update", "cancel");
380                     if (!CommandStatus.SUCCESS.equals(res.getStatus())) {
381                         CLog.w(
382                                 "Failed to cancel snapshot: %s.\nstdout:%s\nstderr:%s",
383                                 res.getStatus(), res.getStdout(), res.getStderr());
384                     }
385                 }
386                 try (CloseableTraceScope ignored =
387                         new CloseableTraceScope("wait_for_flashing_permit")) {
388                     if (mIncrementalImageUtil == null) {
389                         // Only #flash is included in the critical section
390                         getHostOptions().takePermit(PermitLimitType.CONCURRENT_FLASHER);
391                         tookPermit = true;
392                     }
393                     queueTime = System.currentTimeMillis() - start;
394                     if (tookPermit) {
395                         CLog.v(
396                                 "Flashing permit obtained after %ds",
397                                 TimeUnit.MILLISECONDS.toSeconds(queueTime));
398                     }
399                     InvocationMetricLogger.addInvocationMetrics(
400                             InvocationMetricKey.FLASHING_PERMIT_LATENCY, queueTime);
401                 }
402                 // Don't allow interruptions during flashing operations.
403                 getRunUtil().allowInterrupt(false);
404                 start = System.currentTimeMillis();
405                 // Set flashing method as unknown here as a fallback, in case it wasn't overwritten
406                 // by subclass implementations
407                 InvocationMetricLogger.addInvocationMetrics(
408                         InvocationMetricKey.FLASHING_METHOD,
409                         FlashingMethod.FASTBOOT_UNCATEGORIZED.toString());
410                 flasher.flash(device, deviceBuild);
411             } catch (DeviceNotAvailableException | TargetSetupError | RuntimeException e) {
412                 // Clear tracking in case of error
413                 DeviceImageTracker.getDefaultCache().invalidateTracking(device.getSerialNumber());
414                 throw e;
415             } finally {
416                 flashingTime = System.currentTimeMillis() - start;
417                 if (tookPermit) {
418                     getHostOptions().returnPermit(PermitLimitType.CONCURRENT_FLASHER);
419                 }
420                 flasher.postFlashOperations(device, deviceBuild);
421                 // report flashing status
422                 CommandStatus status = flasher.getSystemFlashingStatus();
423                 if (status == null) {
424                     CLog.i("Skipped reporting metrics because system partitions were not flashed.");
425                 } else {
426                     if (mIncrementalImageUtil != null) {
427                         InvocationMetricLogger.addInvocationMetrics(
428                                 InvocationMetricKey.INCREMENTAL_FLASHING_TIME, flashingTime);
429                     }
430                     InvocationMetricLogger.addInvocationMetrics(
431                             InvocationMetricKey.FLASHING_TIME, flashingTime);
432                     reportFlashMetrics(buildInfo.getBuildBranch(), buildInfo.getBuildFlavor(),
433                             buildInfo.getBuildId(), device.getSerialNumber(), queueTime,
434                             flashingTime, status);
435                 }
436             }
437             if (mIncrementalImageUtil == null) {
438                 // only want logcat captured for current build, delete any accumulated log data
439                 device.clearLogcat();
440             }
441             // In case success with full flashing
442             if (!reEntry) {
443                 moveBaseline(deviceBuild, device.getSerialNumber(), useIncrementalFlashing);
444             }
445             if (mSkipPostFlashingSetup) {
446                 return;
447             }
448             // Temporary re-enable interruptable since the critical flashing operation is over.
449             getRunUtil().allowInterrupt(true);
450             device.waitForDeviceOnline();
451             // device may lose date setting if wiped, update with host side date in case anything on
452             // device side malfunction with an invalid date
453             if (device.enableAdbRoot()) {
454                 device.setDate(null);
455             }
456             // Disable interrupt for encryption operation.
457             getRunUtil().allowInterrupt(false);
458             checkBuild(device, deviceBuild);
459             // Once critical operation is done, we re-enable interruptable
460             getRunUtil().allowInterrupt(true);
461             try {
462                 boolean available = device.waitForDeviceAvailableInRecoverPath(mDeviceBootTime);
463                 if (!available) {
464                     // Clear tracking in case of error
465                     DeviceImageTracker.getDefaultCache()
466                             .invalidateTracking(device.getSerialNumber());
467                     throw new DeviceFailedToBootError(
468                             String.format(
469                                     "Device %s did not become available after flashing %s",
470                                     device.getSerialNumber(), deviceBuild.getDeviceBuildId()),
471                             device.getDeviceDescriptor(),
472                             DeviceErrorIdentifier.ERROR_AFTER_FLASHING);
473                 }
474             } catch (DeviceNotAvailableException e) {
475                 // Clear tracking in case of error
476                 DeviceImageTracker.getDefaultCache().invalidateTracking(device.getSerialNumber());
477                 // Assume this is a build problem
478                 throw new DeviceFailedToBootError(
479                         String.format(
480                                 "Device %s did not become available after flashing %s",
481                                 device.getSerialNumber(), deviceBuild.getDeviceBuildId()),
482                         device.getDeviceDescriptor(),
483                         e,
484                         DeviceErrorIdentifier.ERROR_AFTER_FLASHING);
485             }
486             device.postBootSetup();
487         } finally {
488             device.setRecoveryMode(RecoveryMode.AVAILABLE);
489             // Allow interruption at the end no matter what.
490             getRunUtil().allowInterrupt(true);
491             if (mIncrementalImageUtil != null) {
492                 mIncrementalImageUtil.cleanAfterSetup();
493             }
494         }
495     }
496 
moveBaseline( IDeviceBuildInfo deviceBuild, String serial, boolean useIncrementalFlashing)497     private void moveBaseline(
498             IDeviceBuildInfo deviceBuild, String serial, boolean useIncrementalFlashing) {
499         if (!getHostOptions().isOptOutOfIncrementalFlashing()) {
500             boolean moveBaseLine = true;
501             if (!mUseIncrementalFlashing || useIncrementalFlashing) {
502                 // Do not move baseline if using incremental flashing
503                 moveBaseLine = false;
504             }
505             if (mApplySnapshot) {
506                 // Move baseline when going with incremental + apply update
507                 moveBaseLine = true;
508             }
509             if (moveBaseLine) {
510                 File deviceImage = deviceBuild.getDeviceImageFile();
511                 File tmpReference = null;
512                 try {
513                     if (mAllowUnzippedBaseline
514                             && mIncrementalImageUtil != null
515                             && mIncrementalImageUtil.getExtractedTargetDirectory() != null
516                             && mIncrementalImageUtil.getExtractedTargetDirectory().isDirectory()) {
517                         CLog.d(
518                                 "Using unzipped baseline: %s",
519                                 mIncrementalImageUtil.getExtractedTargetDirectory());
520                         tmpReference = mIncrementalImageUtil.getExtractedTargetDirectory();
521                         deviceImage = tmpReference;
522                     }
523                     DeviceImageTracker.getDefaultCache()
524                             .trackUpdatedDeviceImage(
525                                     serial,
526                                     deviceImage,
527                                     deviceBuild.getBootloaderImageFile(),
528                                     deviceBuild.getBasebandImageFile(),
529                                     deviceBuild.getBuildId(),
530                                     deviceBuild.getBuildBranch(),
531                                     deviceBuild.getBuildFlavor());
532                 } finally {
533                     FileUtil.recursiveDelete(tmpReference);
534                 }
535             }
536         }
537     }
538 
539     /**
540      * Possible check before flashing to ensure the device is as expected compare to the build info.
541      *
542      * @param device the {@link ITestDevice} to flash.
543      * @param deviceBuild the {@link IDeviceBuildInfo} used to flash.
544      * @throws BuildError
545      * @throws DeviceNotAvailableException
546      */
checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)547     protected void checkDeviceProductType(ITestDevice device, IDeviceBuildInfo deviceBuild)
548             throws BuildError, DeviceNotAvailableException {
549         // empty of purpose
550     }
551 
552     /**
553      * Verifies the expected build matches the actual build on device after flashing
554      * @throws DeviceNotAvailableException
555      */
checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)556     private void checkBuild(ITestDevice device, IDeviceBuildInfo deviceBuild)
557             throws DeviceNotAvailableException {
558         // Need to use deviceBuild.getDeviceBuildId instead of getBuildId because the build info
559         // could be an AppBuildInfo and return app build id. Need to be more explicit that we
560         // check for the device build here.
561         if (!mSkipPostFlashBuildIdCheck) {
562             checkBuildAttribute(deviceBuild.getDeviceBuildId(), device.getBuildId(),
563                     device.getSerialNumber());
564         }
565     }
566 
checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr, String serial)567     private void checkBuildAttribute(String expectedBuildAttr, String actualBuildAttr,
568             String serial) throws DeviceNotAvailableException {
569         if (expectedBuildAttr == null || actualBuildAttr == null ||
570                 !expectedBuildAttr.equals(actualBuildAttr)) {
571             // throw DNAE - assume device hardware problem - we think flash was successful but
572             // device is not running right bits
573             throw new DeviceNotAvailableException(
574                     String.format(
575                             "Unexpected build after flashing. Expected %s, actual %s",
576                             expectedBuildAttr, actualBuildAttr),
577                     serial,
578                     DeviceErrorIdentifier.ERROR_AFTER_FLASHING);
579         }
580     }
581 
582     /**
583      * Create {@link IDeviceFlasher} to use. Subclasses can override
584      * @throws DeviceNotAvailableException
585      */
createFlasher(ITestDevice device)586     protected abstract IDeviceFlasher createFlasher(ITestDevice device)
587             throws DeviceNotAvailableException;
588 
589     @Override
tearDown(TestInformation testInfo, Throwable e)590     public void tearDown(TestInformation testInfo, Throwable e) throws DeviceNotAvailableException {
591         if (testInfo.getDevice().getIDevice() instanceof NullDevice) {
592             CLog.i("Skipping device flashing tearDown, this is a null-device.");
593             return;
594         }
595         if (mIncrementalImageUtil != null) {
596             CLog.d("Teardown related to incremental update.");
597             RecoveryMode mode = testInfo.getDevice().getRecoveryMode();
598             try {
599                 testInfo.getDevice().setRecoveryMode(RecoveryMode.NONE);
600                 if (mAllowUnzippedBaseline) {
601                     mIncrementalImageUtil.allowUnzipBaseline();
602                 }
603                 mIncrementalImageUtil.teardownDevice(testInfo);
604             } finally {
605                 testInfo.getDevice().setRecoveryMode(mode);
606             }
607         }
608         if (mEnforceSnapshotCompleted && e == null) {
609             if (mIncrementalImageUtil == null || !mIncrementalImageUtil.updateCompleted()) {
610                 throw new RuntimeException(
611                         "We expected incremental-flashing to be used but wasn't.");
612             }
613         }
614     }
615 
616     /**
617      * Reports device flashing timing data to metrics backend
618      * @param branch the branch where the device build originated from
619      * @param buildFlavor the build flavor of the device build
620      * @param buildId the build number of the device build
621      * @param serial the serial number of device
622      * @param queueTime the time spent waiting for a flashing limit to become available
623      * @param flashingTime the time spent in flashing device image zip
624      * @param flashingStatus the execution status of flashing command
625      */
reportFlashMetrics(String branch, String buildFlavor, String buildId, String serial, long queueTime, long flashingTime, CommandStatus flashingStatus)626     protected void reportFlashMetrics(String branch, String buildFlavor, String buildId,
627             String serial, long queueTime, long flashingTime, CommandStatus flashingStatus) {
628         // no-op as default implementation
629     }
630 
631     /**
632      * Sets the option for whether ramdisk should be flashed
633      *
634      * @param shouldFlashRamdisk
635      */
636     @VisibleForTesting
setShouldFlashRamdisk(boolean shouldFlashRamdisk)637     void setShouldFlashRamdisk(boolean shouldFlashRamdisk) {
638         mShouldFlashRamdisk = shouldFlashRamdisk;
639     }
640 
setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck)641     protected void setSkipPostFlashBuildIdCheck(boolean skipPostFlashBuildIdCheck) {
642         mSkipPostFlashBuildIdCheck = skipPostFlashBuildIdCheck;
643     }
644 
setUseIncrementalFlashing(boolean incrementalFlashing)645     protected void setUseIncrementalFlashing(boolean incrementalFlashing) {
646         mUseIncrementalFlashing = incrementalFlashing;
647     }
648 
isIncrementalFlashingEnabled()649     public boolean isIncrementalFlashingEnabled() {
650         return mUseIncrementalFlashing;
651     }
652 
isIncrementalFlashingForceDisabled()653     public boolean isIncrementalFlashingForceDisabled() {
654         return mForceDisableIncrementalFlashing;
655     }
656 
setAllowCrossReleaseFlashing(boolean allowCrossReleaseFlashing)657     public void setAllowCrossReleaseFlashing(boolean allowCrossReleaseFlashing) {
658         mAllowIncrementalCrossRelease = allowCrossReleaseFlashing;
659     }
660 
setApplySnapshot(boolean applySnapshot)661     public void setApplySnapshot(boolean applySnapshot) {
662         mApplySnapshot = applySnapshot;
663     }
664 
setAllowUnzipBaseline(boolean allowUnzipBaseline)665     public void setAllowUnzipBaseline(boolean allowUnzipBaseline) {
666         mAllowUnzippedBaseline = allowUnzipBaseline;
667     }
668 }
669