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