• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 #include <map>
20 #include <string>
21 #include <vector>
22 
23 #include <android-base/result.h>
24 #include <android-base/strings.h>
25 #include <gtest/gtest.h>
26 
27 #include "aidl_dumpapi.h"
28 #include "aidl_language.h"
29 #include "import_resolver.h"
30 #include "logging.h"
31 #include "options.h"
32 
33 namespace android {
34 namespace aidl {
35 
36 using android::base::Error;
37 using android::base::Result;
38 using android::base::StartsWith;
39 using std::map;
40 using std::set;
41 using std::string;
42 using std::vector;
43 
Dump(const AidlDefinedType & type)44 static std::string Dump(const AidlDefinedType& type) {
45   string code;
46   CodeWriterPtr out = CodeWriter::ForString(&code);
47   DumpVisitor visitor(*out, /*inline_constants=*/true);
48   type.DispatchVisit(visitor);
49   out->Close();
50   return code;
51 }
52 
53 // Uses each type's Dump() and GTest utility(EqHelper).
CheckEquality(const AidlDefinedType & older,const AidlDefinedType & newer)54 static bool CheckEquality(const AidlDefinedType& older, const AidlDefinedType& newer) {
55   using testing::internal::EqHelper;
56   auto older_file = older.GetLocation().GetFile();
57   auto newer_file = newer.GetLocation().GetFile();
58   auto result = EqHelper::Compare(older_file.data(), newer_file.data(), Dump(older), Dump(newer));
59   if (!result) {
60     AIDL_ERROR(newer) << result.failure_message();
61   }
62   return result;
63 }
64 
get_strict_annotations(const AidlAnnotatable & node)65 static vector<string> get_strict_annotations(const AidlAnnotatable& node) {
66   // This must be symmetrical (if you can add something, you must be able to
67   // remove it). The reason is that we have no way of knowing which interface a
68   // server serves and which interface a client serves (e.g. a callback
69   // interface). Note that this is being overly lenient. It makes sense for
70   // newer code to start accepting nullable things. However, here, we don't know
71   // if the client of an interface or the server of an interface is newer.
72   //
73   // Here are two examples to demonstrate this:
74   // - a new implementation might change so that it no longer returns null
75   // values (remove @nullable)
76   // - a new implementation might start accepting null values (add @nullable)
77   //
78   // AidlAnnotation::Type::SENSITIVE_DATA could be ignored for backwards
79   // compatibility, but is not. It should retroactively be applied to the
80   // older versions of the interface. When doing that, we need
81   // to add the new hash to the older versions after the change using
82   // tools/aidl/build/hash_gen.sh.
83   static const set<AidlAnnotation::Type> kIgnoreAnnotations{
84       AidlAnnotation::Type::NULLABLE,
85       // @JavaDerive doesn't affect read/write
86       AidlAnnotation::Type::JAVA_DERIVE,
87       AidlAnnotation::Type::JAVA_DEFAULT,
88       AidlAnnotation::Type::JAVA_DELEGATOR,
89       AidlAnnotation::Type::JAVA_ONLY_IMMUTABLE,
90       AidlAnnotation::Type::JAVA_SUPPRESS_LINT,
91       // @Backing for a enum type is checked by the enum checker
92       AidlAnnotation::Type::BACKING,
93       // @RustDerive doesn't affect read/write
94       AidlAnnotation::Type::RUST_DERIVE,
95       AidlAnnotation::Type::SUPPRESS_WARNINGS,
96   };
97   vector<string> annotations;
98   for (const auto& annotation : node.GetAnnotations()) {
99     if (kIgnoreAnnotations.find(annotation->GetType()) != kIgnoreAnnotations.end()) {
100       continue;
101     }
102     auto annotation_string = annotation->ToString();
103     // adding @Deprecated (with optional args) is okay
104     if (StartsWith(annotation_string, "@JavaPassthrough(annotation=\"@Deprecated")) {
105       continue;
106     }
107     annotations.push_back(annotation_string);
108   }
109   return annotations;
110 }
111 
have_compatible_annotations(const AidlAnnotatable & older,const AidlAnnotatable & newer)112 static bool have_compatible_annotations(const AidlAnnotatable& older,
113                                         const AidlAnnotatable& newer) {
114   vector<string> olderAnnotations = get_strict_annotations(older);
115   vector<string> newerAnnotations = get_strict_annotations(newer);
116   sort(olderAnnotations.begin(), olderAnnotations.end());
117   sort(newerAnnotations.begin(), newerAnnotations.end());
118   if (olderAnnotations != newerAnnotations) {
119     const string from = older.ToString().empty() ? "(empty)" : older.ToString();
120     const string to = newer.ToString().empty() ? "(empty)" : newer.ToString();
121     AIDL_ERROR(newer) << "Changed annotations: " << from << " to " << to;
122     return false;
123   }
124   return true;
125 }
126 
are_compatible_types(const AidlTypeSpecifier & older,const AidlTypeSpecifier & newer)127 static bool are_compatible_types(const AidlTypeSpecifier& older, const AidlTypeSpecifier& newer) {
128   bool compatible = true;
129   if (older.Signature() != newer.Signature()) {
130     AIDL_ERROR(newer) << "Type changed: " << older.Signature() << " to " << newer.Signature()
131                       << ".";
132     compatible = false;
133   }
134   compatible &= have_compatible_annotations(older, newer);
135   return compatible;
136 }
137 
are_compatible_constants(const AidlDefinedType & older,const AidlDefinedType & newer)138 static bool are_compatible_constants(const AidlDefinedType& older, const AidlDefinedType& newer) {
139   bool compatible = true;
140 
141   map<string, AidlConstantDeclaration*> new_constdecls;
142   for (const auto& c : newer.GetConstantDeclarations()) {
143     new_constdecls[c->GetName()] = &*c;
144   }
145 
146   for (const auto& old_c : older.GetConstantDeclarations()) {
147     const auto found = new_constdecls.find(old_c->GetName());
148     if (found == new_constdecls.end()) {
149       AIDL_ERROR(old_c) << "Removed constant declaration: " << older.GetCanonicalName() << "."
150                         << old_c->GetName();
151       compatible = false;
152       continue;
153     }
154 
155     const auto new_c = found->second;
156     compatible &= are_compatible_types(old_c->GetType(), new_c->GetType());
157 
158     const string old_value = old_c->ValueString(AidlConstantValueDecorator);
159     const string new_value = new_c->ValueString(AidlConstantValueDecorator);
160     if (old_value != new_value) {
161       AIDL_ERROR(newer) << "Changed constant value: " << older.GetCanonicalName() << "."
162                         << old_c->GetName() << " from " << old_value << " to " << new_value << ".";
163       compatible = false;
164     }
165   }
166   return compatible;
167 }
168 
are_compatible_interfaces(const AidlInterface & older,const AidlInterface & newer)169 static bool are_compatible_interfaces(const AidlInterface& older, const AidlInterface& newer) {
170   bool compatible = true;
171 
172   map<string, AidlMethod*> new_methods;
173   for (const auto& m : newer.AsInterface()->GetMethods()) {
174     new_methods.emplace(m->Signature(), m.get());
175   }
176 
177   for (const auto& old_m : older.AsInterface()->GetMethods()) {
178     const auto found = new_methods.find(old_m->Signature());
179     if (found == new_methods.end()) {
180       AIDL_ERROR(old_m) << "Removed or changed method: " << older.GetCanonicalName() << "."
181                         << old_m->Signature();
182       compatible = false;
183       continue;
184     }
185 
186     // Compare IDs to detect method reordering. IDs are assigned by their
187     // textual order, so if there is an ID mismatch, that means reordering
188     // has happened.
189     const auto new_m = found->second;
190 
191     if (old_m->IsOneway() != new_m->IsOneway()) {
192       AIDL_ERROR(new_m) << "Oneway attribute " << (old_m->IsOneway() ? "removed" : "added") << ": "
193                         << older.GetCanonicalName() << "." << old_m->Signature();
194       compatible = false;
195     }
196 
197     if (old_m->GetId() != new_m->GetId()) {
198       AIDL_ERROR(new_m) << "Transaction ID changed: " << older.GetCanonicalName() << "."
199                         << old_m->Signature() << " is changed from " << old_m->GetId() << " to "
200                         << new_m->GetId() << ".";
201       compatible = false;
202     }
203 
204     compatible &= are_compatible_types(old_m->GetType(), new_m->GetType());
205 
206     const auto& old_args = old_m->GetArguments();
207     const auto& new_args = new_m->GetArguments();
208     // this is guaranteed because arguments are part of AidlMethod::Signature()
209     AIDL_FATAL_IF(old_args.size() != new_args.size(), old_m);
210     for (size_t i = 0; i < old_args.size(); i++) {
211       const AidlArgument& old_a = *(old_args.at(i));
212       const AidlArgument& new_a = *(new_args.at(i));
213       compatible &= are_compatible_types(old_a.GetType(), new_a.GetType());
214 
215       if (old_a.GetDirection() != new_a.GetDirection()) {
216         AIDL_ERROR(new_m) << "Direction changed: " << old_a.GetDirectionSpecifier() << " to "
217                           << new_a.GetDirectionSpecifier() << ".";
218         compatible = false;
219       }
220     }
221   }
222 
223   compatible = are_compatible_constants(older, newer) && compatible;
224 
225   return compatible;
226 }
227 
HasZeroEnumerator(const AidlEnumDeclaration & enum_decl)228 static bool HasZeroEnumerator(const AidlEnumDeclaration& enum_decl) {
229   return std::any_of(enum_decl.GetEnumerators().begin(), enum_decl.GetEnumerators().end(),
230                      [&](const unique_ptr<AidlEnumerator>& enumerator) {
231                        return enumerator->GetValue()->ValueString(
232                                   enum_decl.GetBackingType(), AidlConstantValueDecorator) == "0";
233                      });
234 }
235 
EvaluatesToZero(const AidlEnumDeclaration & enum_decl,const AidlConstantValue * value)236 static bool EvaluatesToZero(const AidlEnumDeclaration& enum_decl, const AidlConstantValue* value) {
237   if (value == nullptr) return true;
238   return value->ValueString(enum_decl.GetBackingType(), AidlConstantValueDecorator) == "0";
239 }
240 
are_compatible_parcelables(const AidlDefinedType & older,const AidlTypenames &,const AidlDefinedType & newer,const AidlTypenames & new_types)241 static bool are_compatible_parcelables(const AidlDefinedType& older, const AidlTypenames&,
242                                        const AidlDefinedType& newer,
243                                        const AidlTypenames& new_types) {
244   const auto& old_fields = older.GetFields();
245   const auto& new_fields = newer.GetFields();
246   if (old_fields.size() > new_fields.size()) {
247     // you can add new fields only at the end
248     AIDL_ERROR(newer) << "Number of fields in " << older.GetCanonicalName() << " is reduced from "
249                       << old_fields.size() << " to " << new_fields.size() << ".";
250     return false;
251   }
252   if (newer.IsFixedSize() && old_fields.size() != new_fields.size()) {
253     AIDL_ERROR(newer) << "Number of fields in " << older.GetCanonicalName() << " is changed from "
254                       << old_fields.size() << " to " << new_fields.size()
255                       << ". This is an incompatible change for FixedSize types.";
256     return false;
257   }
258 
259   // android.net.UidRangeParcel should be frozen to prevent breakage in legacy (b/186720556)
260   if (older.GetCanonicalName() == "android.net.UidRangeParcel" &&
261       old_fields.size() != new_fields.size()) {
262     AIDL_ERROR(newer) << "Number of fields in " << older.GetCanonicalName() << " is changed from "
263                       << old_fields.size() << " to " << new_fields.size()
264                       << ". But it is forbidden because of legacy support.";
265     return false;
266   }
267 
268   bool compatible = true;
269   for (size_t i = 0; i < old_fields.size(); i++) {
270     const auto& old_field = old_fields.at(i);
271     const auto& new_field = new_fields.at(i);
272     compatible &= are_compatible_types(old_field->GetType(), new_field->GetType());
273 
274     const string old_value = old_field->ValueString(AidlConstantValueDecorator);
275     const string new_value = new_field->ValueString(AidlConstantValueDecorator);
276     if (old_value == new_value) {
277       continue;
278     }
279     // For enum type fields, we accept setting explicit default value which is "zero"
280     auto enum_decl = new_types.GetEnumDeclaration(new_field->GetType());
281     if (old_value == "" && enum_decl && EvaluatesToZero(*enum_decl, new_field->GetDefaultValue())) {
282       continue;
283     }
284 
285     AIDL_ERROR(new_field) << "Changed default value: " << old_value << " to " << new_value << ".";
286     compatible = false;
287   }
288 
289   // Reordering of fields is an incompatible change.
290   for (size_t i = 0; i < new_fields.size(); i++) {
291     const auto& new_field = new_fields.at(i);
292     auto found = std::find_if(old_fields.begin(), old_fields.end(), [&new_field](const auto& f) {
293       return new_field->GetName() == f->GetName();
294     });
295     if (found != old_fields.end()) {
296       size_t old_index = std::distance(old_fields.begin(), found);
297       if (old_index != i) {
298         AIDL_ERROR(new_field) << "Reordered " << new_field->GetName() << " from " << old_index
299                               << " to " << i << ".";
300         compatible = false;
301       }
302     }
303   }
304 
305   // New fields must have default values.
306   if (older.AsUnionDeclaration() == nullptr) {
307     for (size_t i = old_fields.size(); i < new_fields.size(); i++) {
308       const auto& new_field = new_fields.at(i);
309       if (new_field->HasUsefulDefaultValue()) {
310         continue;
311       }
312 
313       // enum can't be nullable, but it's okay if it has 0 as a valid enumerator.
314       if (const auto& enum_decl = new_types.GetEnumDeclaration(new_field->GetType());
315           enum_decl != nullptr) {
316         if (HasZeroEnumerator(*enum_decl)) {
317           continue;
318         }
319 
320         // TODO(b/142893595): Rephrase the message: "provide a default value or make sure ..."
321         AIDL_ERROR(new_field) << "Field '" << new_field->GetName() << "' of enum '"
322                               << enum_decl->GetName()
323                               << "' can't be initialized as '0'. Please make sure '"
324                               << enum_decl->GetName() << "' has '0' as a valid value.";
325         compatible = false;
326         continue;
327       }
328 
329       // Old API versions may suffer from the issue presented here. There is
330       // only a finite number in Android, which we must allow indefinitely.
331       struct HistoricalException {
332         std::string canonical;
333         std::string field;
334       };
335       static std::vector<HistoricalException> exceptions = {
336           {"android.net.DhcpResultsParcelable", "serverHostName"},
337           {"android.net.ResolverParamsParcel", "resolverOptions"},
338       };
339       bool excepted = false;
340       for (const HistoricalException& exception : exceptions) {
341         if (older.GetCanonicalName() == exception.canonical &&
342             new_field->GetName() == exception.field) {
343           excepted = true;
344           break;
345         }
346       }
347       if (excepted) continue;
348 
349       AIDL_ERROR(new_field)
350           << "Field '" << new_field->GetName()
351           << "' does not have a useful default in some backends. Please either provide a default "
352              "value for this field or mark the field as @nullable. This value or a null value will "
353              "be used automatically when an old version of this parcelable is sent to a process "
354              "which understands a new version of this parcelable. In order to make sure your code "
355              "continues to be backwards compatible, make sure the default or null value does not "
356              "cause a semantic change to this parcelable.";
357       compatible = false;
358     }
359   }
360 
361   compatible = are_compatible_constants(older, newer) && compatible;
362 
363   return compatible;
364 }
365 
are_compatible_enums(const AidlEnumDeclaration & older,const AidlEnumDeclaration & newer)366 static bool are_compatible_enums(const AidlEnumDeclaration& older,
367                                  const AidlEnumDeclaration& newer) {
368   if (!are_compatible_types(older.GetBackingType(), newer.GetBackingType())) {
369     AIDL_ERROR(newer) << "Changed backing types.";
370     return false;
371   }
372 
373   std::map<std::string, const AidlConstantValue*> old_enum_map;
374   for (const auto& enumerator : older.GetEnumerators()) {
375     old_enum_map[enumerator->GetName()] = enumerator->GetValue();
376   }
377   std::map<std::string, const AidlConstantValue*> new_enum_map;
378   for (const auto& enumerator : newer.GetEnumerators()) {
379     new_enum_map[enumerator->GetName()] = enumerator->GetValue();
380   }
381 
382   bool compatible = true;
383   for (const auto& [name, value] : old_enum_map) {
384     if (new_enum_map.find(name) == new_enum_map.end()) {
385       AIDL_ERROR(newer) << "Removed enumerator from " << older.GetCanonicalName() << ": " << name;
386       compatible = false;
387       continue;
388     }
389     const string old_value =
390         old_enum_map[name]->ValueString(older.GetBackingType(), AidlConstantValueDecorator);
391     const string new_value =
392         new_enum_map[name]->ValueString(newer.GetBackingType(), AidlConstantValueDecorator);
393     if (old_value != new_value) {
394       AIDL_ERROR(newer) << "Changed enumerator value: " << older.GetCanonicalName() << "::" << name
395                         << " from " << old_value << " to " << new_value << ".";
396       compatible = false;
397     }
398   }
399   return compatible;
400 }
401 
LoadApiDump(const Options & options,const IoDelegate & io_delegate,const std::string & dir)402 static Result<AidlTypenames> LoadApiDump(const Options& options, const IoDelegate& io_delegate,
403                                          const std::string& dir) {
404   Result<std::vector<std::string>> dir_files = io_delegate.ListFiles(dir);
405   if (!dir_files.ok()) {
406     AIDL_ERROR(dir) << dir_files.error();
407     return Error();
408   }
409 
410   AidlTypenames typenames;
411   for (const auto& file : *dir_files) {
412     if (!android::base::EndsWith(file, ".aidl")) continue;
413     // current "dir" is added to "imports" so that referenced.aidl files in the current
414     // module are available when resolving references.
415     if (internals::load_and_validate_aidl(file, options.PlusImportDir(dir), io_delegate, &typenames,
416                                           nullptr /* imported_files */) != AidlError::OK) {
417       AIDL_ERROR(file) << "Failed to read.";
418       return Error();
419     }
420   }
421 
422   return typenames;
423 }
424 
check_api(const Options & options,const IoDelegate & io_delegate)425 bool check_api(const Options& options, const IoDelegate& io_delegate) {
426   AIDL_FATAL_IF(!options.IsStructured(), AIDL_LOCATION_HERE);
427   AIDL_FATAL_IF(options.InputFiles().size() != 2, AIDL_LOCATION_HERE)
428       << "--checkapi requires two inputs "
429       << "but got " << options.InputFiles().size();
430   auto old_tns = LoadApiDump(options, io_delegate, options.InputFiles().at(0));
431   if (!old_tns.ok()) {
432     return false;
433   }
434   auto new_tns = LoadApiDump(options, io_delegate, options.InputFiles().at(1));
435   if (!new_tns.ok()) {
436     return false;
437   }
438 
439   const Options::CheckApiLevel level = options.GetCheckApiLevel();
440 
441   // We don't check impoted types.
442   auto get_types_in = [](const AidlTypenames& tns, const std::string& location) {
443     std::vector<const AidlDefinedType*> types;
444     for (const auto& type : tns.AllDefinedTypes()) {
445       if (StartsWith(type->GetLocation().GetFile(), location)) {
446         types.push_back(type);
447       }
448     }
449     return types;
450   };
451   std::vector<const AidlDefinedType*> old_types =
452       get_types_in(*old_tns, options.InputFiles().at(0));
453   std::vector<const AidlDefinedType*> new_types =
454       get_types_in(*new_tns, options.InputFiles().at(1));
455 
456   bool compatible = true;
457 
458   if (level == Options::CheckApiLevel::EQUAL) {
459     std::set<string> old_type_names;
460     for (const auto t : old_types) {
461       old_type_names.insert(t->GetCanonicalName());
462     }
463     for (const auto new_type : new_types) {
464       const auto found = old_type_names.find(new_type->GetCanonicalName());
465       if (found == old_type_names.end()) {
466         AIDL_ERROR(new_type) << "Added type: " << new_type->GetCanonicalName();
467         compatible = false;
468         continue;
469       }
470     }
471   }
472 
473   map<string, const AidlDefinedType*> new_map;
474   for (const auto t : new_types) {
475     new_map.emplace(t->GetCanonicalName(), t);
476   }
477 
478   for (const auto old_type : old_types) {
479     const auto found = new_map.find(old_type->GetCanonicalName());
480     if (found == new_map.end()) {
481       AIDL_ERROR(old_type) << "Removed type: " << old_type->GetCanonicalName();
482       compatible = false;
483       continue;
484     }
485     const auto new_type = found->second;
486 
487     if (level == Options::CheckApiLevel::EQUAL) {
488       if (!CheckEquality(*old_type, *new_type)) {
489         compatible = false;
490       }
491       continue;
492     }
493 
494     if (!have_compatible_annotations(*old_type, *new_type)) {
495       compatible = false;
496     }
497     if (old_type->AsInterface() != nullptr) {
498       if (new_type->AsInterface() == nullptr) {
499         AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
500                              << " is changed from " << old_type->GetPreprocessDeclarationName()
501                              << " to " << new_type->GetPreprocessDeclarationName();
502         compatible = false;
503         continue;
504       }
505       compatible &=
506           are_compatible_interfaces(*(old_type->AsInterface()), *(new_type->AsInterface()));
507     } else if (old_type->AsStructuredParcelable() != nullptr) {
508       if (new_type->AsStructuredParcelable() == nullptr) {
509         AIDL_ERROR(new_type) << "Parcelable" << new_type->GetCanonicalName()
510                              << " is not structured. ";
511         compatible = false;
512         continue;
513       }
514       compatible &= are_compatible_parcelables(*(old_type->AsStructuredParcelable()), *old_tns,
515                                                *(new_type->AsStructuredParcelable()), *new_tns);
516     } else if (old_type->AsUnionDeclaration() != nullptr) {
517       if (new_type->AsUnionDeclaration() == nullptr) {
518         AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
519                              << " is changed from " << old_type->GetPreprocessDeclarationName()
520                              << " to " << new_type->GetPreprocessDeclarationName();
521         compatible = false;
522         continue;
523       }
524       compatible &= are_compatible_parcelables(*(old_type->AsUnionDeclaration()), *old_tns,
525                                                *(new_type->AsUnionDeclaration()), *new_tns);
526     } else if (old_type->AsEnumDeclaration() != nullptr) {
527       if (new_type->AsEnumDeclaration() == nullptr) {
528         AIDL_ERROR(new_type) << "Type mismatch: " << old_type->GetCanonicalName()
529                              << " is changed from " << old_type->GetPreprocessDeclarationName()
530                              << " to " << new_type->GetPreprocessDeclarationName();
531         compatible = false;
532         continue;
533       }
534       compatible &=
535           are_compatible_enums(*(old_type->AsEnumDeclaration()), *(new_type->AsEnumDeclaration()));
536     } else {
537       AIDL_ERROR(old_type) << "Unsupported type " << old_type->GetPreprocessDeclarationName()
538                            << " for " << old_type->GetCanonicalName();
539       compatible = false;
540     }
541   }
542 
543   return compatible;
544 }
545 
546 }  // namespace aidl
547 }  // namespace android
548