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/result.h>
28 #include <android-base/strings.h>
29
30 namespace android {
31 namespace aidl {
32
33 using android::base::Error;
34 using android::base::Result;
35 using std::map;
36 using std::set;
37 using std::string;
38 using std::vector;
39
get_strict_annotations(const AidlAnnotatable & node)40 static set<AidlAnnotation> get_strict_annotations(const AidlAnnotatable& node) {
41 // This must be symmetrical (if you can add something, you must be able to
42 // remove it). The reason is that we have no way of knowing which interface a
43 // server serves and which interface a client serves (e.g. a callback
44 // interface). Note that this is being overly lenient. It makes sense for
45 // newer code to start accepting nullable things. However, here, we don't know
46 // if the client of an interface or the server of an interface is newer.
47 //
48 // Here are two examples to demonstrate this:
49 // - a new implementation might change so that it no longer returns null
50 // values (remove @nullable)
51 // - a new implementation might start accepting null values (add @nullable)
52 static const set<AidlAnnotation::Type> kIgnoreAnnotations{
53 AidlAnnotation::Type::NULLABLE,
54 AidlAnnotation::Type::JAVA_DEBUG,
55 AidlAnnotation::Type::IMMUTABLE,
56 };
57 set<AidlAnnotation> annotations;
58 for (const AidlAnnotation& annotation : node.GetAnnotations()) {
59 if (kIgnoreAnnotations.find(annotation.GetType()) == kIgnoreAnnotations.end()) {
60 annotations.insert(annotation);
61 }
62 }
63 return annotations;
64 }
65
have_compatible_annotations(const AidlAnnotatable & older,const AidlAnnotatable & newer)66 static bool have_compatible_annotations(const AidlAnnotatable& older,
67 const AidlAnnotatable& newer) {
68 set<AidlAnnotation> olderAnnotations = get_strict_annotations(older);
69 set<AidlAnnotation> newerAnnotations = get_strict_annotations(newer);
70
71 if (olderAnnotations != newerAnnotations) {
72 const string from = older.ToString().empty() ? "(empty)" : older.ToString();
73 const string to = newer.ToString().empty() ? "(empty)" : newer.ToString();
74 AIDL_ERROR(newer) << "Changed annotations: " << from << " to " << to;
75 return false;
76 }
77 return true;
78 }
79
are_compatible_types(const AidlTypeSpecifier & older,const AidlTypeSpecifier & newer)80 static bool are_compatible_types(const AidlTypeSpecifier& older, const AidlTypeSpecifier& newer) {
81 bool compatible = true;
82 if (older.ToString() != newer.ToString()) {
83 AIDL_ERROR(newer) << "Type changed: " << older.ToString() << " to " << newer.ToString() << ".";
84 compatible = false;
85 }
86 compatible &= have_compatible_annotations(older, newer);
87 return compatible;
88 }
89
are_compatible_interfaces(const AidlInterface & older,const AidlInterface & newer)90 static bool are_compatible_interfaces(const AidlInterface& older, const AidlInterface& newer) {
91 bool compatible = true;
92 compatible &= have_compatible_annotations(older, newer);
93
94 map<string, AidlMethod*> new_methods;
95 for (const auto& m : newer.AsInterface()->GetMethods()) {
96 new_methods.emplace(m->Signature(), m.get());
97 }
98
99 for (const auto& old_m : older.AsInterface()->GetMethods()) {
100 const auto found = new_methods.find(old_m->Signature());
101 if (found == new_methods.end()) {
102 AIDL_ERROR(old_m) << "Removed or changed method: " << older.GetCanonicalName() << "."
103 << old_m->Signature();
104 compatible = false;
105 continue;
106 }
107
108 // Compare IDs to detect method reordering. IDs are assigned by their
109 // textual order, so if there is an ID mismatch, that means reordering
110 // has happened.
111 const auto new_m = found->second;
112
113 if (old_m->IsOneway() != new_m->IsOneway()) {
114 AIDL_ERROR(new_m) << "Oneway attribute " << (old_m->IsOneway() ? "removed" : "added") << ": "
115 << older.GetCanonicalName() << "." << old_m->Signature();
116 compatible = false;
117 }
118
119 if (old_m->GetId() != new_m->GetId()) {
120 AIDL_ERROR(new_m) << "Transaction ID changed: " << older.GetCanonicalName() << "."
121 << old_m->Signature() << " is changed from " << old_m->GetId() << " to "
122 << new_m->GetId() << ".";
123 compatible = false;
124 }
125
126 compatible &= are_compatible_types(old_m->GetType(), new_m->GetType());
127
128 const auto& old_args = old_m->GetArguments();
129 const auto& new_args = new_m->GetArguments();
130 // this is guaranteed because arguments are part of AidlMethod::Signature()
131 CHECK(old_args.size() == new_args.size());
132 for (size_t i = 0; i < old_args.size(); i++) {
133 const AidlArgument& old_a = *(old_args.at(i));
134 const AidlArgument& new_a = *(new_args.at(i));
135 compatible &= are_compatible_types(old_a.GetType(), new_a.GetType());
136
137 if (old_a.GetDirection() != new_a.GetDirection()) {
138 AIDL_ERROR(new_m) << "Direction changed: " << old_a.GetDirectionSpecifier() << " to "
139 << new_a.GetDirectionSpecifier() << ".";
140 compatible = false;
141 }
142 }
143 }
144
145 map<string, AidlConstantDeclaration*> new_constdecls;
146 for (const auto& c : newer.AsInterface()->GetConstantDeclarations()) {
147 new_constdecls.emplace(c->GetName(), c.get());
148 }
149
150 for (const auto& old_c : older.AsInterface()->GetConstantDeclarations()) {
151 const auto found = new_constdecls.find(old_c->GetName());
152 if (found == new_constdecls.end()) {
153 AIDL_ERROR(old_c) << "Removed constant declaration: " << older.GetCanonicalName() << "."
154 << old_c->GetName();
155 compatible = false;
156 continue;
157 }
158
159 const auto new_c = found->second;
160 compatible &= are_compatible_types(old_c->GetType(), new_c->GetType());
161
162 const string old_value = old_c->ValueString(AidlConstantValueDecorator);
163 const string new_value = new_c->ValueString(AidlConstantValueDecorator);
164 if (old_value != new_value) {
165 AIDL_ERROR(newer) << "Changed constant value: " << older.GetCanonicalName() << "."
166 << old_c->GetName() << " from " << old_value << " to " << new_value << ".";
167 compatible = false;
168 }
169 }
170 return compatible;
171 }
172
173 // returns whether the given type when defaulted will be accepted by
174 // unmarshalling code
has_usable_nil_type(const AidlTypeSpecifier & specifier)175 static bool has_usable_nil_type(const AidlTypeSpecifier& specifier) {
176 // TODO(b/155238508): fix for primitives
177
178 // This technically only applies in C++, but even if both the client and the
179 // server of an interface are in Java at a particular point in time, where
180 // null is currently always acceptable, we want to make sure that versions
181 // of this service can work in native and future backends without a problem.
182 // Also, in that case, adding nullable does not hurt.
183 return specifier.IsNullable();
184 }
185
are_compatible_parcelables(const AidlStructuredParcelable & older,const AidlStructuredParcelable & newer)186 static bool are_compatible_parcelables(const AidlStructuredParcelable& older,
187 const AidlStructuredParcelable& newer) {
188 const auto& old_fields = older.GetFields();
189 const auto& new_fields = newer.GetFields();
190 if (old_fields.size() > new_fields.size()) {
191 // you can add new fields only at the end
192 AIDL_ERROR(newer) << "Number of fields in " << older.GetCanonicalName() << " is reduced from "
193 << old_fields.size() << " to " << new_fields.size() << ".";
194 return false;
195 }
196
197 bool compatible = true;
198 for (size_t i = 0; i < old_fields.size(); i++) {
199 const auto& old_field = old_fields.at(i);
200 const auto& new_field = new_fields.at(i);
201 compatible &= are_compatible_types(old_field->GetType(), new_field->GetType());
202
203 const string old_value = old_field->ValueString(AidlConstantValueDecorator);
204 const string new_value = new_field->ValueString(AidlConstantValueDecorator);
205 if (old_value != new_value) {
206 AIDL_ERROR(new_field) << "Changed default value: " << old_value << " to " << new_value << ".";
207 compatible = false;
208 }
209 }
210
211 // Reordering of fields is an incompatible change.
212 for (size_t i = 0; i < new_fields.size(); i++) {
213 const auto& new_field = new_fields.at(i);
214 auto found = std::find_if(old_fields.begin(), old_fields.end(), [&new_field](const auto& f) {
215 return new_field->GetName() == f->GetName();
216 });
217 if (found != old_fields.end()) {
218 size_t old_index = std::distance(old_fields.begin(), found);
219 if (old_index != i) {
220 AIDL_ERROR(new_field) << "Reordered " << new_field->GetName() << " from " << old_index
221 << " to " << i << ".";
222 compatible = false;
223 }
224 }
225 }
226
227 for (size_t i = old_fields.size(); i < new_fields.size(); i++) {
228 const auto& new_field = new_fields.at(i);
229 if (!new_field->GetDefaultValue() && !has_usable_nil_type(new_field->GetType())) {
230 // Old API versions may suffer from the issue presented here. There is
231 // only a finite number in Android, which we must allow indefinitely.
232 struct HistoricalException {
233 std::string canonical;
234 std::string field;
235 };
236 static std::vector<HistoricalException> exceptions = {
237 {"android.net.DhcpResultsParcelable", "serverHostName"},
238 {"android.net.ResolverParamsParcel", "resolverOptions"},
239 };
240 bool excepted = false;
241 for (const HistoricalException& exception : exceptions) {
242 if (older.GetCanonicalName() == exception.canonical &&
243 new_field->GetName() == exception.field) {
244 excepted = true;
245 break;
246 }
247 }
248 if (excepted) continue;
249
250 AIDL_ERROR(new_field)
251 << "Field '" << new_field->GetName()
252 << "' does not have a useful default in some backends. Please either provide a default "
253 "value for this field or mark the field as @nullable. This value or a null value will "
254 "be used automatically when an old version of this parcelable is sent to a process "
255 "which understands a new version of this parcelable. In order to make sure your code "
256 "continues to be backwards compatible, make sure the default or null value does not "
257 "cause a semantic change to this parcelable.";
258 compatible = false;
259 }
260 }
261 return compatible;
262 }
263
are_compatible_enums(const AidlEnumDeclaration & older,const AidlEnumDeclaration & newer)264 static bool are_compatible_enums(const AidlEnumDeclaration& older,
265 const AidlEnumDeclaration& newer) {
266 if (!are_compatible_types(older.GetBackingType(), newer.GetBackingType())) {
267 AIDL_ERROR(newer) << "Changed backing types.";
268 return false;
269 }
270
271 std::map<std::string, const AidlConstantValue*> old_enum_map;
272 for (const auto& enumerator : older.GetEnumerators()) {
273 old_enum_map[enumerator->GetName()] = enumerator->GetValue();
274 }
275 std::map<std::string, const AidlConstantValue*> new_enum_map;
276 for (const auto& enumerator : newer.GetEnumerators()) {
277 new_enum_map[enumerator->GetName()] = enumerator->GetValue();
278 }
279
280 bool compatible = true;
281 for (const auto& [name, value] : old_enum_map) {
282 if (new_enum_map.find(name) == new_enum_map.end()) {
283 AIDL_ERROR(newer) << "Removed enumerator from " << older.GetCanonicalName() << ": " << name;
284 compatible = false;
285 continue;
286 }
287 const string old_value =
288 old_enum_map[name]->ValueString(older.GetBackingType(), AidlConstantValueDecorator);
289 const string new_value =
290 new_enum_map[name]->ValueString(newer.GetBackingType(), AidlConstantValueDecorator);
291 if (old_value != new_value) {
292 AIDL_ERROR(newer) << "Changed enumerator value: " << older.GetCanonicalName() << "::" << name
293 << " from " << old_value << " to " << new_value << ".";
294 compatible = false;
295 }
296 }
297 return compatible;
298 }
299
load_from_dir(const Options & options,const IoDelegate & io_delegate,const std::string & dir)300 static Result<AidlTypenames> load_from_dir(const Options& options, const IoDelegate& io_delegate,
301 const std::string& dir) {
302 AidlTypenames typenames;
303 for (const auto& file : io_delegate.ListFiles(dir)) {
304 if (!android::base::EndsWith(file, ".aidl")) continue;
305 if (internals::load_and_validate_aidl(file, options, io_delegate, &typenames,
306 nullptr /* imported_files */) != AidlError::OK) {
307 AIDL_ERROR(file) << "Failed to read.";
308 return Error();
309 }
310 }
311 return typenames;
312 }
313
check_api(const Options & options,const IoDelegate & io_delegate)314 bool check_api(const Options& options, const IoDelegate& io_delegate) {
315 CHECK(options.IsStructured());
316 CHECK(options.InputFiles().size() == 2) << "--checkapi requires two inputs "
317 << "but got " << options.InputFiles().size();
318 auto old_tns = load_from_dir(options, io_delegate, options.InputFiles().at(0));
319 if (!old_tns.ok()) {
320 return false;
321 }
322 auto new_tns = load_from_dir(options, io_delegate, options.InputFiles().at(1));
323 if (!new_tns.ok()) {
324 return false;
325 }
326
327 std::vector<AidlDefinedType*> old_types = old_tns->AllDefinedTypes();
328 std::vector<AidlDefinedType*> new_types = new_tns->AllDefinedTypes();
329
330 map<string, AidlDefinedType*> new_map;
331 for (const auto t : new_types) {
332 new_map.emplace(t->GetCanonicalName(), t);
333 }
334
335 bool compatible = true;
336 for (const auto old_type : old_types) {
337 const auto found = new_map.find(old_type->GetCanonicalName());
338 if (found == new_map.end()) {
339 AIDL_ERROR(old_type) << "Removed type: " << old_type->GetCanonicalName();
340 compatible = false;
341 continue;
342 }
343 const auto new_type = found->second;
344
345 if (old_type->AsInterface() != nullptr) {
346 if (new_type->AsInterface() == nullptr) {
347 AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
348 << " is changed from " << old_type->GetPreprocessDeclarationName()
349 << " to " << new_type->GetPreprocessDeclarationName();
350 compatible = false;
351 continue;
352 }
353 compatible &=
354 are_compatible_interfaces(*(old_type->AsInterface()), *(new_type->AsInterface()));
355 } else if (old_type->AsStructuredParcelable() != nullptr) {
356 if (new_type->AsStructuredParcelable() == nullptr) {
357 AIDL_ERROR(new_type) << "Parcelable" << new_type->GetCanonicalName()
358 << " is not structured. ";
359 compatible = false;
360 continue;
361 }
362 compatible &= are_compatible_parcelables(*(old_type->AsStructuredParcelable()),
363 *(new_type->AsStructuredParcelable()));
364 } else if (old_type->AsEnumDeclaration() != nullptr) {
365 if (new_type->AsEnumDeclaration() == nullptr) {
366 AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
367 << " is changed from " << old_type->GetPreprocessDeclarationName()
368 << " to " << new_type->GetPreprocessDeclarationName();
369 compatible = false;
370 continue;
371 }
372 compatible &=
373 are_compatible_enums(*(old_type->AsEnumDeclaration()), *(new_type->AsEnumDeclaration()));
374 } else {
375 AIDL_ERROR(old_type) << "Unsupported type " << old_type->GetPreprocessDeclarationName()
376 << " for " << old_type->GetCanonicalName();
377 compatible = false;
378 }
379 }
380
381 return compatible;
382 }
383
384 } // namespace aidl
385 } // namespace android
386