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