1 /*
2 * Copyright (C) 2019 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 #include <android-base/logging.h>
18 #include <android-base/parseint.h>
19 #include <android-base/strings.h>
20 #include <hidl-util/FQName.h>
21 #include <hidl-util/Formatter.h>
22 #include <hidl-util/StringHelper.h>
23 #include <cstddef>
24 #include <vector>
25
26 #include "AidlHelper.h"
27 #include "Coordinator.h"
28 #include "DocComment.h"
29 #include "FormattingConstants.h"
30 #include "Interface.h"
31 #include "Location.h"
32 #include "Method.h"
33 #include "NamedType.h"
34 #include "Reference.h"
35 #include "Type.h"
36
37 namespace android {
38
emitAidlMethodParams(WrappedOutput * wrappedOutput,const std::vector<NamedReference<Type> * > args,const std::string & prefix,const std::string & attachToLast,const Interface & iface)39 static void emitAidlMethodParams(WrappedOutput* wrappedOutput,
40 const std::vector<NamedReference<Type>*> args,
41 const std::string& prefix, const std::string& attachToLast,
42 const Interface& iface) {
43 if (args.size() == 0) {
44 *wrappedOutput << attachToLast;
45 return;
46 }
47
48 for (size_t i = 0; i < args.size(); i++) {
49 const NamedReference<Type>* arg = args[i];
50 std::string out =
51 prefix + AidlHelper::getAidlType(*arg->get(), iface.fqName()) + " " + arg->name();
52 wrappedOutput->group([&] {
53 if (i != 0) wrappedOutput->printUnlessWrapped(" ");
54 *wrappedOutput << out;
55 if (i == args.size() - 1) {
56 if (!attachToLast.empty()) *wrappedOutput << attachToLast;
57 } else {
58 *wrappedOutput << ",";
59 }
60 });
61 }
62 }
63
getUserDefinedMethods(Formatter & out,const Interface & interface)64 std::vector<const Method*> AidlHelper::getUserDefinedMethods(Formatter& out,
65 const Interface& interface) {
66 std::vector<const Method*> methods;
67 for (const Interface* iface : interface.typeChain()) {
68 if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName()) &&
69 iface->fqName() != gIBaseFqName) {
70 out << "// Types from " << iface->fqName().string()
71 << " are not included because it is in a separate package, and it is expected to "
72 "be a separate AIDL interface. To include these methods, use the '-e' argument. "
73 "\n";
74 break;
75 }
76 const std::vector<Method*> userDefined = iface->userDefinedMethods();
77 methods.insert(methods.end(), userDefined.begin(), userDefined.end());
78 }
79
80 return methods;
81 }
82
83 // Represents a node which is potentially overriding another node.
84 // e.g. if this is 'foo_1_4'
85 template <class NODE>
86 struct NodeWithVersion {
87 size_t major; // 1
88 size_t minor; // 4
89 const NODE* node; // HIDL object representing foo_1_4.
90 std::string baseName; // foo
91 };
92
getBaseName(const std::string & rawName)93 std::string getBaseName(const std::string& rawName) {
94 size_t underscore = rawName.find('_');
95 if (underscore != std::string::npos) {
96 std::string version = rawName.substr(underscore + 1); // don't include _
97 std::string baseName = rawName.substr(0, underscore);
98 underscore = version.find('_');
99
100 size_t major, minor;
101 if (underscore != std::string::npos &&
102 base::ParseUint(version.substr(0, underscore), &major) &&
103 base::ParseUint(version.substr(underscore + 1), &minor)) {
104 // contains major and minor version. consider it's baseName now.
105 return baseName;
106 }
107 }
108 return rawName;
109 }
110
111 template <class NODE>
pushVersionedNodeOntoMap(const NODE & versionedNode,std::map<std::string,NODE> * latestNodeForBaseName,std::vector<const NODE> * supersededNode)112 static void pushVersionedNodeOntoMap(const NODE& versionedNode,
113 std::map<std::string, NODE>* latestNodeForBaseName,
114 std::vector<const NODE>* supersededNode) {
115 // attempt to push name onto latestNodeForBaseName
116 auto [it, inserted] =
117 latestNodeForBaseName->emplace(std::move(versionedNode.baseName), versionedNode);
118 if (!inserted) {
119 auto* current = &it->second;
120
121 // Node in the latestNodeForBaseName is more recent
122 if ((current->major > versionedNode.major) ||
123 (current->major == versionedNode.major && current->minor > versionedNode.minor)) {
124 supersededNode->push_back(versionedNode);
125 return;
126 }
127
128 // Either current.major < versioned.major OR versioned.minor >= current.minor
129 supersededNode->push_back(*current);
130 *current = std::move(versionedNode);
131 }
132 }
133
134 struct ResultTransformation {
135 enum class TransformType {
136 MOVED, // Moved to the front of the method name
137 REMOVED, // Removed the result
138 };
139
140 std::string resultName;
141 TransformType type;
142 };
143
shouldWarnStatusType(const std::string & typeName)144 static bool shouldWarnStatusType(const std::string& typeName) {
145 static const std::vector<std::string> kUppercaseIgnoreStatusTypes = {"ERROR", "STATUS"};
146
147 const std::string uppercase = StringHelper::Uppercase(typeName);
148 for (const std::string& ignore : kUppercaseIgnoreStatusTypes) {
149 if (uppercase.find(ignore) != std::string::npos) return true;
150 }
151 return false;
152 }
153
shouldWarnOutParam(const std::string & typeName)154 static bool shouldWarnOutParam(const std::string& typeName) {
155 static const std::vector<std::string> kNoOutParamTypes = {"ParcelFileDescriptor",
156 "FileDescriptor",
157 "ParcelableHolder",
158 "IBinder",
159 "String",
160 "CharacterSequence",
161 "void",
162 "boolean",
163 "byte",
164 "char",
165 "int",
166 "long",
167 "float",
168 "double"};
169 return std::find(kNoOutParamTypes.begin(), kNoOutParamTypes.end(), typeName) !=
170 kNoOutParamTypes.end();
171 }
172
emitAidl(const Interface & interface,const Coordinator & coordinator,const std::map<const NamedType *,const ProcessedCompoundType> & processedTypes)173 void AidlHelper::emitAidl(
174 const Interface& interface, const Coordinator& coordinator,
175 const std::map<const NamedType*, const ProcessedCompoundType>& processedTypes) {
176 Formatter out = getFileWithHeader(interface, coordinator, processedTypes);
177
178 interface.emitDocComment(out);
179 if (interface.superType() && interface.superType()->fqName() != gIBaseFqName) {
180 out << "// Interface inherits from " << interface.superType()->fqName().string()
181 << " but AIDL does not support interface inheritance.\n";
182 }
183
184 out << "@VintfStability\n";
185 out << "interface " << getAidlName(interface.fqName()) << " ";
186 out.block([&] {
187 std::map<std::string, NodeWithVersion<NamedType>> latestTypeForBaseName;
188 std::vector<const NodeWithVersion<NamedType>> supersededNamedTypes;
189 std::map<std::string, NodeWithVersion<Method>> latestMethodForBaseName;
190 std::vector<const NodeWithVersion<Method>> supersededMethods;
191 for (const Interface* iface : interface.typeChain()) {
192 if (!AidlHelper::shouldBeExpanded(interface.fqName(), iface->fqName())) {
193 // Stop traversing extended interfaces once they leave this package
194 break;
195 }
196 for (const Method* method : iface->userDefinedMethods()) {
197 pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(),
198 iface->fqName().getPackageMinorVersion(), method,
199 getBaseName(method->name())},
200 &latestMethodForBaseName, &supersededMethods);
201 }
202 // Types from other interfaces will be handled while those interfaces
203 // are being emitted.
204 if (iface->getBaseName() != interface.getBaseName()) {
205 continue;
206 }
207 for (const NamedType* type : iface->getSubTypes()) {
208 // The baseName for types is not being stripped of the version
209 // numbers like that of the methods. If a type was named
210 // BigStruct_1_1 and the previous version was named BigStruct,
211 // they will be treated as two different types.
212 pushVersionedNodeOntoMap({iface->fqName().getPackageMajorVersion(),
213 iface->fqName().getPackageMinorVersion(), type,
214 getAidlName(type->fqName())},
215 &latestTypeForBaseName, &supersededNamedTypes);
216 }
217 }
218
219 // Add comment for superseded types
220 out.join(supersededNamedTypes.begin(), supersededNamedTypes.end(), "\n",
221 [&](const NodeWithVersion<NamedType>& versionedType) {
222 out << "// Ignoring type " << getAidlName(versionedType.node->fqName())
223 << " from " << versionedType.major << "." << versionedType.minor
224 << "::" << getAidlName(interface.fqName())
225 << " since a newer alternative is available.";
226 });
227 if (!supersededNamedTypes.empty()) out << "\n\n";
228
229 // Add comment for superseded methods
230 out.join(supersededMethods.begin(), supersededMethods.end(), "\n",
231 [&](const NodeWithVersion<Method>& versionedMethod) {
232 out << "// Ignoring method " << versionedMethod.node->name() << " from "
233 << versionedMethod.major << "." << versionedMethod.minor
234 << "::" << getAidlName(interface.fqName())
235 << " since a newer alternative is available.";
236 });
237 if (!supersededMethods.empty()) out << "\n\n";
238
239 // Emit latest methods defined for this interface
240 out.join(latestMethodForBaseName.begin(), latestMethodForBaseName.end(), "\n",
241 [&](const std::pair<std::string, NodeWithVersion<Method>>& methodPair) {
242 const Method* method = methodPair.second.node;
243 const std::string& baseName = methodPair.first;
244 std::vector<NamedReference<Type>*> results;
245 std::vector<ResultTransformation> transformations;
246 for (NamedReference<Type>* res : method->results()) {
247 const std::string aidlType = getAidlType(*res->get(), interface.fqName());
248
249 if (shouldWarnStatusType(aidlType)) {
250 out << "// FIXME: AIDL has built-in status types. Do we need the "
251 "status type here?\n";
252 }
253 if (method->results().size() > 1 && shouldWarnOutParam(aidlType)) {
254 out << "// FIXME: AIDL does not allow " << aidlType
255 << " to be an out parameter.\n";
256 out << "// Move it to return, or add it to a Parcelable.\n";
257 }
258 results.push_back(res);
259 }
260
261 if (method->name() != baseName) {
262 out << "// Changing method name from " << method->name() << " to "
263 << baseName << "\n";
264 }
265
266 std::string returnType = "void";
267 if (results.size() == 1) {
268 returnType = getAidlType(*results[0]->get(), interface.fqName());
269
270 out << "// Adding return type to method instead of out param "
271 << returnType << " " << results[0]->name()
272 << " since there is only one return value.\n";
273 transformations.emplace_back(ResultTransformation{
274 results[0]->name(), ResultTransformation::TransformType::MOVED});
275 results.clear();
276 }
277
278 if (method->getDocComment() != nullptr) {
279 std::vector<std::string> modifiedDocComment;
280 for (const std::string& line : method->getDocComment()->lines()) {
281 std::vector<std::string> tokens = base::Split(line, " ");
282 if (tokens.size() <= 1 || tokens[0] != "@return") {
283 // unimportant line
284 modifiedDocComment.emplace_back(line);
285 continue;
286 }
287
288 const std::string& res = tokens[1];
289 bool transformed = false;
290 for (const ResultTransformation& transform : transformations) {
291 if (transform.resultName != res) continue;
292
293 // Some transform was done to it
294 if (transform.type == ResultTransformation::TransformType::MOVED) {
295 // remove the name
296 tokens.erase(++tokens.begin());
297 transformed = true;
298 } else {
299 CHECK(transform.type ==
300 ResultTransformation::TransformType::REMOVED);
301 tokens.insert(tokens.begin(),
302 "FIXME: The following return was removed\n");
303 transformed = true;
304 }
305 }
306
307 if (!transformed) {
308 tokens.erase(tokens.begin());
309 tokens.insert(tokens.begin(), "@param out");
310 }
311
312 modifiedDocComment.emplace_back(base::Join(tokens, " "));
313 }
314
315 DocComment(modifiedDocComment, HIDL_LOCATION_HERE).emit(out);
316 }
317
318 WrappedOutput wrappedOutput(MAX_LINE_LENGTH);
319
320 if (method->isOneway()) wrappedOutput << "oneway ";
321 wrappedOutput << returnType << " " << baseName << "(";
322
323 if (results.empty()) {
324 emitAidlMethodParams(&wrappedOutput, method->args(), /* prefix */ "in ",
325 /* attachToLast */ ");\n", interface);
326 } else {
327 const bool emitArgs = !method->args().empty();
328 if (emitArgs) {
329 emitAidlMethodParams(&wrappedOutput, method->args(),
330 /* prefix */ "in ",
331 /* attachToLast */ ",", interface);
332 }
333 wrappedOutput.group([&] {
334 if (emitArgs) wrappedOutput.printUnlessWrapped(" ");
335 emitAidlMethodParams(&wrappedOutput, results, /* prefix */ "out ",
336 /* attachToLast */ ");\n", interface);
337 });
338 }
339
340 out << wrappedOutput;
341 });
342 });
343 }
344
345 } // namespace android
346