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