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