• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 package com.android.tradefed.invoker;
17 
18 import com.android.ddmlib.IDevice;
19 import com.android.ddmlib.Log.LogLevel;
20 import com.android.tradefed.build.BuildInfo;
21 import com.android.tradefed.build.BuildInfoKey.BuildInfoFileKey;
22 import com.android.tradefed.build.BuildRetrievalError;
23 import com.android.tradefed.build.IBuildInfo;
24 import com.android.tradefed.build.IBuildInfo.BuildInfoProperties;
25 import com.android.tradefed.build.IBuildProvider;
26 import com.android.tradefed.build.IDeviceBuildInfo;
27 import com.android.tradefed.build.IDeviceBuildProvider;
28 import com.android.tradefed.command.ICommandOptions;
29 import com.android.tradefed.config.GlobalConfiguration;
30 import com.android.tradefed.config.IConfiguration;
31 import com.android.tradefed.config.IConfigurationReceiver;
32 import com.android.tradefed.config.IDeviceConfiguration;
33 import com.android.tradefed.config.filter.GetPreviousPassedHelper;
34 import com.android.tradefed.device.DeviceNotAvailableException;
35 import com.android.tradefed.device.ITestDevice;
36 import com.android.tradefed.device.NativeDevice;
37 import com.android.tradefed.device.StubDevice;
38 import com.android.tradefed.device.cloud.GceAvdInfo;
39 import com.android.tradefed.device.cloud.GceManager;
40 import com.android.tradefed.device.cloud.OxygenUtil;
41 import com.android.tradefed.device.metric.AutoLogCollector;
42 import com.android.tradefed.device.metric.CollectorHelper;
43 import com.android.tradefed.device.metric.CountTestCasesCollector;
44 import com.android.tradefed.device.metric.IMetricCollector;
45 import com.android.tradefed.device.metric.IMetricCollectorReceiver;
46 import com.android.tradefed.error.HarnessRuntimeException;
47 import com.android.tradefed.invoker.ExecutionFiles.FilesKey;
48 import com.android.tradefed.invoker.TestInvocation.Stage;
49 import com.android.tradefed.invoker.logger.CurrentInvocation;
50 import com.android.tradefed.invoker.logger.CurrentInvocation.IsolationGrade;
51 import com.android.tradefed.invoker.logger.InvocationMetricLogger;
52 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationGroupMetricKey;
53 import com.android.tradefed.invoker.logger.InvocationMetricLogger.InvocationMetricKey;
54 import com.android.tradefed.invoker.logger.TfObjectTracker;
55 import com.android.tradefed.invoker.shard.IShardHelper;
56 import com.android.tradefed.invoker.shard.TestsPoolPoller;
57 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
58 import com.android.tradefed.log.ITestLogger;
59 import com.android.tradefed.log.LogUtil.CLog;
60 import com.android.tradefed.result.ByteArrayInputStreamSource;
61 import com.android.tradefed.result.ITestInvocationListener;
62 import com.android.tradefed.result.ITestLoggerReceiver;
63 import com.android.tradefed.result.InputStreamSource;
64 import com.android.tradefed.result.LogDataType;
65 import com.android.tradefed.result.error.TestErrorIdentifier;
66 import com.android.tradefed.retry.IRetryDecision;
67 import com.android.tradefed.retry.RetryLogSaverResultForwarder;
68 import com.android.tradefed.retry.RetryStatistics;
69 import com.android.tradefed.retry.RetryStrategy;
70 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
71 import com.android.tradefed.targetprep.BuildError;
72 import com.android.tradefed.targetprep.IHostCleaner;
73 import com.android.tradefed.targetprep.ILabPreparer;
74 import com.android.tradefed.targetprep.ITargetPreparer;
75 import com.android.tradefed.targetprep.TargetSetupError;
76 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
77 import com.android.tradefed.testtype.IBuildReceiver;
78 import com.android.tradefed.testtype.IDeviceTest;
79 import com.android.tradefed.testtype.IInvocationContextReceiver;
80 import com.android.tradefed.testtype.IRemoteTest;
81 import com.android.tradefed.testtype.ITestFilterReceiver;
82 import com.android.tradefed.testtype.retry.IAutoRetriableTest;
83 import com.android.tradefed.testtype.suite.BaseTestSuite;
84 import com.android.tradefed.testtype.suite.ITestSuite;
85 import com.android.tradefed.testtype.suite.ModuleListener;
86 import com.android.tradefed.util.CommandResult;
87 import com.android.tradefed.util.CommandStatus;
88 import com.android.tradefed.util.FileUtil;
89 import com.android.tradefed.util.PrettyPrintDelimiter;
90 import com.android.tradefed.util.RunInterruptedException;
91 import com.android.tradefed.util.RunUtil;
92 import com.android.tradefed.util.SystemUtil;
93 import com.android.tradefed.util.SystemUtil.EnvVariable;
94 import com.android.tradefed.util.TimeUtil;
95 import com.android.tradefed.util.executor.ParallelDeviceExecutor;
96 
97 import com.google.common.annotations.VisibleForTesting;
98 import com.google.common.base.Strings;
99 
100 import java.io.File;
101 import java.io.IOException;
102 import java.time.Duration;
103 import java.util.ArrayList;
104 import java.util.HashSet;
105 import java.util.List;
106 import java.util.ListIterator;
107 import java.util.Map;
108 import java.util.Set;
109 import java.util.Timer;
110 import java.util.TimerTask;
111 import java.util.concurrent.Callable;
112 import java.util.concurrent.ConcurrentHashMap;
113 import java.util.concurrent.TimeUnit;
114 
115 /**
116  * Class that describes all the invocation steps: build download, target_prep, run tests, clean up.
117  * Can be extended to override the default behavior of some steps. Order of the steps is driven by
118  * {@link TestInvocation}.
119  */
120 public class InvocationExecution implements IInvocationExecution {
121 
122     public static final String ADB_VERSION_KEY = "adb_version";
123     public static final String JAVA_VERSION_KEY = "java_version";
124     public static final String JAVA_CLASSPATH_KEY = "java_classpath";
125     // Track which preparer ran in setup to ensure we don't trigger tearDown if setup was skipped.
126     private Set<IMultiTargetPreparer> mTrackMultiPreparers = null;
127     private Map<String, Set<ITargetPreparer>> mTrackLabPreparers = null;
128     private Map<String, Set<ITargetPreparer>> mTrackTargetPreparers = null;
129     // GceManager for multi-device leasing. It's needed for releasing the devices.
130     private GceManager mMultiDeviceRequester = null;
131 
132     /** Timer to make sure Test Phase does not run for too long. */
133     private class TestPhaseMonitor extends TimerTask {
134 
135         private TestThread mTestThread;
136 
TestPhaseMonitor(TestThread toMonitor)137         public TestPhaseMonitor(TestThread toMonitor) {
138             mTestThread = toMonitor;
139         }
140 
141         @Override
run()142         public void run() {
143             if (mTestThread != null) {
144                 mTestThread.stopTestThread();
145             }
146         }
147     }
148 
149     /** A Thread to execute {@link IRemoteTest} */
150     private class TestThread extends Thread {
151         private TestInformation mTestInfo;
152         private ITestInvocationListener mTestListener;
153         private IRemoteTest mTest;
154         private Throwable lastThrownException;
155 
TestThread( TestInformation info, ITestInvocationListener listener, IRemoteTest test)156         public TestThread(
157                 TestInformation info, ITestInvocationListener listener, IRemoteTest test) {
158             mTestInfo = info;
159             mTestListener = listener;
160             mTest = test;
161         }
162 
163         @Override
run()164         public void run() {
165             try {
166                 mTest.run(mTestInfo, mTestListener);
167             } catch (Exception e) {
168                 lastThrownException = e;
169             }
170         }
171 
getLastThrownException()172         public Throwable getLastThrownException() {
173             return lastThrownException;
174         }
175 
stopTestThread()176         public void stopTestThread() {
177             this.interrupt();
178             mTestInfo.notifyTimeout();
179             // record this interrupt as an exception so that TestInvocation thread can throw this.
180             lastThrownException =
181                     new RunInterruptedException(
182                             "Test Phase Timeout Reached.",
183                             TestErrorIdentifier.TEST_PHASE_TIMED_OUT);
184         }
185     }
186 
187     @Override
fetchBuild( TestInformation testInfo, IConfiguration config, IRescheduler rescheduler, ITestInvocationListener listener)188     public boolean fetchBuild(
189             TestInformation testInfo,
190             IConfiguration config,
191             IRescheduler rescheduler,
192             ITestInvocationListener listener)
193             throws DeviceNotAvailableException, BuildRetrievalError {
194         String currentDeviceName = null;
195         IBuildInfo buildReplicat = null;
196         try {
197             // TODO: evaluate fetching build in parallel
198             for (int i = 0; i < testInfo.getContext().getDeviceConfigNames().size(); i++) {
199                 currentDeviceName = testInfo.getContext().getDeviceConfigNames().get(i);
200                 if (buildReplicat != null) {
201                     // TODO: evaluate if cloning the build is needed
202                     testInfo.getContext().addDeviceBuildInfo(currentDeviceName, buildReplicat);
203                     continue;
204                 }
205                 IBuildInfo info = null;
206                 ITestDevice device = testInfo.getContext().getDevice(currentDeviceName);
207                 IDeviceConfiguration deviceConfig = config.getDeviceConfigByName(currentDeviceName);
208                 IBuildProvider provider = deviceConfig.getBuildProvider();
209                 TfObjectTracker.countWithParents(provider.getClass());
210                 // Inject the context to the provider if it can receive it
211                 if (provider instanceof IInvocationContextReceiver) {
212                     ((IInvocationContextReceiver) provider)
213                             .setInvocationContext(testInfo.getContext());
214                 }
215                 if (provider instanceof ITestLoggerReceiver) {
216                     ((ITestLoggerReceiver) provider).setTestLogger(listener);
217                 }
218                 // Get the build
219                 if (provider instanceof IDeviceBuildProvider) {
220                     // Download a device build if the provider can handle it.
221                     info = ((IDeviceBuildProvider) provider).getBuild(device);
222                 } else {
223                     info = provider.getBuild();
224                 }
225                 if (info != null) {
226                     info.setDeviceSerial(device.getSerialNumber());
227                     testInfo.getContext().addDeviceBuildInfo(currentDeviceName, info);
228                     device.setRecovery(deviceConfig.getDeviceRecovery());
229                 } else {
230                     CLog.logAndDisplay(
231                             LogLevel.WARN,
232                             "No build found to test for device: %s",
233                             device.getSerialNumber());
234                     IBuildInfo notFoundStub = new BuildInfo();
235                     updateBuild(notFoundStub, config);
236                     testInfo.getContext().addDeviceBuildInfo(currentDeviceName, notFoundStub);
237                     return false;
238                 }
239                 // TODO: remove build update when reporting is done on context
240                 updateBuild(info, config);
241                 linkExternalDirs(info, testInfo);
242 
243                 if (config.getCommandOptions().shouldUseReplicateSetup()) {
244                     buildReplicat = info;
245                 }
246             }
247         } catch (BuildRetrievalError e) {
248             CLog.e(e);
249             if (currentDeviceName != null) {
250                 IBuildInfo errorBuild = e.getBuildInfo();
251                 updateBuild(errorBuild, config);
252                 testInfo.getContext().addDeviceBuildInfo(currentDeviceName, errorBuild);
253             }
254             throw e;
255         } catch (RuntimeException re) {
256             if (currentDeviceName != null) {
257                 IBuildInfo errorBuild =
258                         TestInvocation.backFillBuildInfoForReporting(config.getCommandLine());
259                 updateBuild(errorBuild, config);
260                 testInfo.getContext().addDeviceBuildInfo(currentDeviceName, errorBuild);
261             }
262             throw re;
263         }
264         setBinariesVersion(testInfo.getContext());
265         copyRemoteFiles(config.getCommandOptions(), testInfo.getBuildInfo());
266         return true;
267     }
268 
269     @Override
cleanUpBuilds(IInvocationContext context, IConfiguration config)270     public void cleanUpBuilds(IInvocationContext context, IConfiguration config) {
271         // Ensure build infos are always cleaned up at the end of invocation.
272         for (String cleanUpDevice : context.getDeviceConfigNames()) {
273             if (context.getBuildInfo(cleanUpDevice) != null) {
274                 try {
275                     config.getDeviceConfigByName(cleanUpDevice)
276                             .getBuildProvider()
277                             .cleanUp(context.getBuildInfo(cleanUpDevice));
278                 } catch (RuntimeException e) {
279                     // We catch an simply log exception in cleanUp to avoid missing any final
280                     // step of the invocation.
281                     CLog.e(e);
282                 }
283             }
284         }
285     }
286 
287     @Override
shardConfig( IConfiguration config, TestInformation testInfo, IRescheduler rescheduler, ITestLogger logger)288     public boolean shardConfig(
289             IConfiguration config,
290             TestInformation testInfo,
291             IRescheduler rescheduler,
292             ITestLogger logger) {
293         IShardHelper helper = createShardHelper();
294         CLog.d("IShardHelper selected: %s", helper);
295         return helper.shardConfig(config, testInfo, rescheduler, logger);
296     }
297 
298     /** Create an return the {@link IShardHelper} to be used. */
299     @VisibleForTesting
createShardHelper()300     protected IShardHelper createShardHelper() {
301         return GlobalConfiguration.getInstance().getShardingStrategy();
302     }
303 
304     /**
305      * Retrieve a list of target preparers to run on this device.
306      *
307      * <p>Overridden in sandbox classes to restrict lab preparers from being run inside the sandbox
308      * child
309      */
getTargetPreparersToRun( IConfiguration config, String deviceName)310     protected List<ITargetPreparer> getTargetPreparersToRun(
311             IConfiguration config, String deviceName) {
312         List<ITargetPreparer> preparersToRun = new ArrayList<>();
313         preparersToRun.addAll(config.getDeviceConfigByName(deviceName).getTargetPreparers());
314         return preparersToRun;
315     }
316 
317     /**
318      * Retrieve a list of lab preparers to run on this device.
319      *
320      * <p>Overridden in sandbox classes to restrict lab preparers from being run inside the sandbox
321      * child
322      */
getLabPreparersToRun(IConfiguration config, String deviceName)323     protected List<ITargetPreparer> getLabPreparersToRun(IConfiguration config, String deviceName) {
324         List<ITargetPreparer> preparersToRun = new ArrayList<>();
325         preparersToRun.addAll(config.getDeviceConfigByName(deviceName).getLabPreparers());
326         return preparersToRun;
327     }
328 
329     @Override
doSetup(TestInformation testInfo, IConfiguration config, final ITestLogger listener)330     public void doSetup(TestInformation testInfo, IConfiguration config, final ITestLogger listener)
331             throws TargetSetupError, BuildError, DeviceNotAvailableException {
332         long start = System.currentTimeMillis();
333         mTrackLabPreparers = new ConcurrentHashMap<>();
334         mTrackTargetPreparers = new ConcurrentHashMap<>();
335         InvocationMetricLogger.addInvocationMetrics(InvocationMetricKey.SETUP_START, start);
336 
337         for (String deviceName : testInfo.getContext().getDeviceConfigNames()) {
338             ITestDevice device = testInfo.getContext().getDevice(deviceName);
339             CLog.d("Starting setup for device: '%s'", device.getSerialNumber());
340             if (device instanceof ITestLoggerReceiver) {
341                 ((ITestLoggerReceiver) testInfo.getContext().getDevice(deviceName))
342                         .setTestLogger(listener);
343             }
344             mTrackLabPreparers.put(deviceName, new HashSet<>());
345             mTrackTargetPreparers.put(deviceName, new HashSet<>());
346         }
347         try {
348             try (CloseableTraceScope ignored =
349                     new CloseableTraceScope(InvocationMetricKey.pre_multi_preparer.name())) {
350                 // Before all the individual setup, make the multi-pre-target-preparer devices setup
351                 runMultiTargetPreparers(
352                         config.getMultiPreTargetPreparers(),
353                         listener,
354                         testInfo,
355                         "multi pre target preparer setup");
356             } finally {
357                 long end = System.currentTimeMillis();
358                 // Pre-multi-preparer are test specific and account toward test setup
359                 InvocationMetricLogger.addInvocationPairMetrics(
360                         InvocationMetricKey.TEST_SETUP_PAIR, start, end);
361             }
362             start = System.currentTimeMillis();
363             try (CloseableTraceScope ignored =
364                     new CloseableTraceScope(InvocationMetricKey.lab_setup.name())) {
365                 runLabPreparersSetup(testInfo, config, listener);
366             } finally {
367                 long end = System.currentTimeMillis();
368                 InvocationMetricLogger.addInvocationMetrics(InvocationMetricKey.SETUP_END, end);
369                 InvocationMetricLogger.addInvocationPairMetrics(
370                         InvocationMetricKey.SETUP_PAIR, start, end);
371             }
372             long startPreparer = System.currentTimeMillis();
373             try (CloseableTraceScope ignored =
374                     new CloseableTraceScope(InvocationMetricKey.test_setup.name())) {
375                 runPreparersSetup(testInfo, config, listener);
376 
377                 // After all the individual setup, make the multi-devices setup
378                 runMultiTargetPreparers(
379                         config.getMultiTargetPreparers(),
380                         listener,
381                         testInfo,
382                         "multi target preparer setup");
383                 // Collect some info automatically after setup
384                 collectAutoInfo(config, testInfo);
385             } finally {
386                 // Note: These metrics are handled in a try in case of a kernel reset or device
387                 // issue.
388                 // Setup timing metric. It does not include flashing time on boot tests.
389                 long end = System.currentTimeMillis();
390                 InvocationMetricLogger.addInvocationPairMetrics(
391                         InvocationMetricKey.TEST_SETUP_PAIR, startPreparer, end);
392                 long setupDuration = end - start;
393                 InvocationMetricLogger.addInvocationMetrics(
394                         InvocationMetricKey.SETUP, setupDuration);
395                 CLog.d("Total setup duration: %s'", TimeUtil.formatElapsedTime(setupDuration));
396             }
397         } finally {
398             // Upload the setup logcat after setup is complete.
399             for (ITestDevice device : testInfo.getDevices()) {
400                 reportLogs(device, listener, Stage.SETUP);
401             }
402         }
403     }
404 
runLabPreparersSetup( TestInformation testInfo, IConfiguration config, ITestLogger listener)405     private void runLabPreparersSetup(
406             TestInformation testInfo, IConfiguration config, ITestLogger listener)
407             throws TargetSetupError, BuildError, DeviceNotAvailableException {
408         int index = 0;
409         if ((config.getCommandOptions().shouldUseParallelSetup()
410                         || config.getCommandOptions().shouldUseReplicateSetup())
411                 && config.getDeviceConfig().size() > 1) {
412             CLog.d("Using parallel setup.");
413             ParallelDeviceExecutor<Boolean> executor =
414                     new ParallelDeviceExecutor<>(testInfo.getContext().getDevices().size());
415             List<Callable<Boolean>> callableTasks = new ArrayList<>();
416             for (String deviceName : testInfo.getContext().getDeviceConfigNames()) {
417                 final int deviceIndex = index;
418                 // Replicate TestInfo
419                 TestInformation replicated =
420                         TestInformation.createModuleTestInfo(testInfo, testInfo.getContext());
421                 Callable<Boolean> callableTask =
422                         () -> {
423                             // Lab preparer then target preparer
424                             runLabPreparationOnDevice(
425                                     replicated,
426                                     deviceName,
427                                     deviceIndex,
428                                     getLabPreparersToRun(config, deviceName),
429                                     mTrackLabPreparers.get(deviceName),
430                                     listener);
431                             return true;
432                         };
433                 callableTasks.add(callableTask);
434                 index++;
435             }
436             Duration timeout = config.getCommandOptions().getParallelSetupTimeout();
437             executor.invokeAll(callableTasks, timeout.toMillis(), TimeUnit.MILLISECONDS);
438             if (executor.hasErrors()) {
439                 List<Throwable> errors = executor.getErrors();
440                 // TODO: Handle throwing multi-exceptions, right now throw the first one.
441                 for (Throwable error : errors) {
442                     if (error instanceof TargetSetupError) {
443                         throw (TargetSetupError) error;
444                     }
445                     if (error instanceof BuildError) {
446                         throw (BuildError) error;
447                     }
448                     if (error instanceof DeviceNotAvailableException) {
449                         throw (DeviceNotAvailableException) error;
450                     }
451                     if (error instanceof HarnessRuntimeException) {
452                         throw (HarnessRuntimeException) error;
453                     }
454                     throw new RuntimeException(error);
455                 }
456             }
457         } else {
458             for (String deviceName : testInfo.getContext().getDeviceConfigNames()) {
459                 // Lab preparer then target preparer
460                 runLabPreparationOnDevice(
461                         testInfo,
462                         deviceName,
463                         index,
464                         getLabPreparersToRun(config, deviceName),
465                         mTrackLabPreparers.get(deviceName),
466                         listener);
467                 index++;
468             }
469         }
470     }
471 
runPreparersSetup( TestInformation testInfo, IConfiguration config, ITestLogger listener)472     private void runPreparersSetup(
473             TestInformation testInfo, IConfiguration config, ITestLogger listener)
474             throws TargetSetupError, BuildError, DeviceNotAvailableException {
475         int index = 0;
476         if ((config.getCommandOptions().shouldUseParallelSetup()
477                         || config.getCommandOptions().shouldUseReplicateSetup())
478                 && config.getDeviceConfig().size() > 1) {
479             CLog.d("Using parallel setup.");
480             ParallelDeviceExecutor<Boolean> executor =
481                     new ParallelDeviceExecutor<>(testInfo.getContext().getDevices().size());
482             List<Callable<Boolean>> callableTasks = new ArrayList<>();
483             for (String deviceName : testInfo.getContext().getDeviceConfigNames()) {
484                 final int deviceIndex = index;
485                 // Replicate TestInfo
486                 TestInformation replicated =
487                         TestInformation.createModuleTestInfo(testInfo, testInfo.getContext());
488                 Callable<Boolean> callableTask =
489                         () -> {
490                             runPreparationOnDevice(
491                                     replicated,
492                                     deviceName,
493                                     deviceIndex,
494                                     getTargetPreparersToRun(config, deviceName),
495                                     mTrackTargetPreparers.get(deviceName),
496                                     listener);
497                             return true;
498                         };
499                 callableTasks.add(callableTask);
500                 index++;
501             }
502             Duration timeout = config.getCommandOptions().getParallelSetupTimeout();
503             executor.invokeAll(callableTasks, timeout.toMillis(), TimeUnit.MILLISECONDS);
504             if (executor.hasErrors()) {
505                 List<Throwable> errors = executor.getErrors();
506                 // TODO: Handle throwing multi-exceptions, right now throw the first one.
507                 for (Throwable error : errors) {
508                     if (error instanceof TargetSetupError) {
509                         throw (TargetSetupError) error;
510                     }
511                     if (error instanceof BuildError) {
512                         throw (BuildError) error;
513                     }
514                     if (error instanceof DeviceNotAvailableException) {
515                         throw (DeviceNotAvailableException) error;
516                     }
517                     throw new RuntimeException(error);
518                 }
519             }
520         } else {
521             for (String deviceName : testInfo.getContext().getDeviceConfigNames()) {
522                 runPreparationOnDevice(
523                         testInfo,
524                         deviceName,
525                         index,
526                         getTargetPreparersToRun(config, deviceName),
527                         mTrackTargetPreparers.get(deviceName),
528                         listener);
529                 index++;
530             }
531         }
532     }
533 
runLabPreparationOnDevice( TestInformation testInfo, String deviceName, int index, List<ITargetPreparer> labPreparersToRun, Set<ITargetPreparer> trackLabPreparers, ITestLogger logger)534     private void runLabPreparationOnDevice(
535             TestInformation testInfo,
536             String deviceName,
537             int index,
538             List<ITargetPreparer> labPreparersToRun,
539             Set<ITargetPreparer> trackLabPreparers,
540             ITestLogger logger)
541             throws TargetSetupError, BuildError, DeviceNotAvailableException {
542         ITestDevice device = testInfo.getContext().getDevice(deviceName);
543 
544         // Run lab preparers on the device
545         for (ITargetPreparer preparer : labPreparersToRun) {
546             if (preparer.isDisabled()) {
547                 CLog.d("%s has been disabled. skipping.", preparer);
548                 continue;
549             }
550             // Track object invoked as lab_preparer that are not ILabPreparer
551             if (!(preparer instanceof ILabPreparer)) {
552                 InvocationMetricLogger.addInvocationMetrics(
553                         InvocationMetricKey.LAB_PREPARER_NOT_ILAB,
554                         preparer.getClass().getCanonicalName());
555             }
556 
557             TfObjectTracker.countWithParents(preparer.getClass());
558             if (preparer instanceof ITestLoggerReceiver) {
559                 ((ITestLoggerReceiver) preparer).setTestLogger(logger);
560             }
561 
562             long startTime = System.currentTimeMillis();
563             CLog.d(
564                     "starting lab preparer '%s' on device: '%s'",
565                     preparer, device.getSerialNumber());
566             try (CloseableTraceScope ignore =
567                     new CloseableTraceScope(preparer.getClass().getSimpleName())) {
568                 testInfo.setActiveDeviceIndex(index);
569                 preparer.setUp(testInfo);
570             } finally {
571                 testInfo.setActiveDeviceIndex(0);
572                 long elapsedTime = System.currentTimeMillis() - startTime;
573 
574                 CLog.d(
575                         "done with lab preparer '%s' on device: '%s' in %s",
576                         preparer,
577                         device.getSerialNumber(),
578                         TimeUtil.formatElapsedTime(elapsedTime));
579 
580                 InvocationMetricLogger.addInvocationMetrics(
581                         InvocationGroupMetricKey.LAB_PREPARER_SETUP_LATENCY,
582                         preparer.getClass().getName(),
583                         elapsedTime);
584             }
585             // Track which lab preparers were executed separately from the target preparers
586             trackLabPreparers.add(preparer);
587         }
588     }
589 
runPreparationOnDevice( TestInformation testInfo, String deviceName, int index, List<ITargetPreparer> targetPreparersToRun, Set<ITargetPreparer> trackPreparers, ITestLogger logger)590     private void runPreparationOnDevice(
591             TestInformation testInfo,
592             String deviceName,
593             int index,
594             List<ITargetPreparer> targetPreparersToRun,
595             Set<ITargetPreparer> trackPreparers,
596             ITestLogger logger)
597             throws TargetSetupError, BuildError, DeviceNotAvailableException {
598         ITestDevice device = testInfo.getContext().getDevice(deviceName);
599         for (ITargetPreparer preparer : targetPreparersToRun) {
600             if (preparer.isDisabled()) {
601                 CLog.d("%s has been disabled. skipping.", preparer);
602                 continue;
603             }
604             // Track object invoked as target_preparer but is ILabPreparer
605             if (preparer instanceof ILabPreparer) {
606                 InvocationMetricLogger.addInvocationMetrics(
607                         InvocationMetricKey.TARGET_PREPARER_IS_ILAB,
608                         preparer.getClass().getCanonicalName());
609             }
610 
611             TfObjectTracker.countWithParents(preparer.getClass());
612             if (preparer instanceof ITestLoggerReceiver) {
613                 ((ITestLoggerReceiver) preparer).setTestLogger(logger);
614             }
615 
616             long startTime = System.currentTimeMillis();
617             CLog.d("starting preparer '%s' on device: '%s'", preparer, device.getSerialNumber());
618             try (CloseableTraceScope ignore =
619                     new CloseableTraceScope(preparer.getClass().getSimpleName())) {
620                 testInfo.setActiveDeviceIndex(index);
621                 preparer.setUp(testInfo);
622             } finally {
623                 testInfo.setActiveDeviceIndex(0);
624             }
625 
626             trackPreparers.add(preparer);
627             long elapsedTime = System.currentTimeMillis() - startTime;
628 
629             CLog.d(
630                     "done with preparer '%s' on device: '%s' in %s",
631                     preparer, device.getSerialNumber(), TimeUtil.formatElapsedTime(elapsedTime));
632 
633             InvocationMetricLogger.addInvocationMetrics(
634                     InvocationGroupMetricKey.TARGET_PREPARER_SETUP_LATENCY,
635                     preparer.getClass().getName(),
636                     elapsedTime);
637         }
638 
639         CLog.d("Done with setup of device: '%s'", device.getSerialNumber());
640     }
641 
642     /** {@inheritDoc} */
643     @Override
runDevicePreInvocationSetup( IInvocationContext context, IConfiguration config, ITestLogger logger)644     public void runDevicePreInvocationSetup(
645             IInvocationContext context, IConfiguration config, ITestLogger logger)
646             throws DeviceNotAvailableException, TargetSetupError {
647         if (config.getCommandOptions().shouldDisableInvocationSetupAndTeardown()) {
648             CLog.i("--disable-invocation-setup-and-teardown, skipping pre-invocation setup.");
649             return;
650         }
651         long start = System.currentTimeMillis();
652         customizeDevicePreInvocation(config, context);
653 
654         // Multi-device test scenario
655         Integer multiDeviceCount = config.getCommandOptions().getMultiDeviceCount();
656         boolean allVirtualDevices = true;
657         for (IDeviceConfiguration deviceConfig : config.getDeviceConfig()) {
658             if (!deviceConfig.getDeviceRequirements().gceDeviceRequested()) {
659                 allVirtualDevices = false;
660                 break;
661             }
662         }
663         if (multiDeviceCount != null && multiDeviceCount != 1 && allVirtualDevices) {
664             try (CloseableTraceScope ignore =
665                     new CloseableTraceScope("runMultiVirtualDevicesPreInvocationSetup")) {
666                 runMultiVirtualDevicesPreInvocationSetup(context, config, logger);
667             } catch (TargetSetupError e) {
668                 // TODO(b/353826394): Refactor when avd_util wrapping is ready.
669                 if (context.getDevices().get(0).getOptions().useCvdCF()) {
670                     // TODO(b/353649277): Flesh out this section when it's ready.
671                     // Basically, the rough processes to pull CF host logs are
672                     // 1. establish the CURL connection via LHP or SSH.
673                     // 2. Compose CURL command and execute it to pull CF logs.
674                 } else {
675                     OxygenUtil util = new OxygenUtil();
676                     util.downloadLaunchFailureLogs(e, logger);
677                 }
678                 throw e;
679             }
680         } else {
681             try (CloseableTraceScope ignore =
682                     new CloseableTraceScope("device_pre_invocation_setup")) {
683                 List<String> deviceNames = context.getDeviceConfigNames();
684                 if (config.getCommandOptions().shouldUseParallelPreInvocationSetup()
685                         && deviceNames.size() > 1) {
686                     CLog.d("Using parallel preInvocationSetup.");
687                     List<Callable<Void>> callableTasks = new ArrayList<>();
688                     for (String deviceName : deviceNames) {
689                         callableTasks.add(
690                                 () -> {
691                                     runSingleDevicePreInvocationSetup(
692                                             deviceName, context, config, logger);
693                                     return null;
694                                 });
695                     }
696                     // The threads are also controlled by
697                     // host_options:concurrent-virtual-device-startup-limit.
698                     ParallelDeviceExecutor<Void> executor =
699                             new ParallelDeviceExecutor<>(callableTasks.size());
700                     executor.invokeAll(
701                             callableTasks,
702                             config.getCommandOptions()
703                                     .getParallelPreInvocationSetupTimeout()
704                                     .toMillis(),
705                             TimeUnit.MILLISECONDS);
706                     // TODO: Handle throwing multi-exceptions, right now throw the first one.
707                     for (Throwable error : executor.getErrors()) {
708                         if (error instanceof DeviceNotAvailableException) {
709                             throw (DeviceNotAvailableException) error;
710                         }
711                         if (error instanceof TargetSetupError) {
712                             throw (TargetSetupError) error;
713                         }
714                         throw new RuntimeException(error);
715                     }
716                 } else {
717                     if (config.getCommandOptions().shouldUseParallelPreInvocationSetup()) {
718                         CLog.w("Parallel pre-invocation setup is enabled but device count <= 1.");
719                     }
720                     for (String deviceName : deviceNames) {
721                         runSingleDevicePreInvocationSetup(deviceName, context, config, logger);
722                     }
723                 }
724             }
725         }
726         // Also report device pre invocation into setup
727         InvocationMetricLogger.addInvocationPairMetrics(
728                 InvocationMetricKey.SETUP_PAIR, start, System.currentTimeMillis());
729     }
730 
731     /**
732      * Launch multiple virtual devices together, then invoke the {@link
733      * ITestDevice#preInvocationSetup(IBuildInfo)} for each device part of the invocation with
734      * setting the GceAvdInfo of the device beforehand.
735      *
736      * @param context the {@link IInvocationContext} of the invocation.
737      * @param config the {@link IConfiguration} of this test run.
738      * @param logger the {@link ITestLogger} to report logs.
739      * @throws DeviceNotAvailableException
740      * @throws TargetSetupError
741      */
runMultiVirtualDevicesPreInvocationSetup( IInvocationContext context, IConfiguration config, ITestLogger logger)742     private void runMultiVirtualDevicesPreInvocationSetup(
743             IInvocationContext context, IConfiguration config, ITestLogger logger)
744             throws TargetSetupError, DeviceNotAvailableException {
745         // One GceManager is needed to lease the whole device group
746         String firstDeviceName = context.getDeviceConfigNames().get(0);
747         ITestDevice firstDevice = context.getDevice(firstDeviceName);
748         mMultiDeviceRequester =
749                 new GceManager(
750                         firstDevice.getDeviceDescriptor(),
751                         firstDevice.getOptions(),
752                         context.getBuildInfo(firstDeviceName));
753 
754         List<ITestDevice> devices = context.getDevices();
755         List<IBuildInfo> buildInfos = context.getBuildInfos();
756         // Set logger on all devices first
757         for (ITestDevice device : devices) {
758             if (device instanceof ITestLoggerReceiver) {
759                 ((ITestLoggerReceiver) device).setTestLogger(logger);
760             }
761         }
762 
763         // Start multiple devices in a group
764         List<GceAvdInfo> gceAvdInfoList =
765                 mMultiDeviceRequester.startMultiDevicesGce(buildInfos, context.getAttributes());
766         for (int i = 0; i < devices.size(); i++) {
767             // For each device, do setup with its GceAvdInfo
768             CLog.d(
769                     "Starting device pre invocation launched device setup with GceAvdInfo %s"
770                             + " for : '%s'",
771                     gceAvdInfoList.get(i), devices.get(i).getSerialNumber());
772             // Use the most common basic interface for device connection setup
773             NativeDevice device = (NativeDevice) devices.get(i);
774 
775             device.setConnectionAvdInfo(gceAvdInfoList.get(i));
776             device.preInvocationSetup(buildInfos.get(i), context.getAttributes());
777 
778             // Last device in the group is responsible for releasing the whole device group
779             if (i != devices.size() - 1) {
780                 CLog.d(
781                         "Set device %s to skip tear down because only the last device in the"
782                                 + " device group will be responsible for tearing down the whole"
783                                 + " device group",
784                         device.getSerialNumber());
785                 device.getOptions().setSkipTearDown(true);
786             }
787         }
788     }
789 
790     /**
791      * Run preInvocationSetup for one device.
792      *
793      * @param deviceName the name of the device to be set up.
794      * @param context the {@link IInvocationContext} of the invocation.
795      * @param config the {@link IConfiguration} of this test run.
796      * @param logger the {@link ITestLogger} to report logs.
797      * @throws DeviceNotAvailableException
798      * @throws TargetSetupError
799      */
runSingleDevicePreInvocationSetup( String deviceName, IInvocationContext context, IConfiguration config, ITestLogger logger)800     private void runSingleDevicePreInvocationSetup(
801             String deviceName,
802             IInvocationContext context,
803             IConfiguration config,
804             ITestLogger logger)
805             throws DeviceNotAvailableException, TargetSetupError {
806         ITestDevice device = context.getDevice(deviceName);
807         CLog.d("Starting device pre invocation setup for : '%s'", device.getSerialNumber());
808         if (device instanceof ITestLoggerReceiver) {
809             ((ITestLoggerReceiver) context.getDevice(deviceName)).setTestLogger(logger);
810         }
811         IDeviceConfiguration deviceConfig = config.getDeviceConfigByName(deviceName);
812         if (deviceConfig != null && deviceConfig.isFake()) {
813             CLog.d("Skip preInvocationSetup on fake device %s", device);
814         } else {
815             device.preInvocationSetup(context.getBuildInfo(deviceName), context.getAttributes());
816         }
817     }
818 
819     /**
820      * Give a chance to customize some of the device before preInvocationSetup.
821      *
822      * @param config The config of the invocation.
823      * @param context The current invocation context.
824      */
customizeDevicePreInvocation(IConfiguration config, IInvocationContext context)825     protected void customizeDevicePreInvocation(IConfiguration config, IInvocationContext context) {
826         // Empty by default
827     }
828 
829     /** {@inheritDoc} */
830     @Override
runDevicePostInvocationTearDown( IInvocationContext context, IConfiguration config, Throwable exception)831     public void runDevicePostInvocationTearDown(
832             IInvocationContext context, IConfiguration config, Throwable exception) {
833         // Extra tear down step for the device
834         if (config.getCommandOptions().shouldDisableInvocationSetupAndTeardown()) {
835             CLog.i("--disable-invocation-setup-and-teardown, skipping post-invocation teardown.");
836             return;
837         }
838         // Check if device tear down is needed for multi-device tests.
839         boolean shouldTearDown = false;
840         for (String deviceName : context.getDeviceConfigNames()) {
841             ITestDevice device = context.getDevice(deviceName);
842             IDeviceConfiguration deviceConfig = config.getDeviceConfigByName(deviceName);
843             if (deviceConfig != null && deviceConfig.isFake()) {
844                 CLog.d("Skip postInvocationTearDown on fake device %s", device);
845                 continue;
846             }
847             // For multi-device tests, only the last device is flagged to be tear down if needed.
848             shouldTearDown |= !device.getOptions().shouldSkipTearDown();
849             device.postInvocationTearDown(exception);
850         }
851         if (mMultiDeviceRequester != null && shouldTearDown) {
852             mMultiDeviceRequester.shutdownGce();
853         }
854     }
855 
856     /** Runs the {@link IMultiTargetPreparer} specified. */
runMultiTargetPreparers( List<IMultiTargetPreparer> multiPreparers, ITestLogger logger, TestInformation testInfo, String description)857     private void runMultiTargetPreparers(
858             List<IMultiTargetPreparer> multiPreparers,
859             ITestLogger logger,
860             TestInformation testInfo,
861             String description)
862             throws TargetSetupError, BuildError, DeviceNotAvailableException {
863         if (mTrackMultiPreparers == null) {
864             mTrackMultiPreparers = new HashSet<>();
865         }
866         for (IMultiTargetPreparer multiPreparer : multiPreparers) {
867             // do not call the preparer if it was disabled
868             if (multiPreparer.isDisabled()) {
869                 CLog.d("%s has been disabled. skipping.", multiPreparer);
870                 continue;
871             }
872             TfObjectTracker.countWithParents(multiPreparer.getClass());
873             if (multiPreparer instanceof ITestLoggerReceiver) {
874                 ((ITestLoggerReceiver) multiPreparer).setTestLogger(logger);
875             }
876             long startTime = System.currentTimeMillis();
877             try (CloseableTraceScope ignore =
878                     new CloseableTraceScope(multiPreparer.getClass().getSimpleName())) {
879                 CLog.d("Starting %s '%s'", description, multiPreparer);
880                 multiPreparer.setUp(testInfo);
881                 mTrackMultiPreparers.add(multiPreparer);
882                 long elapsedTime = System.currentTimeMillis() - startTime;
883                 CLog.d(
884                         "Done with %s '%s' in %s",
885                         description, multiPreparer, TimeUtil.formatElapsedTime(elapsedTime));
886             }
887         }
888     }
889 
890     /** Runs the {@link IMultiTargetPreparer} specified tearDown. */
runMultiTargetPreparersTearDown( List<IMultiTargetPreparer> multiPreparers, TestInformation testInfo, ITestLogger logger, Throwable throwable, String description)891     private Throwable runMultiTargetPreparersTearDown(
892             List<IMultiTargetPreparer> multiPreparers,
893             TestInformation testInfo,
894             ITestLogger logger,
895             Throwable throwable,
896             String description)
897             throws Throwable {
898         ListIterator<IMultiTargetPreparer> iterator =
899                 multiPreparers.listIterator(multiPreparers.size());
900         Throwable deferredThrowable = null;
901 
902         while (iterator.hasPrevious()) {
903             IMultiTargetPreparer multipreparer = iterator.previous();
904             if (multipreparer.isDisabled() || multipreparer.isTearDownDisabled()) {
905                 CLog.d("%s has been disabled. skipping.", multipreparer);
906                 continue;
907             }
908             if (mTrackMultiPreparers == null || !mTrackMultiPreparers.contains(multipreparer)) {
909                 CLog.d("%s didn't run setUp, skipping tearDown.", multipreparer);
910                 continue;
911             }
912             if (multipreparer instanceof ITestLoggerReceiver) {
913                 ((ITestLoggerReceiver) multipreparer).setTestLogger(logger);
914             }
915             long startTime = System.currentTimeMillis();
916             CLog.d("Starting %s '%s'", description, multipreparer);
917             try (CloseableTraceScope ignore =
918                     new CloseableTraceScope(multipreparer.getClass().getSimpleName())) {
919                 multipreparer.tearDown(testInfo, throwable);
920             } catch (Throwable t) {
921                 // We catch it and rethrow later to allow each multi_targetprep to be attempted.
922                 // Only the first one will be thrown but all should be logged.
923                 CLog.e("Deferring throw for:");
924                 CLog.e(t);
925                 if (deferredThrowable == null) {
926                     deferredThrowable = t;
927                 }
928             }
929             long elapsedTime = System.currentTimeMillis() - startTime;
930 
931             CLog.d(
932                     "Done with %s '%s' in %s",
933                     description, multipreparer, TimeUtil.formatElapsedTime(elapsedTime));
934             InvocationMetricLogger.addInvocationMetrics(
935                     InvocationGroupMetricKey.MULTI_TARGET_PREPARER_TEARDOWN_LATENCY,
936                     multipreparer.getClass().getName(),
937                     elapsedTime);
938         }
939 
940         return deferredThrowable;
941     }
942 
943     @Override
doTeardown( TestInformation testInfo, IConfiguration config, ITestLogger logger, Throwable exception)944     public void doTeardown(
945             TestInformation testInfo,
946             IConfiguration config,
947             ITestLogger logger,
948             Throwable exception)
949             throws Throwable {
950         IInvocationContext context = testInfo.getContext();
951         Throwable deferredThrowable;
952         long start = System.currentTimeMillis();
953         InvocationMetricLogger.addInvocationMetrics(InvocationMetricKey.TEARDOWN_START, start);
954         try {
955             int deviceIndex = 0;
956             try {
957                 List<IMultiTargetPreparer> multiPreparers = config.getMultiTargetPreparers();
958                 deferredThrowable =
959                         runMultiTargetPreparersTearDown(
960                                 multiPreparers,
961                                 testInfo,
962                                 logger,
963                                 exception,
964                                 "multi target preparer teardown");
965 
966                 for (String deviceName : context.getDeviceConfigNames()) {
967                     ITestDevice device = context.getDevice(deviceName);
968                     device.clearLastConnectedWifiNetwork();
969 
970                     List<ITargetPreparer> targetPreparersToRun =
971                             getTargetPreparersToRun(config, deviceName);
972                     Throwable firstLocalThrowable =
973                             runPreparersTearDown(
974                                     testInfo,
975                                     device,
976                                     deviceName,
977                                     deviceIndex,
978                                     logger,
979                                     exception,
980                                     targetPreparersToRun,
981                                     mTrackTargetPreparers);
982                     if (deferredThrowable == null) {
983                         deferredThrowable = firstLocalThrowable;
984                     }
985 
986                     deviceIndex++;
987                 }
988 
989                 if (exception == null) {
990                     exception = deferredThrowable;
991                 }
992             } finally {
993                 InvocationMetricLogger.addInvocationPairMetrics(
994                         InvocationMetricKey.TEST_TEARDOWN_PAIR, start, System.currentTimeMillis());
995             }
996 
997             start = System.currentTimeMillis();
998             try {
999                 deviceIndex = 0;
1000                 for (String deviceName : context.getDeviceConfigNames()) {
1001                     ITestDevice device = context.getDevice(deviceName);
1002                     List<ITargetPreparer> labPreparersToRun =
1003                             getLabPreparersToRun(config, deviceName);
1004                     Throwable secondLocalThrowable =
1005                             runPreparersTearDown(
1006                                     testInfo,
1007                                     device,
1008                                     deviceName,
1009                                     deviceIndex,
1010                                     logger,
1011                                     exception,
1012                                     labPreparersToRun,
1013                                     mTrackLabPreparers);
1014                     if (deferredThrowable == null) {
1015                         deferredThrowable = secondLocalThrowable;
1016                     }
1017 
1018                     deviceIndex++;
1019                 }
1020 
1021                 if (exception == null) {
1022                     exception = deferredThrowable;
1023                 }
1024                 // Extra tear down step for the device
1025                 runDevicePostInvocationTearDown(context, config, exception);
1026 
1027                 // After all, run the multi_pre_target_preparer tearDown.
1028                 List<IMultiTargetPreparer> multiPrePreparers = config.getMultiPreTargetPreparers();
1029                 Throwable preTargetTearDownException =
1030                         runMultiTargetPreparersTearDown(
1031                                 multiPrePreparers,
1032                                 testInfo,
1033                                 logger,
1034                                 exception,
1035                                 "multi pre target preparer teardown");
1036                 if (deferredThrowable == null) {
1037                     deferredThrowable = preTargetTearDownException;
1038                 }
1039             } finally {
1040                 InvocationMetricLogger.addInvocationPairMetrics(
1041                         InvocationMetricKey.TEARDOWN_PAIR, start, System.currentTimeMillis());
1042             }
1043         } finally {
1044             // Collect adb logs.
1045             logHostAdb(config, logger);
1046             InvocationMetricLogger.addInvocationMetrics(
1047                     InvocationMetricKey.TEARDOWN_END, System.currentTimeMillis());
1048         }
1049 
1050         if (deferredThrowable != null) {
1051             throw deferredThrowable;
1052         }
1053     }
1054 
runPreparersTearDown( TestInformation testInfo, ITestDevice device, String deviceName, int deviceIndex, ITestLogger logger, Throwable exception, List<ITargetPreparer> preparersToRun, Map<String, Set<ITargetPreparer>> trackPreparersMap)1055     protected Throwable runPreparersTearDown(
1056             TestInformation testInfo,
1057             ITestDevice device,
1058             String deviceName,
1059             int deviceIndex,
1060             ITestLogger logger,
1061             Throwable exception,
1062             List<ITargetPreparer> preparersToRun,
1063             Map<String, Set<ITargetPreparer>> trackPreparersMap) {
1064         Throwable deferredThrowable = null;
1065         ListIterator<ITargetPreparer> itr = preparersToRun.listIterator(preparersToRun.size());
1066         while (itr.hasPrevious()) {
1067             ITargetPreparer preparer = itr.previous();
1068             // do not call the cleaner if it was disabled
1069             if (preparer.isDisabled() || preparer.isTearDownDisabled()) {
1070                 CLog.d("%s has been disabled. skipping.", preparer);
1071                 continue;
1072             }
1073             if (trackPreparersMap == null
1074                     || !trackPreparersMap.containsKey(deviceName)
1075                     || !trackPreparersMap.get(deviceName).contains(preparer)) {
1076                 CLog.d("%s didn't run setUp, skipping tearDown.", preparer);
1077                 continue;
1078             }
1079             // If setup hit a targetSetupError, the setUp() and setTestLogger might not have
1080             // run, ensure we still have the logger.
1081             if (preparer instanceof ITestLoggerReceiver) {
1082                 ((ITestLoggerReceiver) preparer).setTestLogger(logger);
1083             }
1084             long startTime = System.currentTimeMillis();
1085             try (CloseableTraceScope remoteTest =
1086                     new CloseableTraceScope(preparer.getClass().getSimpleName())) {
1087                 CLog.d(
1088                         "starting tearDown '%s' on device: '%s'",
1089                         preparer, device.getSerialNumber());
1090                 testInfo.setActiveDeviceIndex(deviceIndex);
1091                 Throwable tearDownException = exception;
1092                 // If a previous teardown fail, still notify following ones.
1093                 if (exception == null && deferredThrowable != null) {
1094                     tearDownException = deferredThrowable;
1095                 }
1096                 preparer.tearDown(testInfo, tearDownException);
1097             } catch (Throwable e) {
1098                 // We catch it and rethrow later to allow each targetprep to be attempted.
1099                 // Only the first one will be thrown but all should be logged.
1100                 CLog.e("Deferring throw for:");
1101                 CLog.e(e);
1102                 if (deferredThrowable == null) {
1103                     deferredThrowable = e;
1104                 }
1105             } finally {
1106                 testInfo.setActiveDeviceIndex(0);
1107                 long elapsedTime = System.currentTimeMillis() - startTime;
1108                 CLog.d(
1109                         "done with tearDown '%s' on device: '%s' in %s",
1110                         preparer,
1111                         device.getSerialNumber(),
1112                         TimeUtil.formatElapsedTime(elapsedTime));
1113                 InvocationMetricLogger.addInvocationMetrics(
1114                         InvocationGroupMetricKey.TARGET_PREPARER_TEARDOWN_LATENCY,
1115                         preparer.getClass().getName(),
1116                         elapsedTime);
1117             }
1118         }
1119         return deferredThrowable;
1120     }
1121 
1122     @Override
doCleanUp(IInvocationContext context, IConfiguration config, Throwable exception)1123     public void doCleanUp(IInvocationContext context, IConfiguration config, Throwable exception) {
1124         for (String deviceName : context.getDeviceConfigNames()) {
1125 
1126             List<ITargetPreparer> targetPreparers = getTargetPreparersToRun(config, deviceName);
1127 
1128             ListIterator<ITargetPreparer> itr =
1129                     targetPreparers.listIterator(targetPreparers.size());
1130             while (itr.hasPrevious()) {
1131                 ITargetPreparer preparer = itr.previous();
1132                 if (preparer instanceof IHostCleaner) {
1133                     IHostCleaner cleaner = (IHostCleaner) preparer;
1134                     if (preparer.isDisabled() || preparer.isTearDownDisabled()) {
1135                         CLog.d("%s has been disabled. skipping.", cleaner);
1136                         continue;
1137                     }
1138                     cleaner.cleanUp(context.getBuildInfo(deviceName), exception);
1139                 }
1140             }
1141 
1142             List<ITargetPreparer> labPreparers = getLabPreparersToRun(config, deviceName);
1143 
1144             // Yes this ends up very redundant to the above stanza, but 8 lines isn't really worth
1145             // extracting to a helper method.
1146             itr = labPreparers.listIterator(labPreparers.size());
1147             while (itr.hasPrevious()) {
1148                 ITargetPreparer preparer = itr.previous();
1149                 if (preparer instanceof IHostCleaner) {
1150                     IHostCleaner cleaner = (IHostCleaner) preparer;
1151                     if (preparer.isDisabled() || preparer.isTearDownDisabled()) {
1152                         CLog.d("%s has been disabled. skipping.", cleaner);
1153                         continue;
1154                     }
1155                     cleaner.cleanUp(context.getBuildInfo(deviceName), exception);
1156                 }
1157             }
1158         }
1159     }
1160 
1161     @Override
runTests( TestInformation info, IConfiguration config, ITestInvocationListener listener)1162     public void runTests(
1163             TestInformation info, IConfiguration config, ITestInvocationListener listener)
1164             throws Throwable {
1165         Timer testPhaseTimer = new Timer(true);
1166         long remainingTestPhaseTime =
1167                 GlobalConfiguration.getInstance().getHostOptions().getTestPhaseTimeout();
1168         boolean testPhaseTimeoutNeeded = remainingTestPhaseTime > 0;
1169         // Make sure Test Phase timeout is less than or equal to invocation timeout
1170         long invocationTimeout = config.getCommandOptions().getInvocationTimeout();
1171         if (testPhaseTimeoutNeeded && invocationTimeout > 0) {
1172             remainingTestPhaseTime = Math.min(remainingTestPhaseTime, invocationTimeout);
1173         }
1174 
1175         List<IRemoteTest> remainingTests = new ArrayList<>(config.getTests());
1176         UnexecutedTestReporterThread reporterThread =
1177                 new UnexecutedTestReporterThread(listener, remainingTests);
1178         Runtime.getRuntime().addShutdownHook(reporterThread);
1179         TestInvocation.printStageDelimiter(Stage.TEST, false);
1180         long start = System.currentTimeMillis();
1181         try (CloseableTraceScope ignored =
1182                 new CloseableTraceScope(InvocationMetricKey.test_execution.name())) {
1183             GetPreviousPassedHelper previousPassHelper = new GetPreviousPassedHelper();
1184             // Add new exclude filters to global filters
1185             Set<String> previousPassedFilters = previousPassHelper.getPreviousPassedFilters(config);
1186             // TODO: Ensure global filters are cloned for local sharding
1187             config.getGlobalFilters().addPreviousPassedTests(previousPassedFilters);
1188             for (IRemoteTest test : config.getTests()) {
1189                 try (CloseableTraceScope remoteTest =
1190                         new CloseableTraceScope(test.getClass().getSimpleName())) {
1191                     TfObjectTracker.countWithParents(test.getClass());
1192                     // For compatibility of those receivers, they are assumed to be single device
1193                     // alloc.
1194                     if (test instanceof IDeviceTest) {
1195                         ((IDeviceTest) test).setDevice(info.getDevice());
1196                     }
1197                     if (test instanceof IBuildReceiver) {
1198                         ((IBuildReceiver) test).setBuild(info.getBuildInfo());
1199                     }
1200                     if (test instanceof ISystemStatusCheckerReceiver) {
1201                         ((ISystemStatusCheckerReceiver) test)
1202                                 .setSystemStatusChecker(config.getSystemStatusCheckers());
1203                     }
1204                     if (test instanceof IInvocationContextReceiver) {
1205                         ((IInvocationContextReceiver) test).setInvocationContext(info.getContext());
1206                     }
1207 
1208                     updateAutoCollectors(config);
1209 
1210                     IRetryDecision decision = config.getRetryDecision();
1211                     // Apply the filters
1212                     if (test instanceof ITestFilterReceiver) {
1213                         config.getGlobalFilters().applyFiltersToTest((ITestFilterReceiver) test);
1214                     } else if (test instanceof BaseTestSuite) {
1215                         config.getGlobalFilters().applyFiltersToTest((BaseTestSuite) test);
1216                     }
1217                     // Handle the no-retry use case
1218                     if (!decision.isAutoRetryEnabled()
1219                             || RetryStrategy.NO_RETRY.equals(decision.getRetryStrategy())
1220                             || test instanceof ITestSuite
1221                             // Exclude special launcher
1222                             || test.getClass().getSimpleName().equals("CtsTestLauncher")
1223                             // TODO: Handle auto-retry in local-sharding for non-suite
1224                             || test instanceof TestsPoolPoller
1225                             // If test doesn't support auto-retry
1226                             || (!(test instanceof ITestFilterReceiver)
1227                                     && !(test instanceof IAutoRetriableTest)
1228                                     && !RetryStrategy.ITERATIONS.equals(
1229                                             decision.getRetryStrategy()))) {
1230                         try {
1231                             long timeSpentOnTest =
1232                                     runTest(
1233                                             config,
1234                                             info,
1235                                             listener,
1236                                             test,
1237                                             testPhaseTimer,
1238                                             remainingTestPhaseTime,
1239                                             testPhaseTimeoutNeeded);
1240                             remainingTestPhaseTime -= timeSpentOnTest;
1241                         } finally {
1242                             CurrentInvocation.setRunIsolation(IsolationGrade.NOT_ISOLATED);
1243                             CurrentInvocation.setModuleIsolation(IsolationGrade.NOT_ISOLATED);
1244                             // Clean the suite internals once done
1245                             if (test instanceof BaseTestSuite) {
1246                                 ((BaseTestSuite) test).cleanUpSuiteSetup();
1247                             }
1248                         }
1249                         remainingTests.remove(test);
1250                         continue;
1251                     }
1252                     CLog.d("Using RetryLogSaverResultForwarder to forward results.");
1253                     ModuleListener mainGranularRunListener =
1254                             new ModuleListener(null, info.getContext());
1255                     RetryLogSaverResultForwarder runListener =
1256                             initializeListeners(config, listener, mainGranularRunListener);
1257                     mainGranularRunListener.setAttemptIsolation(
1258                             CurrentInvocation.runCurrentIsolation());
1259                     try {
1260                         long timeSpentOnTest =
1261                                 runTest(
1262                                         config,
1263                                         info,
1264                                         runListener,
1265                                         test,
1266                                         testPhaseTimer,
1267                                         remainingTestPhaseTime,
1268                                         testPhaseTimeoutNeeded);
1269                         remainingTestPhaseTime -= timeSpentOnTest;
1270                     } finally {
1271                         CurrentInvocation.setRunIsolation(IsolationGrade.NOT_ISOLATED);
1272                         CurrentInvocation.setModuleIsolation(IsolationGrade.NOT_ISOLATED);
1273                     }
1274                     remainingTests.remove(test);
1275                     runListener.incrementAttempt();
1276 
1277                     // Avoid entering the loop if no retry to be done.
1278                     if (!decision.shouldRetry(
1279                             test, 0, mainGranularRunListener.getTestRunForAttempts(0))) {
1280                         continue;
1281                     }
1282                     // Avoid rechecking the shouldRetry below the first time as it could retrigger
1283                     // reboot.
1284                     boolean firstCheck = true;
1285                     long startTime = System.currentTimeMillis();
1286                     try {
1287                         PrettyPrintDelimiter.printStageDelimiter("Starting auto-retry");
1288                         for (int attemptNumber = 1;
1289                                 attemptNumber < decision.getMaxTestRunAttempts();
1290                                 attemptNumber++) {
1291                             if (!firstCheck) {
1292                                 boolean retry =
1293                                         decision.shouldRetry(
1294                                                 test,
1295                                                 attemptNumber - 1,
1296                                                 mainGranularRunListener.getTestRunForAttempts(
1297                                                         attemptNumber - 1));
1298                                 if (!retry) {
1299                                     continue;
1300                                 }
1301                             }
1302                             firstCheck = false;
1303                             CLog.d("auto-retry attempt number '%s'", attemptNumber);
1304                             mainGranularRunListener.setAttemptIsolation(
1305                                     CurrentInvocation.runCurrentIsolation());
1306                             try {
1307                                 // Run the tests again
1308                                 long timeSpent =
1309                                         runTest(
1310                                                 config,
1311                                                 info,
1312                                                 runListener,
1313                                                 test,
1314                                                 testPhaseTimer,
1315                                                 remainingTestPhaseTime,
1316                                                 testPhaseTimeoutNeeded);
1317                                 remainingTestPhaseTime -= timeSpent;
1318                             } finally {
1319                                 CurrentInvocation.setRunIsolation(IsolationGrade.NOT_ISOLATED);
1320                                 CurrentInvocation.setModuleIsolation(IsolationGrade.NOT_ISOLATED);
1321                             }
1322                             runListener.incrementAttempt();
1323                         }
1324                         // Feed the last attempt if we reached here.
1325                         decision.addLastAttempt(
1326                                 mainGranularRunListener.getTestRunForAttempts(
1327                                         decision.getMaxTestRunAttempts() - 1));
1328                     } finally {
1329                         RetryStatistics retryStats = decision.getRetryStatistics();
1330                         // Track how long we spend in retry
1331                         retryStats.mRetryTime = System.currentTimeMillis() - startTime;
1332                         addRetryTime(retryStats.mRetryTime);
1333                     }
1334                 }
1335             }
1336         } finally {
1337             testPhaseTimer.cancel();
1338             TestInvocation.printStageDelimiter(Stage.TEST, true);
1339             // TODO: Look if this can be improved to DeviceNotAvailableException too.
1340             try {
1341                 Runtime.getRuntime().removeShutdownHook(reporterThread);
1342             } catch (IllegalStateException e) {
1343                 // Ignore as it would throw only if JVM shutdown is in progress.
1344             }
1345             // Only log if it was no already logged to keep the value closest to execution
1346             if (!InvocationMetricLogger.getInvocationMetrics()
1347                     .containsKey(InvocationMetricKey.TEST_PAIR.toString())) {
1348                 InvocationMetricLogger.addInvocationPairMetrics(
1349                         InvocationMetricKey.TEST_PAIR, start, System.currentTimeMillis());
1350             }
1351         }
1352     }
1353 
1354     @Override
reportLogs(ITestDevice device, ITestLogger listener, Stage stage)1355     public void reportLogs(ITestDevice device, ITestLogger listener, Stage stage) {
1356         if (device == null) {
1357             return;
1358         }
1359         IDevice idevice = device.getIDevice();
1360         try (InputStreamSource logcatSource = device.getLogcat()) {
1361             device.clearLogcat();
1362             if (logcatSource != null && logcatSource.size() > 0L) {
1363                 String name =
1364                         String.format(
1365                                 "%s_%s",
1366                                 TestInvocation.getDeviceLogName(stage), device.getSerialNumber());
1367                 listener.testLog(name, LogDataType.LOGCAT, logcatSource);
1368             }
1369         }
1370         // Emulator logs
1371         if (idevice != null && idevice.isEmulator()) {
1372             try (InputStreamSource emulatorOutput = device.getEmulatorOutput()) {
1373                 // TODO: Clear the emulator log
1374                 String name = TestInvocation.getEmulatorLogName(stage);
1375                 listener.testLog(name, LogDataType.TEXT, emulatorOutput);
1376             }
1377         }
1378     }
1379 
1380     /** Helper to create the test tag from the configuration. */
getTestTag(IConfiguration config)1381     private String getTestTag(IConfiguration config) {
1382         String testTag = config.getCommandOptions().getTestTag();
1383         if (config.getCommandOptions().getTestTagSuffix() != null) {
1384             testTag =
1385                     String.format("%s-%s", testTag, config.getCommandOptions().getTestTagSuffix());
1386         }
1387         return testTag;
1388     }
1389 
1390     /** Handle setting the test tag on the build info. */
setTestTag(IBuildInfo info, IConfiguration config)1391     protected void setTestTag(IBuildInfo info, IConfiguration config) {
1392         // When CommandOption is set, it overrides any test-tag from build_providers
1393         if (!"stub".equals(config.getCommandOptions().getTestTag())) {
1394             info.setTestTag(getTestTag(config));
1395         } else if (Strings.isNullOrEmpty(info.getTestTag())) {
1396             // We ensure that that a default test-tag is always available.
1397             info.setTestTag("stub");
1398         }
1399     }
1400 
1401     /**
1402      * Update the {@link IBuildInfo} with additional info from the {@link IConfiguration}.
1403      *
1404      * @param info the {@link IBuildInfo}
1405      * @param config the {@link IConfiguration}
1406      */
updateBuild(IBuildInfo info, IConfiguration config)1407     void updateBuild(IBuildInfo info, IConfiguration config) {
1408         setTestTag(info, config);
1409         if (config.getCommandOptions().getInvocationData().containsKey("subprocess")) {
1410             // Avoid relogging the properties in a subprocess
1411             return;
1412         }
1413         if (config.getCommandLine() != null) {
1414             // TODO: obfuscate the password if any.
1415             info.addBuildAttribute(TestInvocation.COMMAND_ARGS_KEY, config.getCommandLine());
1416         }
1417         if (config.getCommandOptions().getShardCount() != null) {
1418             info.addBuildAttribute(
1419                     "shard_count", config.getCommandOptions().getShardCount().toString());
1420         }
1421         if (config.getCommandOptions().getShardIndex() != null) {
1422             info.addBuildAttribute(
1423                     "shard_index", config.getCommandOptions().getShardIndex().toString());
1424         }
1425     }
1426 
1427     /**
1428      * Runs a test and returns the time taken to finish the test.
1429      *
1430      * <p>Tests will be run on a separate thread with a timer when test phase level timeout is
1431      * needed.
1432      */
runTest( IConfiguration config, TestInformation info, ITestInvocationListener listener, IRemoteTest test, Timer timer, long testPhaseTimeout, boolean testPhaseTimeoutNeeded)1433     private long runTest(
1434             IConfiguration config,
1435             TestInformation info,
1436             ITestInvocationListener listener,
1437             IRemoteTest test,
1438             Timer timer,
1439             long testPhaseTimeout,
1440             boolean testPhaseTimeoutNeeded)
1441             throws DeviceNotAvailableException, Throwable {
1442         // We clone the collectors for each IRemoteTest to ensure no state conflicts.
1443         List<IMetricCollector> clonedCollectors = new ArrayList<>();
1444         // Add automated collectors
1445         for (AutoLogCollector auto : config.getCommandOptions().getAutoLogCollectors()) {
1446             clonedCollectors.add(auto.getInstanceForValue());
1447         }
1448         // Add the collector from the configuration
1449         clonedCollectors.addAll(CollectorHelper.cloneCollectors(config.getMetricCollectors()));
1450         if (test instanceof IMetricCollectorReceiver) {
1451             ((IMetricCollectorReceiver) test).setMetricCollectors(clonedCollectors);
1452             // If test can receive collectors then let it handle the how to set them up
1453             if (testPhaseTimeoutNeeded) {
1454                 return runTestThread(info, listener, test, timer, testPhaseTimeout);
1455             } else {
1456                 long startTime = System.currentTimeMillis();
1457                 test.run(info, listener);
1458                 return System.currentTimeMillis() - startTime;
1459             }
1460         } else {
1461             // Wrap collectors in each other and collection will be sequential, do this in the
1462             // loop to ensure they are always initialized against the right context.
1463             ITestInvocationListener listenerWithCollectors = listener;
1464             if (config.getCommandOptions().reportTestCaseCount()) {
1465                 CountTestCasesCollector counter = new CountTestCasesCollector(test);
1466                 clonedCollectors.add(counter);
1467             }
1468             for (IMetricCollector collector : clonedCollectors) {
1469                 if (collector.isDisabled()) {
1470                     CLog.d("%s has been disabled. Skipping.", collector);
1471                 } else {
1472                     if (collector instanceof IConfigurationReceiver) {
1473                         ((IConfigurationReceiver) collector).setConfiguration(config);
1474                     }
1475                     listenerWithCollectors =
1476                             collector.init(info.getContext(), listenerWithCollectors);
1477                     TfObjectTracker.countWithParents(collector.getClass());
1478                 }
1479             }
1480             if (testPhaseTimeoutNeeded) {
1481                 return runTestThread(info, listenerWithCollectors, test, timer, testPhaseTimeout);
1482             } else {
1483                 long startTime = System.currentTimeMillis();
1484                 test.run(info, listenerWithCollectors);
1485                 return System.currentTimeMillis() - startTime;
1486             }
1487         }
1488     }
1489 
1490     /** Runs a test in a separate thread and returns the time spent on running the test. */
runTestThread( TestInformation info, ITestInvocationListener listener, IRemoteTest test, Timer timer, long testPhaseTimeout)1491     private long runTestThread(
1492             TestInformation info,
1493             ITestInvocationListener listener,
1494             IRemoteTest test,
1495             Timer timer,
1496             long testPhaseTimeout)
1497             throws Throwable {
1498         if (testPhaseTimeout <= 0) {
1499             // throw run interrupted exception so that it can be handled the same way as TestThreads
1500             // when timeout is reached.
1501             throw new RunInterruptedException(
1502                     "Test Phase Timeout Reached.", TestErrorIdentifier.TEST_PHASE_TIMED_OUT);
1503         }
1504         TestThread testThread = new TestThread(info, listener, test);
1505         TestPhaseMonitor testPhaseMonitor = new TestPhaseMonitor(testThread);
1506         timer.schedule(testPhaseMonitor, testPhaseTimeout);
1507         long startTime = System.currentTimeMillis();
1508         testThread.start();
1509         try {
1510             testThread.join();
1511         } catch (InterruptedException e) {
1512             CLog.e(e);
1513         } finally {
1514             testPhaseMonitor.cancel();
1515             long timeSpent = System.currentTimeMillis() - startTime;
1516             if (testThread.getLastThrownException() != null) {
1517                 throw testThread.getLastThrownException();
1518             }
1519             return timeSpent;
1520         }
1521     }
1522 
initializeListeners( IConfiguration config, ITestInvocationListener mainListener, ITestInvocationListener mainGranularLevelListener)1523     private RetryLogSaverResultForwarder initializeListeners(
1524             IConfiguration config,
1525             ITestInvocationListener mainListener,
1526             ITestInvocationListener mainGranularLevelListener) {
1527         List<ITestInvocationListener> currentTestListeners = new ArrayList<>();
1528         currentTestListeners.add(mainGranularLevelListener);
1529         currentTestListeners.add(mainListener);
1530         return new RetryLogSaverResultForwarder(
1531                 config.getLogSaver(), currentTestListeners, config) {
1532             @Override
1533             public void testLog(
1534                     String dataName, LogDataType dataType, InputStreamSource dataStream) {
1535                 // We know for sure that the sub-listeners are LogSaverResultForwarder
1536                 // so we delegate to them to save and generate the logAssociation.
1537                 testLogForward(dataName, dataType, dataStream);
1538             }
1539         };
1540     }
1541 
1542     private void addRetryTime(long retryTimeMs) {
1543         // InvocationMetricLogger automatically adds the auto retry time.
1544         InvocationMetricLogger.addInvocationMetrics(
1545                 InvocationMetricKey.AUTO_RETRY_TIME, retryTimeMs);
1546     }
1547 
1548     protected void linkExternalDirs(IBuildInfo info, TestInformation testInfo) {
1549         if (info.getProperties().contains(BuildInfoProperties.DO_NOT_LINK_TESTS_DIR)) {
1550             CLog.d("Skip linking external directory as FileProperty was set.");
1551             return;
1552         }
1553         // Load environment tests dir.
1554         if (info instanceof IDeviceBuildInfo) {
1555             // TODO: Use tests directory from TestInformation instead.
1556             File testsDir = ((IDeviceBuildInfo) info).getTestsDir();
1557             if (testsDir != null && testsDir.exists()) {
1558                 if (testInfo.executionFiles().get(FilesKey.TARGET_TESTS_DIRECTORY) == null) {
1559                     File targetTestCases =
1560                             handleLinkingExternalDirs(
1561                                     (IDeviceBuildInfo) info,
1562                                     testsDir,
1563                                     EnvVariable.ANDROID_TARGET_OUT_TESTCASES,
1564                                     BuildInfoFileKey.TARGET_LINKED_DIR.getFileKey());
1565                     if (targetTestCases != null) {
1566                         testInfo.executionFiles()
1567                                 .put(FilesKey.TARGET_TESTS_DIRECTORY, targetTestCases, true);
1568                     }
1569                 }
1570                 if (testInfo.executionFiles().get(FilesKey.HOST_TESTS_DIRECTORY) == null) {
1571                     File hostTestCases =
1572                             handleLinkingExternalDirs(
1573                                     (IDeviceBuildInfo) info,
1574                                     testsDir,
1575                                     EnvVariable.ANDROID_HOST_OUT_TESTCASES,
1576                                     BuildInfoFileKey.HOST_LINKED_DIR.getFileKey());
1577                     if (hostTestCases != null) {
1578                         testInfo.executionFiles()
1579                                 .put(FilesKey.HOST_TESTS_DIRECTORY, hostTestCases, true);
1580                     }
1581                 }
1582             }
1583         }
1584     }
1585 
1586     private File handleLinkingExternalDirs(
1587             IDeviceBuildInfo info, File testsDir, EnvVariable var, String baseName) {
1588         File externalDir = getExternalTestCasesDirs(var);
1589         if (externalDir == null) {
1590             String path = SystemUtil.ENV_VARIABLE_PATHS_IN_TESTS_DIR.get(var);
1591             File varDir = FileUtil.getFileForPath(testsDir, path);
1592             if (varDir.exists()) {
1593                 // If we found a dir already in the tests dir we keep track of it
1594                 info.setFile(
1595                         baseName,
1596                         varDir,
1597                         /** version */
1598                         "v1");
1599                 return varDir;
1600             }
1601             return null;
1602         }
1603         try {
1604             // Avoid conflict by creating a randomized name for the arriving symlink file.
1605             File subDir = FileUtil.createTempDir(baseName, testsDir);
1606             subDir.delete();
1607             FileUtil.symlinkFile(externalDir, subDir);
1608             // Tag the dir in the build info to be possibly cleaned.
1609             info.setFile(
1610                     baseName,
1611                     subDir,
1612                     /** version */
1613                     "v1");
1614             // Ensure we always delete the linking, no matter how the JVM exits.
1615             subDir.deleteOnExit();
1616             return subDir;
1617         } catch (IOException e) {
1618             CLog.e("Failed to load external test dir %s. Ignoring it.", externalDir);
1619             CLog.e(e);
1620         }
1621         return null;
1622     }
1623 
1624     private void setBinariesVersion(IInvocationContext context) {
1625         String version = getAdbVersion();
1626         if (version != null) {
1627             context.addInvocationAttribute(ADB_VERSION_KEY, version);
1628         }
1629         String javaVersion = System.getProperty("java.version");
1630         if (javaVersion != null && !javaVersion.isEmpty()) {
1631             context.addInvocationAttribute(JAVA_VERSION_KEY, javaVersion);
1632         }
1633         String javaClasspath = System.getProperty("java.class.path");
1634         if (javaClasspath != null && !javaClasspath.isEmpty()) {
1635             context.addInvocationAttribute(JAVA_CLASSPATH_KEY, javaClasspath);
1636         }
1637     }
1638 
1639     private void copyRemoteFiles(ICommandOptions options, IBuildInfo info) {
1640         for (String remoteFile : options.getRemoteFiles()) {
1641             info.setFile(
1642                     IBuildInfo.REMOTE_FILE_PREFIX,
1643                     new File(remoteFile),
1644                     IBuildInfo.REMOTE_FILE_VERSION);
1645         }
1646     }
1647 
1648     /** Convert the legacy *-on-failure options to the new auto-collect. */
1649     private void updateAutoCollectors(IConfiguration config) {
1650         if (config.getCommandOptions().captureScreenshotOnFailure()) {
1651             config.getCommandOptions()
1652                     .getAutoLogCollectors()
1653                     .add(AutoLogCollector.SCREENSHOT_ON_FAILURE);
1654         }
1655         if (config.getCommandOptions().captureLogcatOnFailure()) {
1656             config.getCommandOptions()
1657                     .getAutoLogCollectors()
1658                     .add(AutoLogCollector.LOGCAT_ON_FAILURE);
1659         }
1660     }
1661 
1662     /** Collect the logs from $TMPDIR/adb.$UID.log. */
1663     @VisibleForTesting
1664     protected void logHostAdb(IConfiguration config, ITestLogger logger) {
1665         if (SystemUtil.isLocalMode()) {
1666             // Skip logging host adb locally
1667             return;
1668         }
1669         if (config.getCommandOptions().getInvocationData().containsKey("subprocess")) {
1670             // Avoid relogging the adb log in a subprocess
1671             return;
1672         }
1673         String tmpDir = "/tmp";
1674         if (System.getenv("TMPDIR") != null) {
1675             tmpDir = System.getenv("TMPDIR");
1676         }
1677         CommandResult uidRes =
1678                 RunUtil.getDefault()
1679                         .runTimedCmd(60000, "id", "-u", System.getProperty("user.name"));
1680         if (!CommandStatus.SUCCESS.equals(uidRes.getStatus())) {
1681             CLog.e("Failed to collect UID for adb logs: %s", uidRes.getStderr());
1682             return;
1683         }
1684         String uid = uidRes.getStdout().trim();
1685         File adbLog = new File(tmpDir, String.format("adb.%s.log", uid));
1686         if (!adbLog.exists()) {
1687             CLog.i("Did not find adb log file: %s, upload skipped.", adbLog);
1688             return;
1689         }
1690         CommandResult truncAdb =
1691                 RunUtil.getDefault()
1692                         .runTimedCmd(60000, "tail", "-c", "10MB", adbLog.getAbsolutePath());
1693         if (!CommandStatus.SUCCESS.equals(truncAdb.getStatus())) {
1694             CLog.e("Failed to truncate the adb log: %s\n%s", adbLog, truncAdb.getStderr());
1695             return;
1696         }
1697         try (InputStreamSource source =
1698                 new ByteArrayInputStreamSource(truncAdb.getStdout().getBytes())) {
1699             logger.testLog("host_adb_log", LogDataType.ADB_HOST_LOG, source);
1700         }
1701     }
1702 
1703     /** Returns the external directory coming from the environment. */
1704     @VisibleForTesting
1705     File getExternalTestCasesDirs(EnvVariable envVar) {
1706         return SystemUtil.getExternalTestCasesDir(envVar);
1707     }
1708 
1709     /** Returns the adb version in use for the invocation. */
1710     protected String getAdbVersion() {
1711         return GlobalConfiguration.getDeviceManagerInstance().getAdbVersion();
1712     }
1713 
1714     /** Collect automatically some information on the primary device under test. */
1715     protected void collectAutoInfo(IConfiguration config, TestInformation info)
1716             throws DeviceNotAvailableException {
1717         if (SystemUtil.isLocalMode()) {
1718             // Avoid collecting for local modes since data collected in this method is used
1719             // in CI only.
1720             return;
1721         }
1722         if (config.getCommandOptions().getInvocationData().containsKey("subprocess")) {
1723             // Avoid logging in the subprocess
1724             return;
1725         }
1726         ITestDevice device = info.getDevice();
1727         if (device.getIDevice() instanceof StubDevice) {
1728             return;
1729         }
1730         try (CloseableTraceScope ignored = new CloseableTraceScope("collect_device_info")) {
1731             CommandResult kernelInfoResult = device.executeShellV2Command("uname -a");
1732             if (kernelInfoResult != null
1733                     && CommandStatus.SUCCESS.equals(kernelInfoResult.getStatus())) {
1734                 CLog.i(
1735                         "Device %s kernel information: '%s'",
1736                         device.getSerialNumber(), kernelInfoResult.getStdout().trim());
1737                 info.getBuildInfo()
1738                         .addBuildAttribute(
1739                                 "device_kernel_info", kernelInfoResult.getStdout().trim());
1740             }
1741             String system_img_info = device.getProperty("ro.system.build.fingerprint");
1742             if (system_img_info != null) {
1743                 CLog.i(
1744                         "Device %s system image build information: '%s'",
1745                         device.getSerialNumber(), system_img_info);
1746                 info.getBuildInfo().addBuildAttribute("system_img_info", system_img_info);
1747             }
1748             String vendor_img_info = device.getProperty("ro.vendor.build.fingerprint");
1749             if (vendor_img_info != null) {
1750                 CLog.i(
1751                         "Device %s vendor image build information: '%s'",
1752                         device.getSerialNumber(), vendor_img_info);
1753                 info.getBuildInfo().addBuildAttribute("vendor_img_info", vendor_img_info);
1754             }
1755         }
1756     }
1757 }
1758