1 /*
2 * Copyright (C) 2018, 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 "aidl.h"
18 #include "aidl_language.h"
19 #include "import_resolver.h"
20 #include "logging.h"
21 #include "options.h"
22
23 #include <map>
24 #include <string>
25 #include <vector>
26
27 #include <android-base/strings.h>
28
29 namespace android {
30 namespace aidl {
31
32 using std::map;
33 using std::set;
34 using std::string;
35 using std::vector;
36
get_strict_annotations(const AidlAnnotatable & node)37 static set<AidlAnnotation> get_strict_annotations(const AidlAnnotatable& node) {
38 // This must be symmetrical (if you can add something, you must be able to
39 // remove it). The reason is that we have no way of knowing which interface a
40 // server serves and which interface a client serves (e.g. a callback
41 // interface). Note that this is being overly lenient. It makes sense for
42 // newer code to start accepting nullable things. However, here, we don't know
43 // if the client of an interface or the server of an interface is newer.
44 //
45 // Here are two examples to demonstrate this:
46 // - a new implementation might change so that it no longer returns null
47 // values (remove @nullable)
48 // - a new implementation might start accepting null values (add @nullable)
49 static const set<std::string> kIgnoreAnnotations{
50 "nullable",
51 };
52 set<AidlAnnotation> annotations;
53 for (const AidlAnnotation& annotation : node.GetAnnotations()) {
54 if (kIgnoreAnnotations.find(annotation.GetName()) == kIgnoreAnnotations.end()) {
55 annotations.insert(annotation);
56 }
57 }
58 return annotations;
59 }
60
have_compatible_annotations(const AidlAnnotatable & older,const AidlAnnotatable & newer)61 static bool have_compatible_annotations(const AidlAnnotatable& older,
62 const AidlAnnotatable& newer) {
63 set<AidlAnnotation> olderAnnotations = get_strict_annotations(older);
64 set<AidlAnnotation> newerAnnotations = get_strict_annotations(newer);
65
66 if (olderAnnotations != newerAnnotations) {
67 const string from = older.ToString().empty() ? "(empty)" : older.ToString();
68 const string to = newer.ToString().empty() ? "(empty)" : newer.ToString();
69 AIDL_ERROR(newer) << "Changed annotations: " << from << " to " << to;
70 return false;
71 }
72 return true;
73 }
74
are_compatible_types(const AidlTypeSpecifier & older,const AidlTypeSpecifier & newer)75 static bool are_compatible_types(const AidlTypeSpecifier& older, const AidlTypeSpecifier& newer) {
76 bool compatible = true;
77 if (older.ToString() != newer.ToString()) {
78 AIDL_ERROR(newer) << "Type changed: " << older.ToString() << " to " << newer.ToString() << ".";
79 compatible = false;
80 }
81 compatible &= have_compatible_annotations(older, newer);
82 return compatible;
83 }
84
are_compatible_interfaces(const AidlInterface & older,const AidlInterface & newer)85 static bool are_compatible_interfaces(const AidlInterface& older, const AidlInterface& newer) {
86 bool compatible = true;
87 compatible &= have_compatible_annotations(older, newer);
88
89 map<string, AidlMethod*> new_methods;
90 for (const auto& m : newer.AsInterface()->GetMethods()) {
91 new_methods.emplace(m->Signature(), m.get());
92 }
93
94 for (const auto& old_m : older.AsInterface()->GetMethods()) {
95 const auto found = new_methods.find(old_m->Signature());
96 if (found == new_methods.end()) {
97 AIDL_ERROR(old_m) << "Removed or changed method: " << older.GetCanonicalName() << "."
98 << old_m->Signature();
99 compatible = false;
100 continue;
101 }
102
103 // Compare IDs to detect method reordering. IDs are assigned by their
104 // textual order, so if there is an ID mismatch, that means reordering
105 // has happened.
106 const auto new_m = found->second;
107
108 if (old_m->IsOneway() != new_m->IsOneway()) {
109 AIDL_ERROR(new_m) << "Oneway attribute " << (old_m->IsOneway() ? "removed" : "added") << ": "
110 << older.GetCanonicalName() << "." << old_m->Signature();
111 compatible = false;
112 }
113
114 if (old_m->GetId() != new_m->GetId()) {
115 AIDL_ERROR(new_m) << "Transaction ID changed: " << older.GetCanonicalName() << "."
116 << old_m->Signature() << " is changed from " << old_m->GetId() << " to "
117 << new_m->GetId() << ".";
118 compatible = false;
119 }
120
121 compatible &= are_compatible_types(old_m->GetType(), new_m->GetType());
122
123 const auto& old_args = old_m->GetArguments();
124 const auto& new_args = new_m->GetArguments();
125 // this is guaranteed because arguments are part of AidlMethod::Signature()
126 CHECK(old_args.size() == new_args.size());
127 for (size_t i = 0; i < old_args.size(); i++) {
128 const AidlArgument& old_a = *(old_args.at(i));
129 const AidlArgument& new_a = *(new_args.at(i));
130 compatible &= are_compatible_types(old_a.GetType(), new_a.GetType());
131
132 if (old_a.GetDirection() != new_a.GetDirection()) {
133 AIDL_ERROR(new_m) << "Direction changed: " << old_a.GetDirectionSpecifier() << " to "
134 << new_a.GetDirectionSpecifier() << ".";
135 compatible = false;
136 }
137 }
138 }
139
140 map<string, AidlConstantDeclaration*> new_constdecls;
141 for (const auto& c : newer.AsInterface()->GetConstantDeclarations()) {
142 new_constdecls.emplace(c->GetName(), c.get());
143 }
144
145 for (const auto& old_c : older.AsInterface()->GetConstantDeclarations()) {
146 const auto found = new_constdecls.find(old_c->GetName());
147 if (found == new_constdecls.end()) {
148 AIDL_ERROR(old_c) << "Removed constant declaration: " << older.GetCanonicalName() << "."
149 << old_c->GetName();
150 compatible = false;
151 continue;
152 }
153
154 const auto new_c = found->second;
155 compatible &= are_compatible_types(old_c->GetType(), new_c->GetType());
156
157 const string old_value = old_c->ValueString(AidlConstantValueDecorator);
158 const string new_value = new_c->ValueString(AidlConstantValueDecorator);
159 if (old_value != new_value) {
160 AIDL_ERROR(newer) << "Changed constant value: " << older.GetCanonicalName() << "."
161 << old_c->GetName() << " from " << old_value << " to " << new_value << ".";
162 compatible = false;
163 }
164 }
165 return compatible;
166 }
167
are_compatible_parcelables(const AidlStructuredParcelable & older,const AidlStructuredParcelable & newer)168 static bool are_compatible_parcelables(const AidlStructuredParcelable& older,
169 const AidlStructuredParcelable& newer) {
170 const auto& old_fields = older.GetFields();
171 const auto& new_fields = newer.GetFields();
172 if (old_fields.size() > new_fields.size()) {
173 // you can add new fields only at the end
174 AIDL_ERROR(newer) << "Number of fields in " << older.GetCanonicalName() << " is reduced from "
175 << old_fields.size() << " to " << new_fields.size() << ".";
176 return false;
177 }
178
179 bool compatible = true;
180 for (size_t i = 0; i < old_fields.size(); i++) {
181 const auto& old_field = old_fields.at(i);
182 const auto& new_field = new_fields.at(i);
183 compatible &= are_compatible_types(old_field->GetType(), new_field->GetType());
184
185 const string old_value = old_field->ValueString(AidlConstantValueDecorator);
186 const string new_value = new_field->ValueString(AidlConstantValueDecorator);
187 if (old_value != new_value) {
188 AIDL_ERROR(newer) << "Changed default value: " << old_value << " to " << new_value << ".";
189 compatible = false;
190 }
191 }
192
193 // Reordering of fields is an incompatible change.
194 for (size_t i = 0; i < new_fields.size(); i++) {
195 const auto& new_field = new_fields.at(i);
196 auto found = std::find_if(old_fields.begin(), old_fields.end(), [&new_field](const auto& f) {
197 return new_field->GetName() == f->GetName();
198 });
199 if (found != old_fields.end()) {
200 size_t old_index = std::distance(old_fields.begin(), found);
201 if (old_index != i) {
202 AIDL_ERROR(new_field) << "Reordered " << new_field->GetName() << " from " << old_index
203 << " to " << i << ".";
204 compatible = false;
205 }
206 }
207 }
208
209 return compatible;
210 }
211
are_compatible_enums(const AidlEnumDeclaration & older,const AidlEnumDeclaration & newer)212 static bool are_compatible_enums(const AidlEnumDeclaration& older,
213 const AidlEnumDeclaration& newer) {
214 if (!are_compatible_types(older.GetBackingType(), newer.GetBackingType())) {
215 AIDL_ERROR(newer) << "Changed backing types.";
216 return false;
217 }
218
219 std::map<std::string, const AidlConstantValue*> old_enum_map;
220 for (const auto& enumerator : older.GetEnumerators()) {
221 old_enum_map[enumerator->GetName()] = enumerator->GetValue();
222 }
223 std::map<std::string, const AidlConstantValue*> new_enum_map;
224 for (const auto& enumerator : newer.GetEnumerators()) {
225 new_enum_map[enumerator->GetName()] = enumerator->GetValue();
226 }
227
228 bool compatible = true;
229 for (const auto& [name, value] : old_enum_map) {
230 if (new_enum_map.find(name) == new_enum_map.end()) {
231 AIDL_ERROR(newer) << "Removed enumerator from " << older.GetCanonicalName() << ": " << name;
232 compatible = false;
233 continue;
234 }
235 const string old_value =
236 old_enum_map[name]->ValueString(older.GetBackingType(), AidlConstantValueDecorator);
237 const string new_value =
238 new_enum_map[name]->ValueString(newer.GetBackingType(), AidlConstantValueDecorator);
239 if (old_value != new_value) {
240 AIDL_ERROR(newer) << "Changed enumerator value: " << older.GetCanonicalName() << "::" << name
241 << " from " << old_value << " to " << new_value << ".";
242 compatible = false;
243 }
244 }
245 return compatible;
246 }
247
check_api(const Options & options,const IoDelegate & io_delegate)248 bool check_api(const Options& options, const IoDelegate& io_delegate) {
249 CHECK(options.IsStructured());
250 CHECK(options.InputFiles().size() == 2) << "--checkapi requires two inputs "
251 << "but got " << options.InputFiles().size();
252 AidlTypenames old_tns;
253 const string old_dir = options.InputFiles().at(0);
254 vector<AidlDefinedType*> old_types;
255 vector<string> old_files = io_delegate.ListFiles(old_dir);
256 if (old_files.size() == 0) {
257 AIDL_ERROR(old_dir) << "No API file exist";
258 return false;
259 }
260 for (const auto& file : old_files) {
261 if (!android::base::EndsWith(file, ".aidl")) continue;
262
263 vector<AidlDefinedType*> types;
264 if (internals::load_and_validate_aidl(file, options, io_delegate, &old_tns, &types,
265 nullptr /* imported_files */) != AidlError::OK) {
266 AIDL_ERROR(file) << "Failed to read.";
267 return false;
268 }
269 old_types.insert(old_types.end(), types.begin(), types.end());
270 }
271
272 AidlTypenames new_tns;
273 const string new_dir = options.InputFiles().at(1);
274 vector<AidlDefinedType*> new_types;
275 vector<string> new_files = io_delegate.ListFiles(new_dir);
276 if (new_files.size() == 0) {
277 AIDL_ERROR(new_dir) << "No API file exist";
278 return false;
279 }
280 for (const auto& file : new_files) {
281 if (!android::base::EndsWith(file, ".aidl")) continue;
282
283 vector<AidlDefinedType*> types;
284 if (internals::load_and_validate_aidl(file, options, io_delegate, &new_tns, &types,
285 nullptr /* imported_files */) != AidlError::OK) {
286 AIDL_ERROR(file) << "Failed to read.";
287 return false;
288 }
289 new_types.insert(new_types.end(), types.begin(), types.end());
290 }
291
292 map<string, AidlDefinedType*> new_map;
293 for (const auto t : new_types) {
294 new_map.emplace(t->GetCanonicalName(), t);
295 }
296
297 bool compatible = true;
298 for (const auto old_type : old_types) {
299 const auto found = new_map.find(old_type->GetCanonicalName());
300 if (found == new_map.end()) {
301 AIDL_ERROR(old_type) << "Removed type: " << old_type->GetCanonicalName();
302 compatible = false;
303 continue;
304 }
305 const auto new_type = found->second;
306
307 if (old_type->AsInterface() != nullptr) {
308 if (new_type->AsInterface() == nullptr) {
309 AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
310 << " is changed from " << old_type->GetPreprocessDeclarationName()
311 << " to " << new_type->GetPreprocessDeclarationName();
312 compatible = false;
313 continue;
314 }
315 compatible &=
316 are_compatible_interfaces(*(old_type->AsInterface()), *(new_type->AsInterface()));
317 } else if (old_type->AsStructuredParcelable() != nullptr) {
318 if (new_type->AsStructuredParcelable() == nullptr) {
319 AIDL_ERROR(new_type) << "Parcelable" << new_type->GetCanonicalName()
320 << " is not structured. ";
321 compatible = false;
322 continue;
323 }
324 compatible &= are_compatible_parcelables(*(old_type->AsStructuredParcelable()),
325 *(new_type->AsStructuredParcelable()));
326 } else if (old_type->AsEnumDeclaration() != nullptr) {
327 if (new_type->AsEnumDeclaration() == nullptr) {
328 AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
329 << " is changed from " << old_type->GetPreprocessDeclarationName()
330 << " to " << new_type->GetPreprocessDeclarationName();
331 compatible = false;
332 continue;
333 }
334 compatible &=
335 are_compatible_enums(*(old_type->AsEnumDeclaration()), *(new_type->AsEnumDeclaration()));
336 } else {
337 AIDL_ERROR(old_type) << "Unsupported type " << old_type->GetPreprocessDeclarationName()
338 << " for " << old_type->GetCanonicalName();
339 compatible = false;
340 }
341 }
342
343 return compatible;
344 }
345
346 } // namespace aidl
347 } // namespace android
348