1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.tradefed.config.filter;
17 
18 import com.android.tradefed.command.ICommandOptions;
19 import com.android.tradefed.config.Configuration;
20 import com.android.tradefed.config.IConfiguration;
21 import com.android.tradefed.config.IConfigurationReceiver;
22 import com.android.tradefed.config.Option;
23 import com.android.tradefed.config.OptionSetter;
24 import com.android.tradefed.retry.IRetryDecision;
25 import com.android.tradefed.sandbox.SandboxOptions;
26 import com.android.tradefed.service.IRemoteFeature;
27 import com.proto.tradefed.feature.ErrorInfo;
28 import com.proto.tradefed.feature.FeatureRequest;
29 import com.proto.tradefed.feature.FeatureResponse;
30 import com.proto.tradefed.feature.MultiPartResponse;
31 import com.proto.tradefed.feature.PartResponse;
32 import java.lang.reflect.Field;
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Collection;
36 import java.util.List;
37 import java.util.Set;
38 
39 /**
40  * Service implementation that returns the command options value of a given invocation.
41  * This can be extended in the future to support more options.
42  */
43 public class CommandOptionsGetter implements IRemoteFeature, IConfigurationReceiver {
44 
45     public static final String COMMAND_OPTIONS_GETTER = "getCommandOptions";
46     public static final String OPTION_NAME = "option_name";
47 
48     private IConfiguration mConfig;
49 
50     @Override
getName()51     public String getName() {
52         return COMMAND_OPTIONS_GETTER;
53     }
54 
55     @Override
setConfiguration(IConfiguration configuration)56     public void setConfiguration(IConfiguration configuration) {
57         mConfig = configuration;
58     }
59 
60     @Override
execute(FeatureRequest request)61     public FeatureResponse execute(FeatureRequest request) {
62         FeatureResponse.Builder responseBuilder = FeatureResponse.newBuilder();
63         if (mConfig == null) {
64             responseBuilder.setErrorInfo(
65                     ErrorInfo.newBuilder().setErrorTrace("Internal error, configuration not set."));
66             return responseBuilder.build();
67         }
68         if (request.getArgsMap().isEmpty() || !request.getArgsMap().containsKey(OPTION_NAME)) {
69             responseBuilder.setErrorInfo(
70                     ErrorInfo.newBuilder().setErrorTrace("No option_name specified in the args."));
71             return responseBuilder.build();
72         }
73         List<String> optionsToFill = new ArrayList<>(Arrays.asList(request.getArgsMap()
74                 .get(OPTION_NAME).split(",")));
75         // Capture options of CommandOptions & RetryDecision
76         ICommandOptions commandOptions = mConfig.getCommandOptions();
77         IRetryDecision retryOptions = mConfig.getRetryDecision();
78         SandboxOptions sandboxOptions =
79                 (SandboxOptions) mConfig
80                     .getConfigurationObject(Configuration.SANBOX_OPTIONS_TYPE_NAME);
81         List<Object> optionObjects = Arrays.asList(commandOptions, retryOptions, sandboxOptions);
82 
83         List<PartResponse> partResponses = new ArrayList<>();
84         for (Object o : optionObjects) {
85             partResponses.addAll(findOptionsForObject(o, optionsToFill));
86         }
87         if (partResponses.isEmpty()) {
88             responseBuilder.setErrorInfo(
89                     ErrorInfo.newBuilder().setErrorTrace(
90                             String.format("No option or not value set for '%s'",
91                                     request.getArgsMap().get(OPTION_NAME))));
92         } else if (partResponses.size() == 1) {
93             responseBuilder.setResponse(partResponses.get(0).getValue());
94         } else {
95             responseBuilder.setMultiPartResponse(MultiPartResponse.newBuilder()
96                     .addAllResponsePart(partResponses));
97         }
98         return responseBuilder.build();
99     }
100 
findOptionsForObject(Object objectForFields, List<String> optionsToResolve)101     private List<PartResponse> findOptionsForObject(Object objectForFields,
102                 List<String> optionsToResolve) {
103         List<PartResponse> responses = new ArrayList<>();
104         Collection<Field> allFields = OptionSetter.getOptionFieldsForClass(
105                 objectForFields.getClass());
106         for (String toResolve : optionsToResolve) {
107             for (Field field : allFields) {
108                 final Option option = field.getAnnotation(Option.class);
109                 if (option.name().equals(toResolve)) {
110                     Object fieldValue = OptionSetter.getFieldValue(field, objectForFields);
111                     if (fieldValue != null) {
112                         if (fieldValue instanceof Set) {
113                             for (Object o : (Set) fieldValue) {
114                                 responses.add(
115                                         PartResponse.newBuilder()
116                                                 .setKey(toResolve)
117                                                 .setValue(o.toString())
118                                                 .build());
119                             }
120                         } else if (fieldValue instanceof List) {
121                             for (Object o : (List) fieldValue) {
122                                 responses.add(
123                                         PartResponse.newBuilder()
124                                                 .setKey(toResolve)
125                                                 .setValue(o.toString())
126                                                 .build());
127                             }
128                         } else {
129                             responses.add(
130                                     PartResponse.newBuilder()
131                                             .setKey(toResolve)
132                                             .setValue(fieldValue.toString())
133                                             .build());
134                         }
135                         continue;
136                     }
137                 }
138             }
139 
140         }
141         return responses;
142     }
143 }
144