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 
17 #include "check_valid.h"
18 #include "aidl.h"
19 
20 #include <vector>
21 
22 namespace android {
23 namespace aidl {
24 
25 using TypePredicate = std::function<bool(const AidlTypeSpecifier&)>;
26 using DefinedTypePredicate = std::function<bool(const AidlDefinedType&)>;
27 
28 namespace {
IsListOf(const AidlTypeSpecifier & type,TypePredicate pred)29 bool IsListOf(const AidlTypeSpecifier& type, TypePredicate pred) {
30   return type.GetName() == "List" && type.IsGeneric() && type.GetTypeParameters().size() == 1 &&
31          pred(*type.GetTypeParameters().at(0));
32 }
IsArrayOf(const AidlTypeSpecifier & type,TypePredicate pred)33 bool IsArrayOf(const AidlTypeSpecifier& type, TypePredicate pred) {
34   return type.IsArray() && pred(type);
35 }
IsInterface(const AidlTypeSpecifier & type)36 bool IsInterface(const AidlTypeSpecifier& type) {
37   return type.GetDefinedType() && type.GetDefinedType()->AsInterface();
38 }
39 }  // namespace
40 
41 struct CheckTypeVisitor : AidlVisitor {
42   bool success = true;
43   std::vector<TypePredicate> checkers;
44   std::vector<DefinedTypePredicate> defined_checkers;
45 
Visitandroid::aidl::CheckTypeVisitor46   void Visit(const AidlTypeSpecifier& type) override {
47     for (auto& checker : checkers) {
48       if (!checker(type)) {
49         success = false;
50       }
51     }
52   }
Visitandroid::aidl::CheckTypeVisitor53   void Visit(const AidlInterface& t) override { CheckDefinedType(t); }
Visitandroid::aidl::CheckTypeVisitor54   void Visit(const AidlEnumDeclaration& t) override { CheckDefinedType(t); }
Visitandroid::aidl::CheckTypeVisitor55   void Visit(const AidlStructuredParcelable& t) override { CheckDefinedType(t); }
Visitandroid::aidl::CheckTypeVisitor56   void Visit(const AidlUnionDecl& t) override { CheckDefinedType(t); }
Visitandroid::aidl::CheckTypeVisitor57   void Visit(const AidlParcelable& t) override { CheckDefinedType(t); }
58 
Checkandroid::aidl::CheckTypeVisitor59   void Check(TypePredicate checker) { checkers.push_back(std::move(checker)); }
Checkandroid::aidl::CheckTypeVisitor60   void Check(DefinedTypePredicate checker) { defined_checkers.push_back(std::move(checker)); }
61 
62  private:
CheckDefinedTypeandroid::aidl::CheckTypeVisitor63   void CheckDefinedType(const AidlDefinedType& type) {
64     for (auto& checker : defined_checkers) {
65       if (!checker(type)) {
66         success = false;
67       }
68     }
69   }
70 };
71 
CheckValid(const AidlDocument & doc,const Options & options)72 bool CheckValid(const AidlDocument& doc, const Options& options) {
73   const auto lang = options.TargetLanguage();
74   const auto min_sdk_version = options.GetMinSdkVersion();
75 
76   CheckTypeVisitor v;
77 
78   v.Check([&](const AidlTypeSpecifier& type) {
79     const auto valid_version = MinSdkVersionFromString("Tiramisu").value();
80     if ((IsListOf(type, IsInterface) || IsArrayOf(type, IsInterface)) &&
81         lang == Options::Language::JAVA && min_sdk_version < valid_version) {
82       const auto kind = IsListOf(type, IsInterface) ? "List" : "Array";
83       AIDL_ERROR(type) << kind << " of interfaces is available since SDK = " << valid_version
84                        << " in Java. Current min_sdk_version is " << min_sdk_version << ".";
85       return false;
86     }
87     return true;
88   });
89 
90   v.Check([&](const AidlTypeSpecifier& type) {
91     const auto valid_version = MinSdkVersionFromString("S").value();
92     if (type.GetName() == "ParcelableHolder" && min_sdk_version < valid_version) {
93       AIDL_ERROR(type) << " ParcelableHolder is available since SDK = " << valid_version
94                        << ". Current min_sdk_version is " << min_sdk_version << ".";
95       return false;
96     }
97     return true;
98   });
99 
100   // Check all nested types for potential #include cycles that would contain
101   // them. The algorithm performs a depth-first search on a graph with the
102   // following properties:
103   //
104   // * Graph nodes are top-level (non-nested) types, under the assumption that
105   //   there is a 1:1 mapping between top-level types and included headers. This
106   //   implies that a cycle between these types will be equivalent to a cycle
107   //   between headers.
108   //
109   // * Each edge U -> V represents a "declare V before U" relationship between
110   //   types. This means that V.h needs to be included by U.h, or the V type
111   //   needs to be forward-declared before U. For any type U, its neighbors
112   //   are all nodes V such that U or its nested types have a reference to V
113   //   or any type nested in it.
114   //
115   // * The algorithm tries to find a cycle containing start_type. Such a
116   //   cycle exists if the following hold true:
117   //   * There exists a path from start_type to another top-level type T
118   //     (different from start_type)
119   //   * There is a back edge from T to start_type which closes the cycle
120   v.Check([&](const AidlDefinedType& start_type) {
121     if (start_type.GetParentType() == nullptr) {
122       return true;
123     }
124 
125     std::set<const AidlDefinedType*> visited;
126     std::function<bool(const AidlDefinedType*)> dfs = [&](const AidlDefinedType* type) {
127       if (!visited.insert(type).second) {
128         // Already visited
129         return false;
130       }
131 
132       for (const auto& t : Collect<AidlTypeSpecifier>(*type)) {
133         auto defined_type = t->GetDefinedType();
134         if (!defined_type) {
135           // Skip primitive/builtin types
136           continue;
137         }
138 
139         auto top_type = defined_type->GetRootType();
140         if (top_type == type) {
141           // Skip type references within the same top-level type
142           continue;
143         }
144 
145         if (defined_type == &start_type) {
146           // Found a cycle back to the starting nested type
147           return true;
148         }
149 
150         if (dfs(top_type)) {
151           // Found a cycle while visiting the top type for the next node
152           return true;
153         }
154       }
155 
156       return false;
157     };
158 
159     bool has_cycle = dfs(start_type.GetRootType());
160     if (has_cycle) {
161       AIDL_ERROR(start_type) << "has cyclic references to nested types.";
162       return false;
163     }
164 
165     return true;
166   });
167 
168   VisitTopDown(v, doc);
169   return v.success;
170 }
171 
172 }  // namespace aidl
173 }  // namespace android
174