• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.tradefed.config;
18 
19 import com.android.tradefed.build.BuildRetrievalError;
20 import com.android.tradefed.build.IBuildProvider;
21 import com.android.tradefed.command.CommandOptions;
22 import com.android.tradefed.command.ICommandOptions;
23 import com.android.tradefed.config.OptionSetter.FieldDef;
24 import com.android.tradefed.config.filter.GlobalTestFilter;
25 import com.android.tradefed.config.proxy.TradefedDelegator;
26 import com.android.tradefed.device.IDeviceRecovery;
27 import com.android.tradefed.device.IDeviceSelection;
28 import com.android.tradefed.device.TestDeviceOptions;
29 import com.android.tradefed.device.metric.IMetricCollector;
30 import com.android.tradefed.invoker.tracing.CloseableTraceScope;
31 import com.android.tradefed.log.ILeveledLogOutput;
32 import com.android.tradefed.log.LogUtil.CLog;
33 import com.android.tradefed.log.StdoutLogger;
34 import com.android.tradefed.postprocessor.BasePostProcessor;
35 import com.android.tradefed.postprocessor.IPostProcessor;
36 import com.android.tradefed.result.FileSystemLogSaver;
37 import com.android.tradefed.result.ILogSaver;
38 import com.android.tradefed.result.ITestInvocationListener;
39 import com.android.tradefed.result.TextResultReporter;
40 import com.android.tradefed.result.skipped.SkipManager;
41 import com.android.tradefed.retry.BaseRetryDecision;
42 import com.android.tradefed.retry.IRetryDecision;
43 import com.android.tradefed.sandbox.SandboxOptions;
44 import com.android.tradefed.sandbox.TradefedSandbox;
45 import com.android.tradefed.suite.checker.ISystemStatusChecker;
46 import com.android.tradefed.targetprep.ILabPreparer;
47 import com.android.tradefed.targetprep.ITargetPreparer;
48 import com.android.tradefed.targetprep.multi.IMultiTargetPreparer;
49 import com.android.tradefed.testtype.IRemoteTest;
50 import com.android.tradefed.testtype.StubTest;
51 import com.android.tradefed.testtype.coverage.CoverageOptions;
52 import com.android.tradefed.util.FileUtil;
53 import com.android.tradefed.util.IDisableable;
54 import com.android.tradefed.util.QuotationAwareTokenizer;
55 import com.android.tradefed.util.SystemUtil;
56 import com.android.tradefed.util.keystore.IKeyStoreClient;
57 
58 import com.google.common.annotations.VisibleForTesting;
59 import com.google.common.collect.ImmutableSet;
60 
61 import org.kxml2.io.KXmlSerializer;
62 
63 import java.io.File;
64 import java.io.IOException;
65 import java.io.PrintStream;
66 import java.io.PrintWriter;
67 import java.lang.reflect.InvocationTargetException;
68 import java.util.ArrayList;
69 import java.util.Collection;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.LinkedHashMap;
73 import java.util.List;
74 import java.util.Map;
75 import java.util.Map.Entry;
76 import java.util.Set;
77 import java.util.regex.Matcher;
78 import java.util.regex.Pattern;
79 
80 /**
81  * A concrete {@link IConfiguration} implementation that stores the loaded config objects in a map.
82  */
83 public class Configuration implements IConfiguration {
84 
85     // type names for built in configuration objects
86     public static final String BUILD_PROVIDER_TYPE_NAME = "build_provider";
87     public static final String TARGET_PREPARER_TYPE_NAME = "target_preparer";
88     public static final String LAB_PREPARER_TYPE_NAME = "lab_preparer";
89     // Variation of Multi_target_preparer that runs BEFORE each device target_preparer.
90     public static final String MULTI_PRE_TARGET_PREPARER_TYPE_NAME = "multi_pre_target_preparer";
91     public static final String MULTI_PREPARER_TYPE_NAME = "multi_target_preparer";
92     public static final String TEST_TYPE_NAME = "test";
93     public static final String DEVICE_RECOVERY_TYPE_NAME = "device_recovery";
94     public static final String LOGGER_TYPE_NAME = "logger";
95     public static final String LOG_SAVER_TYPE_NAME = "log_saver";
96     public static final String RESULT_REPORTER_TYPE_NAME = "result_reporter";
97     public static final String CMD_OPTIONS_TYPE_NAME = "cmd_options";
98     public static final String DEVICE_REQUIREMENTS_TYPE_NAME = "device_requirements";
99     public static final String DEVICE_OPTIONS_TYPE_NAME = "device_options";
100     public static final String SYSTEM_STATUS_CHECKER_TYPE_NAME = "system_checker";
101     public static final String CONFIGURATION_DESCRIPTION_TYPE_NAME = "config_desc";
102     public static final String DEVICE_NAME = "device";
103     public static final String DEVICE_METRICS_COLLECTOR_TYPE_NAME = "metrics_collector";
104     public static final String METRIC_POST_PROCESSOR_TYPE_NAME = "metric_post_processor";
105     public static final String SANDBOX_TYPE_NAME = "sandbox";
106     public static final String SANBOX_OPTIONS_TYPE_NAME = "sandbox_options";
107     public static final String RETRY_DECISION_TYPE_NAME = "retry_decision";
108     public static final String COVERAGE_OPTIONS_TYPE_NAME = "coverage";
109     public static final String GLOBAL_FILTERS_TYPE_NAME = "global_filters";
110     public static final String SKIP_MANAGER_TYPE_NAME = "skip_manager";
111 
112     public static final Set<String> NON_MODULE_OBJECTS =
113             ImmutableSet.of(
114                     BUILD_PROVIDER_TYPE_NAME,
115                     CMD_OPTIONS_TYPE_NAME,
116                     DEVICE_RECOVERY_TYPE_NAME,
117                     LOGGER_TYPE_NAME,
118                     LOG_SAVER_TYPE_NAME,
119                     RESULT_REPORTER_TYPE_NAME,
120                     SANDBOX_TYPE_NAME,
121                     SANBOX_OPTIONS_TYPE_NAME,
122                     RETRY_DECISION_TYPE_NAME,
123                     GLOBAL_FILTERS_TYPE_NAME,
124                     SKIP_MANAGER_TYPE_NAME);
125 
126     private static Map<String, ObjTypeInfo> sObjTypeMap = null;
127     private static Set<String> sMultiDeviceSupportedTag =
128             ImmutableSet.of(
129                     BUILD_PROVIDER_TYPE_NAME,
130                     TARGET_PREPARER_TYPE_NAME,
131                     LAB_PREPARER_TYPE_NAME,
132                     DEVICE_RECOVERY_TYPE_NAME,
133                     DEVICE_REQUIREMENTS_TYPE_NAME,
134                     DEVICE_OPTIONS_TYPE_NAME);
135     // regexp pattern used to parse map option values
136     private static final Pattern OPTION_KEY_VALUE_PATTERN = Pattern.compile("(?<!\\\\)=");
137 
138     private static final Pattern CONFIG_EXCEPTION_PATTERN =
139             Pattern.compile("Could not find option with name '(.*)'");
140 
141     /** Mapping of config object type name to config objects. */
142     private Map<String, List<Object>> mConfigMap;
143     private final String mName;
144     private final String mDescription;
145     // original command line used to create this given configuration.
146     private String[] mCommandLine;
147 
148     // Track options that had no effect
149     private Set<String> mInopOptions = new HashSet<>();
150 
151     // used to track the files that where dynamically downloaded
152     private Set<File> mRemoteFiles = new HashSet<>();
153 
154     /**
155      * Container struct for built-in config object type
156      */
157     private static class ObjTypeInfo {
158         final Class<?> mExpectedType;
159         /**
160          * true if a list (ie many objects in a single config) are supported for this type
161          */
162         final boolean mIsListSupported;
163 
ObjTypeInfo(Class<?> expectedType, boolean isList)164         ObjTypeInfo(Class<?> expectedType, boolean isList) {
165             mExpectedType = expectedType;
166             mIsListSupported = isList;
167         }
168     }
169 
170     /**
171      * Determine if given config object type name is a built in object
172      *
173      * @param typeName the config object type name
174      * @return <code>true</code> if name is a built in object type
175      */
isBuiltInObjType(String typeName)176     static boolean isBuiltInObjType(String typeName) {
177         return getObjTypeMap().containsKey(typeName);
178     }
179 
getObjTypeMap()180     private static synchronized Map<String, ObjTypeInfo> getObjTypeMap() {
181         if (sObjTypeMap == null) {
182             sObjTypeMap = new HashMap<String, ObjTypeInfo>();
183             sObjTypeMap.put(BUILD_PROVIDER_TYPE_NAME, new ObjTypeInfo(IBuildProvider.class, false));
184             sObjTypeMap.put(TARGET_PREPARER_TYPE_NAME,
185                     new ObjTypeInfo(ITargetPreparer.class, true));
186             sObjTypeMap.put(LAB_PREPARER_TYPE_NAME, new ObjTypeInfo(ILabPreparer.class, true));
187             sObjTypeMap.put(
188                     MULTI_PRE_TARGET_PREPARER_TYPE_NAME,
189                     new ObjTypeInfo(IMultiTargetPreparer.class, true));
190             sObjTypeMap.put(MULTI_PREPARER_TYPE_NAME,
191                     new ObjTypeInfo(IMultiTargetPreparer.class, true));
192             sObjTypeMap.put(TEST_TYPE_NAME, new ObjTypeInfo(IRemoteTest.class, true));
193             sObjTypeMap.put(DEVICE_RECOVERY_TYPE_NAME,
194                     new ObjTypeInfo(IDeviceRecovery.class, false));
195             sObjTypeMap.put(LOGGER_TYPE_NAME, new ObjTypeInfo(ILeveledLogOutput.class, false));
196             sObjTypeMap.put(LOG_SAVER_TYPE_NAME, new ObjTypeInfo(ILogSaver.class, false));
197             sObjTypeMap.put(RESULT_REPORTER_TYPE_NAME,
198                     new ObjTypeInfo(ITestInvocationListener.class, true));
199             sObjTypeMap.put(CMD_OPTIONS_TYPE_NAME, new ObjTypeInfo(ICommandOptions.class,
200                     false));
201             sObjTypeMap.put(DEVICE_REQUIREMENTS_TYPE_NAME, new ObjTypeInfo(IDeviceSelection.class,
202                     false));
203             sObjTypeMap.put(DEVICE_OPTIONS_TYPE_NAME, new ObjTypeInfo(TestDeviceOptions.class,
204                     false));
205             sObjTypeMap.put(DEVICE_NAME, new ObjTypeInfo(IDeviceConfiguration.class, true));
206             sObjTypeMap.put(SYSTEM_STATUS_CHECKER_TYPE_NAME,
207                     new ObjTypeInfo(ISystemStatusChecker.class, true));
208             sObjTypeMap.put(
209                     CONFIGURATION_DESCRIPTION_TYPE_NAME,
210                     new ObjTypeInfo(ConfigurationDescriptor.class, false));
211             sObjTypeMap.put(
212                     DEVICE_METRICS_COLLECTOR_TYPE_NAME,
213                     new ObjTypeInfo(IMetricCollector.class, true));
214             sObjTypeMap.put(
215                     METRIC_POST_PROCESSOR_TYPE_NAME,
216                     new ObjTypeInfo(BasePostProcessor.class, true));
217             sObjTypeMap.put(SANBOX_OPTIONS_TYPE_NAME, new ObjTypeInfo(SandboxOptions.class, false));
218             sObjTypeMap.put(RETRY_DECISION_TYPE_NAME, new ObjTypeInfo(IRetryDecision.class, false));
219             sObjTypeMap.put(
220                     COVERAGE_OPTIONS_TYPE_NAME, new ObjTypeInfo(CoverageOptions.class, false));
221             sObjTypeMap.put(
222                     GLOBAL_FILTERS_TYPE_NAME, new ObjTypeInfo(GlobalTestFilter.class, false));
223             sObjTypeMap.put(SKIP_MANAGER_TYPE_NAME, new ObjTypeInfo(SkipManager.class, false));
224         }
225         return sObjTypeMap;
226     }
227 
228     /**
229      * Determine if a given config object type is allowed to exists inside a device tag
230      * configuration.
231      * Authorized type are: build_provider, target_preparer, device_recovery, device_requirements,
232      * device_options
233      *
234      * @param typeName the config object type name
235      * @return True if name is allowed to exists inside the device tag
236      */
doesBuiltInObjSupportMultiDevice(String typeName)237     static boolean doesBuiltInObjSupportMultiDevice(String typeName) {
238         return getMultiDeviceSupportedTag().contains(typeName);
239     }
240 
241     /**
242      * Return the {@link Set} of tags that are supported in a device tag for multi device
243      * configuration.
244      */
getMultiDeviceSupportedTag()245     public static synchronized Set<String> getMultiDeviceSupportedTag() {
246         return sMultiDeviceSupportedTag;
247     }
248 
249     /**
250      * Creates an {@link Configuration} with default config objects.
251      */
Configuration(String name, String description)252     public Configuration(String name, String description) {
253         mName = name;
254         mDescription = description;
255         mConfigMap = new LinkedHashMap<String, List<Object>>();
256         setDeviceConfig(new DeviceConfigurationHolder(ConfigurationDef.DEFAULT_DEVICE_NAME));
257         setCommandOptions(new CommandOptions());
258         setTest(new StubTest());
259         setLogOutput(new StdoutLogger());
260         setLogSaver(new FileSystemLogSaver()); // FileSystemLogSaver saves to tmp by default.
261         setTestInvocationListener(new TextResultReporter());
262         // Init an empty list of target_preparers
263         setConfigurationObjectListNoThrow(TARGET_PREPARER_TYPE_NAME, new ArrayList<>());
264         setConfigurationObjectListNoThrow(LAB_PREPARER_TYPE_NAME, new ArrayList<>());
265         setMultiPreTargetPreparers(new ArrayList<>());
266         setMultiTargetPreparers(new ArrayList<>());
267         setSystemStatusCheckers(new ArrayList<ISystemStatusChecker>());
268         setConfigurationDescriptor(new ConfigurationDescriptor());
269         setDeviceMetricCollectors(new ArrayList<>());
270         setPostProcessors(new ArrayList<>());
271         setCoverageOptions(new CoverageOptions());
272         setConfigurationObjectNoThrow(SANBOX_OPTIONS_TYPE_NAME, new SandboxOptions());
273         setConfigurationObjectNoThrow(RETRY_DECISION_TYPE_NAME, new BaseRetryDecision());
274         setConfigurationObjectNoThrow(GLOBAL_FILTERS_TYPE_NAME, new GlobalTestFilter());
275         setConfigurationObjectNoThrow(SKIP_MANAGER_TYPE_NAME, new SkipManager());
276     }
277 
278     /**
279      * If we are in multi device mode, we cannot allow fetching the regular references because
280      * they are most likely wrong.
281      */
notAllowedInMultiMode(String function)282     private void notAllowedInMultiMode(String function) {
283         if (getConfigurationObjectList(DEVICE_NAME).size() > 1) {
284             throw new UnsupportedOperationException(String.format("Calling %s is not allowed "
285                     + "in multi device mode", function));
286         }
287         if (getConfigurationObjectList(DEVICE_NAME).isEmpty()) {
288             throw new UnsupportedOperationException(
289                     "We should always have at least 1 Device config");
290         }
291     }
292 
293     /** {@inheritDoc} */
294     @Override
getName()295     public String getName() {
296         return mName;
297     }
298 
299     /**
300      * @return a short user readable description this {@link Configuration}
301      */
getDescription()302     public String getDescription() {
303         return mDescription;
304     }
305 
306     /**
307      * {@inheritDoc}
308      */
309     @Override
setCommandLine(String[] arrayArgs)310     public void setCommandLine(String[] arrayArgs) {
311         mCommandLine = arrayArgs;
312     }
313 
314     /**
315      * {@inheritDoc}
316      */
317     @Override
getCommandLine()318     public String getCommandLine() {
319         // FIXME: obfuscated passwords from command line.
320         if (mCommandLine != null && mCommandLine.length != 0) {
321             return QuotationAwareTokenizer.combineTokens(mCommandLine);
322         }
323         // If no args were available return null.
324         return null;
325     }
326 
327     /**
328      * {@inheritDoc}
329      */
330     @SuppressWarnings("unchecked")
331     @Override
getBuildProvider()332     public IBuildProvider getBuildProvider() {
333         notAllowedInMultiMode("getBuildProvider");
334         return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME))
335                 .get(0).getBuildProvider();
336     }
337 
338     /**
339      * {@inheritDoc}
340      */
341     @SuppressWarnings("unchecked")
342     @Override
getTargetPreparers()343     public List<ITargetPreparer> getTargetPreparers() {
344         notAllowedInMultiMode("getTargetPreparers");
345         return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME))
346                 .get(0).getTargetPreparers();
347     }
348 
349     /** {@inheritDoc} */
350     @SuppressWarnings("unchecked")
351     @Override
getLabPreparers()352     public List<ITargetPreparer> getLabPreparers() {
353         notAllowedInMultiMode("getLabPreparers");
354         return ((List<IDeviceConfiguration>) getConfigurationObjectList(DEVICE_NAME))
355                 .get(0)
356                 .getLabPreparers();
357     }
358 
359     /**
360      * {@inheritDoc}
361      */
362     @SuppressWarnings("unchecked")
363     @Override
getTests()364     public List<IRemoteTest> getTests() {
365         return (List<IRemoteTest>) getConfigurationObjectList(TEST_TYPE_NAME);
366     }
367 
368     /**
369      * {@inheritDoc}
370      */
371     @SuppressWarnings("unchecked")
372     @Override
getDeviceRecovery()373     public IDeviceRecovery getDeviceRecovery() {
374         notAllowedInMultiMode("getDeviceRecovery");
375         return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME))
376                 .get(0).getDeviceRecovery();
377     }
378 
379     /**
380      * {@inheritDoc}
381      */
382     @Override
getLogOutput()383     public ILeveledLogOutput getLogOutput() {
384         return (ILeveledLogOutput) getConfigurationObject(LOGGER_TYPE_NAME);
385     }
386 
387     /**
388      * {@inheritDoc}
389      */
390     @Override
getLogSaver()391     public ILogSaver getLogSaver() {
392         return (ILogSaver) getConfigurationObject(LOG_SAVER_TYPE_NAME);
393     }
394 
395     /** {@inheritDoc} */
396     @Override
getRetryDecision()397     public IRetryDecision getRetryDecision() {
398         return (IRetryDecision) getConfigurationObject(RETRY_DECISION_TYPE_NAME);
399     }
400 
401     /**
402      * {@inheritDoc}
403      */
404     @SuppressWarnings("unchecked")
405     @Override
getMultiTargetPreparers()406     public List<IMultiTargetPreparer> getMultiTargetPreparers() {
407         return (List<IMultiTargetPreparer>) getConfigurationObjectList(MULTI_PREPARER_TYPE_NAME);
408     }
409 
410     /** {@inheritDoc} */
411     @SuppressWarnings("unchecked")
412     @Override
getMultiPreTargetPreparers()413     public List<IMultiTargetPreparer> getMultiPreTargetPreparers() {
414         return (List<IMultiTargetPreparer>)
415                 getConfigurationObjectList(MULTI_PRE_TARGET_PREPARER_TYPE_NAME);
416     }
417 
418     /**
419      * {@inheritDoc}
420      */
421     @SuppressWarnings("unchecked")
422     @Override
getSystemStatusCheckers()423     public List<ISystemStatusChecker> getSystemStatusCheckers() {
424         return (List<ISystemStatusChecker>)
425                 getConfigurationObjectList(SYSTEM_STATUS_CHECKER_TYPE_NAME);
426     }
427 
428     /**
429      * {@inheritDoc}
430      */
431     @SuppressWarnings("unchecked")
432     @Override
getTestInvocationListeners()433     public List<ITestInvocationListener> getTestInvocationListeners() {
434         return (List<ITestInvocationListener>) getConfigurationObjectList(
435                 RESULT_REPORTER_TYPE_NAME);
436     }
437 
438     /** {@inheritDoc} */
439     @SuppressWarnings("unchecked")
440     @Override
getMetricCollectors()441     public List<IMetricCollector> getMetricCollectors() {
442         return (List<IMetricCollector>)
443                 getConfigurationObjectList(DEVICE_METRICS_COLLECTOR_TYPE_NAME);
444     }
445 
446     @SuppressWarnings("unchecked")
447     @Override
getPostProcessors()448     public List<IPostProcessor> getPostProcessors() {
449         return (List<IPostProcessor>) getConfigurationObjectList(METRIC_POST_PROCESSOR_TYPE_NAME);
450     }
451 
452     /** {@inheritDoc} */
453     @Override
getCommandOptions()454     public ICommandOptions getCommandOptions() {
455         return (ICommandOptions) getConfigurationObject(CMD_OPTIONS_TYPE_NAME);
456     }
457 
458     /** {@inheritDoc} */
459     @Override
getConfigurationDescription()460     public ConfigurationDescriptor getConfigurationDescription() {
461         return (ConfigurationDescriptor)
462                 getConfigurationObject(CONFIGURATION_DESCRIPTION_TYPE_NAME);
463     }
464 
465     /** {@inheritDoc} */
466     @SuppressWarnings("unchecked")
467     @Override
getDeviceRequirements()468     public IDeviceSelection getDeviceRequirements() {
469         notAllowedInMultiMode("getDeviceRequirements");
470         return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME))
471                 .get(0).getDeviceRequirements();
472     }
473 
474     /**
475      * {@inheritDoc}
476      */
477     @SuppressWarnings("unchecked")
478     @Override
getDeviceOptions()479     public TestDeviceOptions getDeviceOptions() {
480         notAllowedInMultiMode("getDeviceOptions");
481         return ((List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME))
482                 .get(0).getDeviceOptions();
483     }
484 
485     /**
486      * {@inheritDoc}
487      */
488     @Override
getConfigurationObjectList(String typeName)489     public List<?> getConfigurationObjectList(String typeName) {
490         return mConfigMap.get(typeName);
491     }
492 
493     /**
494      * {@inheritDoc}
495      */
496     @SuppressWarnings("unchecked")
497     @Override
getDeviceConfigByName(String nameDevice)498     public IDeviceConfiguration getDeviceConfigByName(String nameDevice) {
499         for (IDeviceConfiguration deviceHolder :
500                 (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME)) {
501             if (deviceHolder.getDeviceName().equals(nameDevice)) {
502                 return deviceHolder;
503             }
504         }
505         return null;
506     }
507 
508     /**
509      * {@inheritDoc}
510      */
511     @SuppressWarnings("unchecked")
512     @Override
getDeviceConfig()513     public List<IDeviceConfiguration> getDeviceConfig() {
514         return (List<IDeviceConfiguration>)getConfigurationObjectList(DEVICE_NAME);
515     }
516 
517     /** {@inheritDoc} */
518     @SuppressWarnings("unchecked")
519     @Override
getCoverageOptions()520     public CoverageOptions getCoverageOptions() {
521         return (CoverageOptions) getConfigurationObject(COVERAGE_OPTIONS_TYPE_NAME);
522     }
523 
524     /** {@inheritDoc} */
525     @SuppressWarnings("unchecked")
526     @Override
getGlobalFilters()527     public GlobalTestFilter getGlobalFilters() {
528         return (GlobalTestFilter) getConfigurationObject(GLOBAL_FILTERS_TYPE_NAME);
529     }
530 
531     /** {@inheritDoc} */
532     @SuppressWarnings("unchecked")
533     @Override
getSkipManager()534     public SkipManager getSkipManager() {
535         return (SkipManager) getConfigurationObject(SKIP_MANAGER_TYPE_NAME);
536     }
537 
538     /**
539      * {@inheritDoc}
540      */
541     @Override
getConfigurationObject(String typeName)542     public Object getConfigurationObject(String typeName) {
543         List<?> configObjects = getConfigurationObjectList(typeName);
544         if (configObjects == null) {
545             return null;
546         }
547         ObjTypeInfo typeInfo = getObjTypeMap().get(typeName);
548         if (typeInfo != null && typeInfo.mIsListSupported) {
549             throw new IllegalStateException(
550                     String.format(
551                             "Wrong method call for type %s. Used getConfigurationObject() for a "
552                                     + "config object that is stored as a list",
553                             typeName));
554         }
555         if (configObjects.size() != 1) {
556             throw new IllegalStateException(String.format(
557                     "Attempted to retrieve single object for %s, but %d are present",
558                     typeName, configObjects.size()));
559         }
560         return configObjects.get(0);
561     }
562 
563     /** {@inheritDoc} */
564     @Override
getAllConfigurationObjectsOfType(String configType)565     public Collection<Object> getAllConfigurationObjectsOfType(String configType) {
566         Collection<Object> objectsCopy = new ArrayList<Object>();
567         if (doesBuiltInObjSupportMultiDevice(configType)) {
568             for (IDeviceConfiguration deviceConfig : getDeviceConfig()) {
569                 objectsCopy.addAll(deviceConfig.getAllObjectOfType(configType));
570             }
571         } else {
572             List<?> configObjects = getConfigurationObjectList(configType);
573             if (configObjects != null) {
574                 objectsCopy.addAll(configObjects);
575             }
576         }
577         return objectsCopy;
578     }
579 
580     /**
581      * Return a copy of all config objects
582      */
getAllConfigurationObjects()583     private Collection<Object> getAllConfigurationObjects() {
584         return getAllConfigurationObjects(null, true);
585     }
586 
587     /**
588      * Return a copy of all config objects, minus the object configuration of the type specified.
589      * Returns all the config objects if param is null.
590      */
getAllConfigurationObjects( String excludedConfigName, boolean includeDisabled)591     private Collection<Object> getAllConfigurationObjects(
592             String excludedConfigName, boolean includeDisabled) {
593         Collection<Object> objectsCopy = new ArrayList<Object>();
594         for (Entry<String, List<Object>> entryList : mConfigMap.entrySet()) {
595             if (excludedConfigName != null && excludedConfigName.equals(entryList.getKey())) {
596                 continue;
597             }
598             if (includeDisabled) {
599                 objectsCopy.addAll(entryList.getValue());
600             } else {
601                 for (Object o : entryList.getValue()) {
602                     if (o instanceof IDisableable && ((IDisableable) o).isDisabled()) {
603                         continue;
604                     }
605                     objectsCopy.add(o);
606                 }
607             }
608         }
609         return objectsCopy;
610     }
611 
612     /** Return a copy of all config objects that are not disabled via {@link IDisableable}. */
getAllNonDisabledConfigurationObjects()613     private Collection<Object> getAllNonDisabledConfigurationObjects() {
614         String excluded = null;
615         // Inside the sandbox disable lab preparers
616         if (System.getenv(TradefedSandbox.SANDBOX_ENABLED) != null) {
617             excluded = LAB_PREPARER_TYPE_NAME;
618         }
619         return getAllConfigurationObjects(excluded, false);
620     }
621 
622     /**
623      * Creates an OptionSetter which is appropriate for setting options on all objects which
624      * will be returned by {@link #getAllConfigurationObjects}.
625      */
createOptionSetter()626     private OptionSetter createOptionSetter() throws ConfigurationException {
627         return new OptionSetter(getAllConfigurationObjects());
628     }
629 
630     /**
631      * Injects an option value into the set of configuration objects.
632      *
633      * Uses provided arguments as is and fails if arguments have invalid format or
634      * provided ambiguously, e.g. {@code optionKey} argument is provided for non-map option,
635      * or the value for an option of integer type cannot be parsed as an integer number.
636      *
637      * @param optionSetter setter to use for the injection
638      * @param optionName name of the option
639      * @param optionKey map key, if the option is of map type
640      * @param optionValue value of the option or map value, if the option is of map type
641      * @param source source of the option
642      * @throws ConfigurationException if option value cannot be injected
643      */
internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionKey, String optionValue, String source)644     private void internalInjectOptionValue(OptionSetter optionSetter, String optionName,
645             String optionKey, String optionValue, String source) throws ConfigurationException {
646         if (optionSetter == null) {
647             throw new IllegalArgumentException("optionSetter cannot be null");
648         }
649 
650         // Set all fields that match this option name / key
651         List<FieldDef> affectedFields = optionSetter.setOptionValue(
652                 optionName, optionKey, optionValue);
653 
654         boolean requiredForRerun = false;
655         // Update the source for each affected field
656         for (FieldDef field : affectedFields) {
657             requiredForRerun |= field.field.getAnnotation(Option.class).requiredForRerun();
658             if (requiredForRerun) {
659                 // Only need to check if the option is required for rerun once if it's set to true.
660                 break;
661             }
662         }
663 
664         if (requiredForRerun) {
665             OptionDef optionDef = new OptionDef(optionName, optionKey, optionValue, source, null);
666             getConfigurationDescription().addRerunOption(optionDef);
667         }
668     }
669 
670     /**
671      * Injects an option value into the set of configuration objects.
672      *
673      * If the option to be set is of map type, an attempt to parse {@code optionValue} argument
674      * into key-value pair is made. In this case {@code optionValue} must have an equal sign
675      * separating a key and a value (e.g. my_key=my_value).
676      * In case a key or a value themselves contain an equal sign, this equal sign in them
677      * must be escaped using a backslash (e.g. a\=b=y\=z).
678      *
679      * @param optionSetter setter to use for the injection
680      * @param optionName name of the option
681      * @param optionValue value of the option
682      * @throws ConfigurationException if option value cannot be injected
683      */
internalInjectOptionValue(OptionSetter optionSetter, String optionName, String optionValue)684     private void internalInjectOptionValue(OptionSetter optionSetter, String optionName,
685             String optionValue) throws ConfigurationException {
686         // Cannot continue without optionSetter
687         if (optionSetter == null) {
688             throw new IllegalArgumentException("optionSetter cannot be null");
689         }
690 
691         // If the option is not a map, then the key is null...
692         if (!optionSetter.isMapOption(optionName)) {
693             internalInjectOptionValue(optionSetter, optionName, null, optionValue, null);
694             return;
695         }
696 
697         // ..., otherwise try to parse the value to retrieve the key
698         String[] parts = OPTION_KEY_VALUE_PATTERN.split(optionValue);
699         if (parts.length != 2) {
700             throw new ConfigurationException(String.format(
701                     "option '%s' has an invalid format for value %s:w",
702                     optionName, optionValue));
703         }
704         internalInjectOptionValue(optionSetter, optionName,
705                 parts[0].replace("\\\\=", "="), parts[1].replace("\\\\=", "="), null);
706     }
707 
708     /**
709      * {@inheritDoc}
710      */
711     @Override
injectOptionValue(String optionName, String optionValue)712     public void injectOptionValue(String optionName, String optionValue)
713             throws ConfigurationException {
714         internalInjectOptionValue(createOptionSetter(), optionName, optionValue);
715     }
716 
717     /**
718      * {@inheritDoc}
719      */
720     @Override
injectOptionValue(String optionName, String optionKey, String optionValue)721     public void injectOptionValue(String optionName, String optionKey, String optionValue)
722             throws ConfigurationException {
723         internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, null);
724     }
725 
726     /**
727      * {@inheritDoc}
728      */
729     @Override
injectOptionValueWithSource(String optionName, String optionKey, String optionValue, String source)730     public void injectOptionValueWithSource(String optionName, String optionKey, String optionValue,
731             String source) throws ConfigurationException {
732         internalInjectOptionValue(createOptionSetter(), optionName, optionKey, optionValue, source);
733     }
734 
735     /**
736      * {@inheritDoc}
737      */
738     @Override
injectOptionValues(List<OptionDef> optionDefs)739     public void injectOptionValues(List<OptionDef> optionDefs) throws ConfigurationException {
740         if (optionDefs.isEmpty()) {
741             return;
742         }
743         OptionSetter optionSetter = createOptionSetter();
744         for (OptionDef optionDef : optionDefs) {
745             internalInjectOptionValue(optionSetter, optionDef.name, optionDef.key, optionDef.value,
746                     optionDef.source);
747         }
748     }
749 
750     /** {@inheritDoc} */
751     @Override
safeInjectOptionValues(List<OptionDef> optionDefs)752     public void safeInjectOptionValues(List<OptionDef> optionDefs) throws ConfigurationException {
753         if (optionDefs.isEmpty()) {
754             return;
755         }
756         OptionSetter optionSetter = createOptionSetter();
757         for (OptionDef optionDef : optionDefs) {
758             try {
759                 internalInjectOptionValue(
760                         optionSetter,
761                         optionDef.name,
762                         optionDef.key,
763                         optionDef.value,
764                         optionDef.source);
765             } catch (ConfigurationException e) {
766                 // Ignoring
767             }
768         }
769     }
770 
771     /**
772      * Creates a shallow copy of this object.
773      */
774     @Override
clone()775     public Configuration clone() {
776         Configuration clone = new Configuration(getName(), getDescription());
777         for (Map.Entry<String, List<Object>> entry : mConfigMap.entrySet()) {
778             if (DEVICE_NAME.equals(entry.getKey())) {
779                 List<Object> newDeviceConfigList = new ArrayList<Object>();
780                 for (Object deviceConfig : entry.getValue()) {
781                     IDeviceConfiguration config = ((IDeviceConfiguration) deviceConfig);
782                     IDeviceConfiguration newDeviceConfig = config.clone();
783                     newDeviceConfigList.add(newDeviceConfig);
784                 }
785                 clone.setConfigurationObjectListNoThrow(entry.getKey(), newDeviceConfigList);
786             } else {
787                 clone.setConfigurationObjectListNoThrow(entry.getKey(), entry.getValue());
788             }
789         }
790         clone.setCommandLine(this.mCommandLine);
791         return clone;
792     }
793 
794     /** {@inheritDoc} */
795     @Override
partialDeepClone(List<String> objectToDeepClone, IKeyStoreClient client)796     public IConfiguration partialDeepClone(List<String> objectToDeepClone, IKeyStoreClient client)
797             throws ConfigurationException {
798         Configuration clonedConfig = this.clone();
799         List<String> objToDeepClone = new ArrayList<>(objectToDeepClone);
800         if (objectToDeepClone.contains(Configuration.DEVICE_NAME)) {
801             objToDeepClone.remove(Configuration.DEVICE_NAME);
802             objToDeepClone.addAll(getMultiDeviceSupportedTag());
803         }
804         for (String objType : objToDeepClone) {
805             if (doesBuiltInObjSupportMultiDevice(objType)) {
806                 for (int i = 0; i < clonedConfig.getDeviceConfig().size(); i++) {
807                     IDeviceConfiguration deepCopyConfig = clonedConfig.getDeviceConfig().get(i);
808                     List<?> listOfType =
809                             cloneListTFObject(deepCopyConfig.getAllObjectOfType(objType));
810                     clonedConfig.getDeviceConfig().get(i).removeObjectType(objType);
811                     for (Object o : listOfType) {
812                         clonedConfig.getDeviceConfig().get(i).addSpecificConfig(o, objType);
813                         if (o instanceof IConfigurationReceiver) {
814                             ((IConfigurationReceiver) o).setConfiguration(clonedConfig);
815                         }
816                     }
817                 }
818             } else {
819                 clonedConfig.setConfigurationObjectList(
820                         objType,
821                         cloneListTFObject(clonedConfig.getConfigurationObjectList(objType)));
822             }
823         }
824         return clonedConfig;
825     }
826 
cloneListTFObject(List<?> objects)827     private List<?> cloneListTFObject(List<?> objects) throws ConfigurationException {
828         List<Object> copiedList = new ArrayList<>();
829         for (Object o : objects) {
830             copiedList.add(cloneTFobject(o));
831         }
832         return copiedList;
833     }
834 
cloneTFobject(Object o)835     private Object cloneTFobject(Object o) throws ConfigurationException {
836         try {
837             Object clone = o.getClass().getConstructor().newInstance();
838             OptionCopier.copyOptions(o, clone);
839             return clone;
840         } catch (InstantiationException
841                 | IllegalAccessException
842                 | IllegalArgumentException
843                 | InvocationTargetException
844                 | NoSuchMethodException
845                 | SecurityException e) {
846             // Shouldn't happen, except in unit tests
847             throw new ConfigurationException(String.format("Failed to copy %s", o), e);
848         }
849     }
850 
addToDefaultDeviceConfig(Object obj, String type)851     private void addToDefaultDeviceConfig(Object obj, String type) {
852         try {
853             getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME)
854                     .addSpecificConfig(obj, type);
855         } catch (ConfigurationException e) {
856             // should never happen
857             throw new IllegalArgumentException(e);
858         }
859     }
860 
861     /**
862      * {@inheritDoc}
863      */
864     @Override
setBuildProvider(IBuildProvider provider)865     public void setBuildProvider(IBuildProvider provider) {
866         notAllowedInMultiMode("setBuildProvider");
867         addToDefaultDeviceConfig(provider, BUILD_PROVIDER_TYPE_NAME);
868     }
869 
870     /**
871      * {@inheritDoc}
872      */
873     @Override
setTestInvocationListeners(List<ITestInvocationListener> listeners)874     public void setTestInvocationListeners(List<ITestInvocationListener> listeners) {
875         setConfigurationObjectListNoThrow(RESULT_REPORTER_TYPE_NAME, listeners);
876     }
877 
878     /** {@inheritDoc} */
879     @Override
setDeviceMetricCollectors(List<IMetricCollector> collectors)880     public void setDeviceMetricCollectors(List<IMetricCollector> collectors) {
881         setConfigurationObjectListNoThrow(DEVICE_METRICS_COLLECTOR_TYPE_NAME, collectors);
882     }
883 
884     /** {@inheritDoc} */
885     @Override
setPostProcessors(List<IPostProcessor> processors)886     public void setPostProcessors(List<IPostProcessor> processors) {
887         setConfigurationObjectListNoThrow(METRIC_POST_PROCESSOR_TYPE_NAME, processors);
888     }
889 
890     /**
891      * {@inheritDoc}
892      */
893     @Override
setTestInvocationListener(ITestInvocationListener listener)894     public void setTestInvocationListener(ITestInvocationListener listener) {
895         setConfigurationObjectNoThrow(RESULT_REPORTER_TYPE_NAME, listener);
896     }
897 
898     /**
899      * {@inheritDoc}
900      */
901     @Override
setDeviceConfig(IDeviceConfiguration deviceConfig)902     public void setDeviceConfig(IDeviceConfiguration deviceConfig) {
903         setConfigurationObjectNoThrow(DEVICE_NAME, deviceConfig);
904     }
905 
906     /**
907      * {@inheritDoc}
908      */
909     @Override
setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs)910     public void setDeviceConfigList(List<IDeviceConfiguration> deviceConfigs) {
911         setConfigurationObjectListNoThrow(DEVICE_NAME, deviceConfigs);
912     }
913 
914     /** {@inheritDoc} */
915     @Override
setCoverageOptions(CoverageOptions coverageOptions)916     public void setCoverageOptions(CoverageOptions coverageOptions) {
917         setConfigurationObjectNoThrow(COVERAGE_OPTIONS_TYPE_NAME, coverageOptions);
918     }
919 
920     /**
921      * {@inheritDoc}
922      */
923     @Override
setTest(IRemoteTest test)924     public void setTest(IRemoteTest test) {
925         setConfigurationObjectNoThrow(TEST_TYPE_NAME, test);
926     }
927 
928     /**
929      * {@inheritDoc}
930      */
931     @Override
setTests(List<IRemoteTest> tests)932     public void setTests(List<IRemoteTest> tests) {
933         setConfigurationObjectListNoThrow(TEST_TYPE_NAME, tests);
934     }
935 
936     /**
937      * {@inheritDoc}
938      */
939     @Override
setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps)940     public void setMultiTargetPreparers(List<IMultiTargetPreparer> multiTargPreps) {
941         setConfigurationObjectListNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPreps);
942     }
943 
944     /**
945      * {@inheritDoc}
946      */
947     @Override
setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep)948     public void setMultiTargetPreparer(IMultiTargetPreparer multiTargPrep) {
949         setConfigurationObjectNoThrow(MULTI_PREPARER_TYPE_NAME, multiTargPrep);
950     }
951 
952     /** {@inheritDoc} */
953     @Override
setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps)954     public void setMultiPreTargetPreparers(List<IMultiTargetPreparer> multiPreTargPreps) {
955         setConfigurationObjectListNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPreps);
956     }
957 
958     /** {@inheritDoc} */
959     @Override
setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep)960     public void setMultiPreTargetPreparer(IMultiTargetPreparer multiPreTargPrep) {
961         setConfigurationObjectNoThrow(MULTI_PRE_TARGET_PREPARER_TYPE_NAME, multiPreTargPrep);
962     }
963 
964     /**
965      * {@inheritDoc}
966      */
967     @Override
setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers)968     public void setSystemStatusCheckers(List<ISystemStatusChecker> systemCheckers) {
969         setConfigurationObjectListNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemCheckers);
970     }
971 
972     /**
973      * {@inheritDoc}
974      */
975     @Override
setSystemStatusChecker(ISystemStatusChecker systemChecker)976     public void setSystemStatusChecker(ISystemStatusChecker systemChecker) {
977         setConfigurationObjectNoThrow(SYSTEM_STATUS_CHECKER_TYPE_NAME, systemChecker);
978     }
979 
980     /** {@inheritDoc} */
981     @Override
setLogOutput(ILeveledLogOutput logger)982     public void setLogOutput(ILeveledLogOutput logger) {
983         setConfigurationObjectNoThrow(LOGGER_TYPE_NAME, logger);
984     }
985 
986     /** {@inheritDoc} */
987     @Override
setLogSaver(ILogSaver logSaver)988     public void setLogSaver(ILogSaver logSaver) {
989         setConfigurationObjectNoThrow(LOG_SAVER_TYPE_NAME, logSaver);
990     }
991 
992     /** {@inheritDoc} */
993     @Override
setRetryDecision(IRetryDecision decisionRetry)994     public void setRetryDecision(IRetryDecision decisionRetry) {
995         setConfigurationObjectNoThrow(RETRY_DECISION_TYPE_NAME, decisionRetry);
996     }
997 
998     /** Sets the {@link ConfigurationDescriptor} to be used in the configuration. */
setConfigurationDescriptor(ConfigurationDescriptor configDescriptor)999     private void setConfigurationDescriptor(ConfigurationDescriptor configDescriptor) {
1000         setConfigurationObjectNoThrow(CONFIGURATION_DESCRIPTION_TYPE_NAME, configDescriptor);
1001     }
1002 
1003     /** {@inheritDoc} */
1004     @Override
setDeviceRecovery(IDeviceRecovery recovery)1005     public void setDeviceRecovery(IDeviceRecovery recovery) {
1006         notAllowedInMultiMode("setDeviceRecovery");
1007         addToDefaultDeviceConfig(recovery, DEVICE_RECOVERY_TYPE_NAME);
1008     }
1009 
1010     /**
1011      * {@inheritDoc}
1012      */
1013     @Override
setTargetPreparer(ITargetPreparer preparer)1014     public void setTargetPreparer(ITargetPreparer preparer) {
1015         notAllowedInMultiMode("setTargetPreparer");
1016         addToDefaultDeviceConfig(preparer, TARGET_PREPARER_TYPE_NAME);
1017     }
1018 
1019     /** {@inheritDoc} */
1020     @Override
setTargetPreparers(List<ITargetPreparer> preparers)1021     public void setTargetPreparers(List<ITargetPreparer> preparers) {
1022         notAllowedInMultiMode("setTargetPreparers");
1023         getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).getTargetPreparers().clear();
1024         for (ITargetPreparer prep : preparers) {
1025             addToDefaultDeviceConfig(prep, TARGET_PREPARER_TYPE_NAME);
1026         }
1027     }
1028 
1029     /** {@inheritDoc} */
1030     @Override
setLabPreparer(ITargetPreparer preparer)1031     public void setLabPreparer(ITargetPreparer preparer) {
1032         notAllowedInMultiMode("setLabPreparer");
1033         addToDefaultDeviceConfig(preparer, LAB_PREPARER_TYPE_NAME);
1034     }
1035 
1036     /** {@inheritDoc} */
1037     @Override
setLabPreparers(List<ITargetPreparer> preparers)1038     public void setLabPreparers(List<ITargetPreparer> preparers) {
1039         notAllowedInMultiMode("setLabPreparers");
1040         getDeviceConfigByName(ConfigurationDef.DEFAULT_DEVICE_NAME).getLabPreparers().clear();
1041         for (ITargetPreparer prep : preparers) {
1042             addToDefaultDeviceConfig(prep, LAB_PREPARER_TYPE_NAME);
1043         }
1044     }
1045 
1046     /**
1047      * {@inheritDoc}
1048      */
1049     @Override
setCommandOptions(ICommandOptions cmdOptions)1050     public void setCommandOptions(ICommandOptions cmdOptions) {
1051         setConfigurationObjectNoThrow(CMD_OPTIONS_TYPE_NAME, cmdOptions);
1052     }
1053 
1054     /**
1055      * {@inheritDoc}
1056      */
1057     @Override
setDeviceRequirements(IDeviceSelection devRequirements)1058     public void setDeviceRequirements(IDeviceSelection devRequirements) {
1059         notAllowedInMultiMode("setDeviceRequirements");
1060         addToDefaultDeviceConfig(devRequirements, DEVICE_REQUIREMENTS_TYPE_NAME);
1061     }
1062 
1063     /**
1064      * {@inheritDoc}
1065      */
1066     @Override
setDeviceOptions(TestDeviceOptions devOptions)1067     public void setDeviceOptions(TestDeviceOptions devOptions) {
1068         notAllowedInMultiMode("setDeviceOptions");
1069         addToDefaultDeviceConfig(devOptions, DEVICE_OPTIONS_TYPE_NAME);
1070     }
1071 
1072     /**
1073      * {@inheritDoc}
1074      */
1075     @Override
setConfigurationObject(String typeName, Object configObject)1076     public synchronized void setConfigurationObject(String typeName, Object configObject)
1077             throws ConfigurationException {
1078         if (configObject == null) {
1079             throw new IllegalArgumentException("configObject cannot be null");
1080         }
1081         mConfigMap.remove(typeName);
1082         addObject(typeName, configObject);
1083     }
1084 
1085     /**
1086      * {@inheritDoc}
1087      */
1088     @Override
setConfigurationObjectList(String typeName, List<?> configList)1089     public synchronized void setConfigurationObjectList(String typeName, List<?> configList)
1090             throws ConfigurationException {
1091         if (configList == null) {
1092             throw new IllegalArgumentException("configList cannot be null");
1093         }
1094         mConfigMap.remove(typeName);
1095         mConfigMap.put(typeName, new ArrayList<Object>(1));
1096         for (Object configObject : configList) {
1097             addObject(typeName, configObject);
1098         }
1099     }
1100 
1101     /** {@inheritDoc} */
1102     @Override
isDeviceConfiguredFake(String deviceName)1103     public boolean isDeviceConfiguredFake(String deviceName) {
1104         IDeviceConfiguration deviceConfig = getDeviceConfigByName(deviceName);
1105         if (deviceConfig == null) {
1106             return false;
1107         }
1108         return deviceConfig.isFake();
1109     }
1110 
1111     /**
1112      * Adds a loaded object to this configuration.
1113      *
1114      * @param typeName the unique object type name of the configuration object
1115      * @param configObject the configuration object
1116      * @throws ConfigurationException if object was not the correct type
1117      */
addObject(String typeName, Object configObject)1118     private synchronized void addObject(String typeName, Object configObject)
1119             throws ConfigurationException {
1120         List<Object> objList = mConfigMap.get(typeName);
1121         if (objList == null) {
1122             objList = new ArrayList<Object>(1);
1123             mConfigMap.put(typeName, objList);
1124         }
1125         ObjTypeInfo typeInfo = getObjTypeMap().get(typeName);
1126         if (typeInfo != null && !typeInfo.mExpectedType.isInstance(configObject)) {
1127             throw new ConfigurationException(String.format(
1128                     "The config object %s is not the correct type. Expected %s, received %s",
1129                     typeName, typeInfo.mExpectedType.getCanonicalName(),
1130                     configObject.getClass().getCanonicalName()));
1131         }
1132         if (typeInfo != null && !typeInfo.mIsListSupported && objList.size() > 0) {
1133             throw new ConfigurationException(String.format(
1134                     "Only one config object allowed for %s, but multiple were specified.",
1135                     typeName));
1136         }
1137         objList.add(configObject);
1138         if (configObject instanceof IConfigurationReceiver) {
1139             ((IConfigurationReceiver) configObject).setConfiguration(this);
1140         }
1141         // Inject to object inside device holder too.
1142         if (configObject instanceof IDeviceConfiguration) {
1143             for (Object obj : ((IDeviceConfiguration) configObject).getAllObjects()) {
1144                 if (obj instanceof IConfigurationReceiver) {
1145                     ((IConfigurationReceiver) obj).setConfiguration(this);
1146                 }
1147             }
1148         }
1149     }
1150 
1151     /**
1152      * A wrapper around {@link #setConfigurationObject(String, Object)} that
1153      * will not throw {@link ConfigurationException}.
1154      * <p/>
1155      * Intended to be used in cases where its guaranteed that
1156      * <var>configObject</var> is the correct type.
1157      *
1158      * @param typeName
1159      * @param configObject
1160      */
setConfigurationObjectNoThrow(String typeName, Object configObject)1161     private void setConfigurationObjectNoThrow(String typeName, Object configObject) {
1162         try {
1163             setConfigurationObject(typeName, configObject);
1164         } catch (ConfigurationException e) {
1165             // should never happen
1166             throw new IllegalArgumentException(e);
1167         }
1168     }
1169 
1170     /**
1171      * A wrapper around {@link #setConfigurationObjectList(String, List)} that
1172      * will not throw {@link ConfigurationException}.
1173      * <p/>
1174      * Intended to be used in cases where its guaranteed that
1175      * <var>configObject</var> is the correct type
1176      *
1177      * @param typeName
1178      * @param configList
1179      */
setConfigurationObjectListNoThrow(String typeName, List<?> configList)1180     private void setConfigurationObjectListNoThrow(String typeName, List<?> configList) {
1181         try {
1182             setConfigurationObjectList(typeName, configList);
1183         } catch (ConfigurationException e) {
1184             // should never happen
1185             throw new IllegalArgumentException(e);
1186         }
1187     }
1188 
1189     /**
1190      * {@inheritDoc}
1191      */
1192     @Override
setOptionsFromCommandLineArgs(List<String> listArgs)1193     public List<String> setOptionsFromCommandLineArgs(List<String> listArgs)
1194             throws ConfigurationException {
1195         return setOptionsFromCommandLineArgs(listArgs, null);
1196     }
1197 
1198     /**
1199      * {@inheritDoc}
1200      */
1201     @Override
setOptionsFromCommandLineArgs(List<String> listArgs, IKeyStoreClient keyStoreClient)1202     public List<String> setOptionsFromCommandLineArgs(List<String> listArgs,
1203             IKeyStoreClient keyStoreClient)
1204             throws ConfigurationException {
1205         try (CloseableTraceScope ignored =
1206                 new CloseableTraceScope("setOptionsFromCommandLineArgs")) {
1207             // We get all the objects except the one describing the Configuration itself which does
1208             // not
1209             // allow passing its option via command line.
1210             ArgsOptionParser parser =
1211                     new ArgsOptionParser(
1212                             getAllConfigurationObjects(CONFIGURATION_DESCRIPTION_TYPE_NAME, true));
1213             if (keyStoreClient != null) {
1214                 parser.setKeyStore(keyStoreClient);
1215             }
1216             try {
1217                 List<String> leftOver = parser.parse(listArgs);
1218                 mInopOptions.addAll(parser.getInopOptions());
1219                 return leftOver;
1220             } catch (ConfigurationException e) {
1221                 Matcher m = CONFIG_EXCEPTION_PATTERN.matcher(e.getMessage());
1222                 if (!m.matches()) {
1223                     throw e;
1224                 }
1225                 String optionName = m.group(1);
1226                 try {
1227                     // In case the option exists in the config descriptor, we change the error
1228                     // message
1229                     // to be more specific about why the option is rejected.
1230                     OptionSetter setter = new OptionSetter(getConfigurationDescription());
1231                     setter.getTypeForOption(optionName);
1232                 } catch (ConfigurationException stillThrowing) {
1233                     // Throw the original exception since it cannot be found at all.
1234                     throw e;
1235                 }
1236                 throw new OptionNotAllowedException(
1237                         String.format(
1238                                 "Option '%s' cannot be specified via "
1239                                         + "command line. Only in the configuration xml.",
1240                                 optionName));
1241             }
1242         }
1243     }
1244 
1245     /** {@inheritDoc} */
1246     @Override
setBestEffortOptionsFromCommandLineArgs( List<String> listArgs, IKeyStoreClient keyStoreClient)1247     public List<String> setBestEffortOptionsFromCommandLineArgs(
1248             List<String> listArgs, IKeyStoreClient keyStoreClient) throws ConfigurationException {
1249         // We get all the objects except the one describing the Configuration itself which does not
1250         // allow passing its option via command line.
1251         ArgsOptionParser parser =
1252                 new ArgsOptionParser(
1253                         getAllConfigurationObjects(CONFIGURATION_DESCRIPTION_TYPE_NAME, true));
1254         if (keyStoreClient != null) {
1255             parser.setKeyStore(keyStoreClient);
1256         }
1257         return parser.parseBestEffort(listArgs, /* Force continue */ true);
1258     }
1259 
1260     /**
1261      * Outputs a command line usage help text for this configuration to given
1262      * printStream.
1263      *
1264      * @param out the {@link PrintStream} to use.
1265      * @throws ConfigurationException
1266      */
1267     @Override
printCommandUsage(boolean importantOnly, PrintStream out)1268     public void printCommandUsage(boolean importantOnly, PrintStream out)
1269             throws ConfigurationException {
1270         out.println(String.format("'%s' configuration: %s", getName(), getDescription()));
1271         out.println();
1272         if (importantOnly) {
1273             out.println("Printing help for only the important options. " +
1274                     "To see help for all options, use the --help-all flag");
1275             out.println();
1276         }
1277         for (Map.Entry<String, List<Object>> configObjectsEntry : mConfigMap.entrySet()) {
1278             for (Object configObject : configObjectsEntry.getValue()) {
1279                 if (configObject instanceof IDeviceConfiguration) {
1280                     // We expand the Device Config Object.
1281                     for (Object subconfigObject : ((IDeviceConfiguration)configObject)
1282                             .getAllObjects()) {
1283                         printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(),
1284                                 subconfigObject);
1285                     }
1286                 } else {
1287                     printCommandUsageForObject(importantOnly, out, configObjectsEntry.getKey(),
1288                             configObject);
1289                 }
1290             }
1291         }
1292     }
1293 
printCommandUsageForObject(boolean importantOnly, PrintStream out, String key, Object obj)1294     private void printCommandUsageForObject(boolean importantOnly, PrintStream out, String key,
1295             Object obj) throws ConfigurationException {
1296         String optionHelp = printOptionsForObject(importantOnly, key, obj);
1297         // only print help for object if optionHelp is non zero length
1298         if (optionHelp.length() > 0) {
1299             String classAlias = "";
1300             if (obj.getClass().isAnnotationPresent(OptionClass.class)) {
1301                 final OptionClass classAnnotation = obj.getClass().getAnnotation(
1302                         OptionClass.class);
1303                 classAlias = String.format("'%s' ", classAnnotation.alias());
1304             }
1305             out.printf("  %s%s options:", classAlias, key);
1306             out.println();
1307             out.print(optionHelp);
1308             out.println();
1309         }
1310     }
1311 
1312     /**
1313      * Prints out the available config options for given configuration object.
1314      *
1315      * @param importantOnly print only the important options
1316      * @param objectTypeName the config object type name. Used to generate more
1317      *            descriptive error messages
1318      * @param configObject the config object
1319      * @return a {@link String} of option help text
1320      * @throws ConfigurationException
1321      */
printOptionsForObject(boolean importantOnly, String objectTypeName, Object configObject)1322     private String printOptionsForObject(boolean importantOnly, String objectTypeName,
1323             Object configObject) throws ConfigurationException {
1324         return ArgsOptionParser.getOptionHelp(importantOnly, configObject);
1325     }
1326 
1327     /**
1328      * {@inheritDoc}
1329      */
1330     @Override
validateOptions()1331     public void validateOptions() throws ConfigurationException {
1332         ArgsOptionParser argsParser = new ArgsOptionParser(getAllNonDisabledConfigurationObjects());
1333         argsParser.validateMandatoryOptions();
1334         ICommandOptions options = getCommandOptions();
1335         if (options.getShardCount() != null && options.getShardCount() < 1) {
1336             throw new ConfigurationException("a shard count must be a positive number");
1337         }
1338         if (options.getShardIndex() != null
1339                 && (options.getShardCount() == null || options.getShardIndex() < 0
1340                         || options.getShardIndex() >= options.getShardCount())) {
1341             throw new ConfigurationException("a shard index must be in range [0, shard count)");
1342         }
1343     }
1344 
1345     /** {@inheritDoc} */
1346     @Override
resolveDynamicOptions(DynamicRemoteFileResolver resolver)1347     public void resolveDynamicOptions(DynamicRemoteFileResolver resolver)
1348             throws ConfigurationException, BuildRetrievalError {
1349         List<Object> configObjects = new ArrayList<>(getAllNonDisabledConfigurationObjects());
1350         // Resolve regardless of sharding if we are in remote environment because we know that's
1351         // where the execution will occur.
1352         if (!isRemoteEnvironment()) {
1353             ICommandOptions options = getCommandOptions();
1354             if (getConfigurationObject(TradefedDelegator.DELEGATE_OBJECT) != null) {
1355                 configObjects.clear();
1356                 configObjects.add(getConfigurationObject(TradefedDelegator.DELEGATE_OBJECT));
1357                 CLog.d("Resolving only delegator object dynamic download.");
1358             } else if (options.getShardCount() != null
1359                     && options.getShardCount() > 1
1360                     && options.getShardIndex() == null
1361                     && !getCommandOptions().shouldUseSandboxing()
1362                     && !getCommandOptions().shouldUseRemoteSandboxMode()) {
1363                 CLog.w("Skipping dynamic download due to local sharding detected.");
1364                 return;
1365             }
1366         }
1367 
1368         ArgsOptionParser argsParser = new ArgsOptionParser(configObjects);
1369         CLog.d("Resolve and download remote files from @Option");
1370         // Setup and validate the GCS File paths
1371         mRemoteFiles.addAll(argsParser.validateRemoteFilePath(resolver));
1372     }
1373 
1374     /** Returns whether or not the environment of TF is a remote invocation. */
1375     @VisibleForTesting
isRemoteEnvironment()1376     protected boolean isRemoteEnvironment() {
1377         return SystemUtil.isRemoteEnvironment();
1378     }
1379 
1380     /** {@inheritDoc} */
1381     @Override
cleanConfigurationData()1382     public void cleanConfigurationData() {
1383         for (File file : mRemoteFiles) {
1384             FileUtil.recursiveDelete(file);
1385         }
1386         mRemoteFiles.clear();
1387     }
1388 
1389     /** {@inheritDoc} */
1390     @Override
addFilesToClean(Set<File> toBeCleaned)1391     public void addFilesToClean(Set<File> toBeCleaned) {
1392         mRemoteFiles.addAll(toBeCleaned);
1393     }
1394 
1395     /** {@inheritDoc} */
1396     @Override
getFilesToClean()1397     public Set<File> getFilesToClean() {
1398         return mRemoteFiles;
1399     }
1400 
1401     /** {@inheritDoc} */
1402     @Override
getInopOptions()1403     public Set<String> getInopOptions() {
1404         return mInopOptions;
1405     }
1406 
1407     /**
1408      * {@inheritDoc}
1409      */
1410     @Override
dumpXml(PrintWriter output)1411     public void dumpXml(PrintWriter output) throws IOException {
1412         dumpXml(output, new ArrayList<String>());
1413     }
1414 
1415     /** {@inheritDoc} */
1416     @Override
dumpXml(PrintWriter output, List<String> excludeFilters)1417     public void dumpXml(PrintWriter output, List<String> excludeFilters) throws IOException {
1418         dumpXml(output, excludeFilters, true, true);
1419     }
1420 
1421     /** {@inheritDoc} */
1422     @Override
dumpXml( PrintWriter output, List<String> excludeFilters, boolean printDeprecatedOptions, boolean printUnchangedOptions)1423     public void dumpXml(
1424             PrintWriter output,
1425             List<String> excludeFilters,
1426             boolean printDeprecatedOptions,
1427             boolean printUnchangedOptions)
1428             throws IOException {
1429         KXmlSerializer serializer = new KXmlSerializer();
1430         serializer.setOutput(output);
1431         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
1432         serializer.startDocument("UTF-8", null);
1433         serializer.startTag(null, ConfigurationUtil.CONFIGURATION_NAME);
1434 
1435         for (IMultiTargetPreparer multiPreTargerPrep : getMultiPreTargetPreparers()) {
1436             ConfigurationUtil.dumpClassToXml(
1437                     serializer,
1438                     MULTI_PRE_TARGET_PREPARER_TYPE_NAME,
1439                     multiPreTargerPrep,
1440                     excludeFilters,
1441                     printDeprecatedOptions,
1442                     printUnchangedOptions);
1443             output.flush();
1444         }
1445 
1446         for (IMultiTargetPreparer multipreparer : getMultiTargetPreparers()) {
1447             ConfigurationUtil.dumpClassToXml(
1448                     serializer,
1449                     MULTI_PREPARER_TYPE_NAME,
1450                     multipreparer,
1451                     excludeFilters,
1452                     printDeprecatedOptions,
1453                     printUnchangedOptions);
1454         }
1455 
1456         if (getDeviceConfig().size() > 1) {
1457             // Handle multi device.
1458             for (IDeviceConfiguration deviceConfig : getDeviceConfig()) {
1459                 serializer.startTag(null, Configuration.DEVICE_NAME);
1460                 serializer.attribute(null, "name", deviceConfig.getDeviceName());
1461                 if (deviceConfig.isFake()) {
1462                     serializer.attribute(null, "isFake", "true");
1463                 }
1464                 ConfigurationUtil.dumpClassToXml(
1465                         serializer,
1466                         BUILD_PROVIDER_TYPE_NAME,
1467                         deviceConfig.getBuildProvider(),
1468                         excludeFilters,
1469                         printDeprecatedOptions,
1470                         printUnchangedOptions);
1471                 for (ITargetPreparer preparer : deviceConfig.getTargetPreparers()) {
1472                     ConfigurationUtil.dumpClassToXml(
1473                             serializer,
1474                             TARGET_PREPARER_TYPE_NAME,
1475                             preparer,
1476                             excludeFilters,
1477                             printDeprecatedOptions,
1478                             printUnchangedOptions);
1479                 }
1480                 for (ITargetPreparer preparer : deviceConfig.getLabPreparers()) {
1481                     ConfigurationUtil.dumpClassToXml(
1482                             serializer,
1483                             LAB_PREPARER_TYPE_NAME,
1484                             preparer,
1485                             excludeFilters,
1486                             printDeprecatedOptions,
1487                             printUnchangedOptions);
1488                 }
1489                 ConfigurationUtil.dumpClassToXml(
1490                         serializer,
1491                         DEVICE_RECOVERY_TYPE_NAME,
1492                         deviceConfig.getDeviceRecovery(),
1493                         excludeFilters,
1494                         printDeprecatedOptions,
1495                         printUnchangedOptions);
1496                 ConfigurationUtil.dumpClassToXml(
1497                         serializer,
1498                         DEVICE_REQUIREMENTS_TYPE_NAME,
1499                         deviceConfig.getDeviceRequirements(),
1500                         excludeFilters,
1501                         printDeprecatedOptions,
1502                         printUnchangedOptions);
1503                 ConfigurationUtil.dumpClassToXml(
1504                         serializer,
1505                         DEVICE_OPTIONS_TYPE_NAME,
1506                         deviceConfig.getDeviceOptions(),
1507                         excludeFilters,
1508                         printDeprecatedOptions,
1509                         printUnchangedOptions);
1510                 serializer.endTag(null, Configuration.DEVICE_NAME);
1511             }
1512         } else {
1513             // Put single device tags
1514             ConfigurationUtil.dumpClassToXml(
1515                     serializer,
1516                     BUILD_PROVIDER_TYPE_NAME,
1517                     getBuildProvider(),
1518                     excludeFilters,
1519                     printDeprecatedOptions,
1520                     printUnchangedOptions);
1521             for (ITargetPreparer preparer : getLabPreparers()) {
1522                 ConfigurationUtil.dumpClassToXml(
1523                         serializer,
1524                         LAB_PREPARER_TYPE_NAME,
1525                         preparer,
1526                         excludeFilters,
1527                         printDeprecatedOptions,
1528                         printUnchangedOptions);
1529             }
1530             for (ITargetPreparer preparer : getTargetPreparers()) {
1531                 ConfigurationUtil.dumpClassToXml(
1532                         serializer,
1533                         TARGET_PREPARER_TYPE_NAME,
1534                         preparer,
1535                         excludeFilters,
1536                         printDeprecatedOptions,
1537                         printUnchangedOptions);
1538             }
1539             ConfigurationUtil.dumpClassToXml(
1540                     serializer,
1541                     DEVICE_RECOVERY_TYPE_NAME,
1542                     getDeviceRecovery(),
1543                     excludeFilters,
1544                     printDeprecatedOptions,
1545                     printUnchangedOptions);
1546             ConfigurationUtil.dumpClassToXml(
1547                     serializer,
1548                     DEVICE_REQUIREMENTS_TYPE_NAME,
1549                     getDeviceRequirements(),
1550                     excludeFilters,
1551                     printDeprecatedOptions,
1552                     printUnchangedOptions);
1553             ConfigurationUtil.dumpClassToXml(
1554                     serializer,
1555                     DEVICE_OPTIONS_TYPE_NAME,
1556                     getDeviceOptions(),
1557                     excludeFilters,
1558                     printDeprecatedOptions,
1559                     printUnchangedOptions);
1560         }
1561         for (IRemoteTest test : getTests()) {
1562             ConfigurationUtil.dumpClassToXml(
1563                     serializer,
1564                     TEST_TYPE_NAME,
1565                     test,
1566                     excludeFilters,
1567                     printDeprecatedOptions,
1568                     printUnchangedOptions);
1569         }
1570         ConfigurationUtil.dumpClassToXml(
1571                 serializer,
1572                 CONFIGURATION_DESCRIPTION_TYPE_NAME,
1573                 getConfigurationDescription(),
1574                 excludeFilters,
1575                 printDeprecatedOptions,
1576                 printUnchangedOptions);
1577         ConfigurationUtil.dumpClassToXml(
1578                 serializer,
1579                 LOGGER_TYPE_NAME,
1580                 getLogOutput(),
1581                 excludeFilters,
1582                 printDeprecatedOptions,
1583                 printUnchangedOptions);
1584         ConfigurationUtil.dumpClassToXml(
1585                 serializer,
1586                 LOG_SAVER_TYPE_NAME,
1587                 getLogSaver(),
1588                 excludeFilters,
1589                 printDeprecatedOptions,
1590                 printUnchangedOptions);
1591         for (ITestInvocationListener listener : getTestInvocationListeners()) {
1592             ConfigurationUtil.dumpClassToXml(
1593                     serializer,
1594                     RESULT_REPORTER_TYPE_NAME,
1595                     listener,
1596                     excludeFilters,
1597                     printDeprecatedOptions,
1598                     printUnchangedOptions);
1599         }
1600         ConfigurationUtil.dumpClassToXml(
1601                 serializer,
1602                 CMD_OPTIONS_TYPE_NAME,
1603                 getCommandOptions(),
1604                 excludeFilters,
1605                 printDeprecatedOptions,
1606                 printUnchangedOptions);
1607 
1608         for (IMetricCollector collector : getMetricCollectors()) {
1609             ConfigurationUtil.dumpClassToXml(
1610                     serializer,
1611                     DEVICE_METRICS_COLLECTOR_TYPE_NAME,
1612                     collector,
1613                     excludeFilters,
1614                     printDeprecatedOptions,
1615                     printUnchangedOptions);
1616         }
1617 
1618         for (IPostProcessor processor : getPostProcessors()) {
1619             ConfigurationUtil.dumpClassToXml(
1620                     serializer,
1621                     METRIC_POST_PROCESSOR_TYPE_NAME,
1622                     processor,
1623                     excludeFilters,
1624                     printDeprecatedOptions,
1625                     printUnchangedOptions);
1626         }
1627 
1628         for (ISystemStatusChecker checker : getSystemStatusCheckers()) {
1629             ConfigurationUtil.dumpClassToXml(
1630                     serializer,
1631                     SYSTEM_STATUS_CHECKER_TYPE_NAME,
1632                     checker,
1633                     excludeFilters,
1634                     printDeprecatedOptions,
1635                     printUnchangedOptions);
1636         }
1637 
1638         ConfigurationUtil.dumpClassToXml(
1639                 serializer,
1640                 SANBOX_OPTIONS_TYPE_NAME,
1641                 getConfigurationObject(SANBOX_OPTIONS_TYPE_NAME),
1642                 excludeFilters,
1643                 printDeprecatedOptions,
1644                 printUnchangedOptions);
1645         ConfigurationUtil.dumpClassToXml(
1646                 serializer,
1647                 RETRY_DECISION_TYPE_NAME,
1648                 getRetryDecision(),
1649                 excludeFilters,
1650                 printDeprecatedOptions,
1651                 printUnchangedOptions);
1652         ConfigurationUtil.dumpClassToXml(
1653                 serializer,
1654                 COVERAGE_OPTIONS_TYPE_NAME,
1655                 getCoverageOptions(),
1656                 excludeFilters,
1657                 printDeprecatedOptions,
1658                 printUnchangedOptions);
1659         ConfigurationUtil.dumpClassToXml(
1660                 serializer,
1661                 GLOBAL_FILTERS_TYPE_NAME,
1662                 getGlobalFilters(),
1663                 excludeFilters,
1664                 printDeprecatedOptions,
1665                 printUnchangedOptions);
1666         ConfigurationUtil.dumpClassToXml(
1667                 serializer,
1668                 SKIP_MANAGER_TYPE_NAME,
1669                 getSkipManager(),
1670                 excludeFilters,
1671                 printDeprecatedOptions,
1672                 printUnchangedOptions);
1673 
1674         serializer.endTag(null, ConfigurationUtil.CONFIGURATION_NAME);
1675         serializer.endDocument();
1676     }
1677 }
1678