1 /*
2  * Copyright (C) 2016 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 "Diff.h"
18 
19 #include "Diagnostics.h"
20 #include "LoadedApk.h"
21 #include "ValueVisitor.h"
22 #include "android-base/macros.h"
23 #include "process/IResourceTableConsumer.h"
24 #include "process/SymbolTable.h"
25 
26 using ::android::StringPiece;
27 
28 namespace aapt {
29 
30 class DiffContext : public IAaptContext {
31  public:
DiffContext()32   DiffContext() : name_mangler_({}), symbol_table_(&name_mangler_) {
33   }
34 
GetPackageType()35   PackageType GetPackageType() override {
36     // Doesn't matter.
37     return PackageType::kApp;
38   }
39 
GetCompilationPackage()40   const std::string& GetCompilationPackage() override {
41     return empty_;
42   }
43 
GetPackageId()44   uint8_t GetPackageId() override {
45     return 0x0;
46   }
47 
GetDiagnostics()48   android::IDiagnostics* GetDiagnostics() override {
49     return &diagnostics_;
50   }
51 
GetNameMangler()52   NameMangler* GetNameMangler() override {
53     return &name_mangler_;
54   }
55 
GetExternalSymbols()56   SymbolTable* GetExternalSymbols() override {
57     return &symbol_table_;
58   }
59 
IsVerbose()60   bool IsVerbose() override {
61     return false;
62   }
63 
GetMinSdkVersion()64   int GetMinSdkVersion() override {
65     return 0;
66   }
67 
GetSplitNameDependencies()68   const std::set<std::string>& GetSplitNameDependencies() override {
69     UNIMPLEMENTED(FATAL) << "Split Name Dependencies should not be necessary";
70     static std::set<std::string> empty;
71     return empty;
72   }
73 
74  private:
75   std::string empty_;
76   StdErrDiagnostics diagnostics_;
77   NameMangler name_mangler_;
78   SymbolTable symbol_table_;
79 };
80 
EmitDiffLine(const android::Source & source,StringPiece message)81 static void EmitDiffLine(const android::Source& source, StringPiece message) {
82   std::cerr << source << ": " << message << "\n";
83 }
84 
IsSymbolVisibilityDifferent(const Visibility & vis_a,const Visibility & vis_b)85 static bool IsSymbolVisibilityDifferent(const Visibility& vis_a, const Visibility& vis_b) {
86   return vis_a.level != vis_b.level || vis_a.staged_api != vis_b.staged_api;
87 }
88 
89 template <typename Id>
IsIdDiff(const Visibility::Level & level_a,const std::optional<Id> & id_a,const Visibility::Level & level_b,const std::optional<Id> & id_b)90 static bool IsIdDiff(const Visibility::Level& level_a, const std::optional<Id>& id_a,
91                      const Visibility::Level& level_b, const std::optional<Id>& id_b) {
92   if (level_a == Visibility::Level::kPublic || level_b == Visibility::Level::kPublic) {
93     return id_a != id_b;
94   }
95   return false;
96 }
97 
EmitResourceConfigValueDiff(IAaptContext * context,LoadedApk * apk_a,const ResourceTablePackageView & pkg_a,const ResourceTableTypeView & type_a,const ResourceTableEntryView & entry_a,const ResourceConfigValue * config_value_a,LoadedApk * apk_b,const ResourceTablePackageView & pkg_b,const ResourceTableTypeView & type_b,const ResourceTableEntryView & entry_b,const ResourceConfigValue * config_value_b)98 static bool EmitResourceConfigValueDiff(
99     IAaptContext* context, LoadedApk* apk_a, const ResourceTablePackageView& pkg_a,
100     const ResourceTableTypeView& type_a, const ResourceTableEntryView& entry_a,
101     const ResourceConfigValue* config_value_a, LoadedApk* apk_b,
102     const ResourceTablePackageView& pkg_b, const ResourceTableTypeView& type_b,
103     const ResourceTableEntryView& entry_b, const ResourceConfigValue* config_value_b) {
104   Value* value_a = config_value_a->value.get();
105   Value* value_b = config_value_b->value.get();
106   if (!value_a->Equals(value_b)) {
107     std::stringstream str_stream;
108     str_stream << "value " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
109                << " config=" << config_value_a->config << " does not match:\n";
110     value_a->Print(&str_stream);
111     str_stream << "\n vs \n";
112     value_b->Print(&str_stream);
113     EmitDiffLine(apk_b->GetSource(), str_stream.str());
114     return true;
115   }
116   return false;
117 }
118 
EmitResourceEntryDiff(IAaptContext * context,LoadedApk * apk_a,const ResourceTablePackageView & pkg_a,const ResourceTableTypeView & type_a,const ResourceTableEntryView & entry_a,LoadedApk * apk_b,const ResourceTablePackageView & pkg_b,const ResourceTableTypeView & type_b,const ResourceTableEntryView & entry_b)119 static bool EmitResourceEntryDiff(IAaptContext* context, LoadedApk* apk_a,
120                                   const ResourceTablePackageView& pkg_a,
121                                   const ResourceTableTypeView& type_a,
122                                   const ResourceTableEntryView& entry_a, LoadedApk* apk_b,
123                                   const ResourceTablePackageView& pkg_b,
124                                   const ResourceTableTypeView& type_b,
125                                   const ResourceTableEntryView& entry_b) {
126   bool diff = false;
127   for (const ResourceConfigValue* config_value_a : entry_a.values) {
128     auto config_value_b = entry_b.FindValue(config_value_a->config);
129     if (!config_value_b) {
130       std::stringstream str_stream;
131       str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
132                  << " config=" << config_value_a->config;
133       EmitDiffLine(apk_b->GetSource(), str_stream.str());
134       diff = true;
135     } else {
136       diff |= EmitResourceConfigValueDiff(context, apk_a, pkg_a, type_a, entry_a, config_value_a,
137                                           apk_b, pkg_b, type_b, entry_b, config_value_b);
138     }
139   }
140 
141   // Check for any newly added config values.
142   for (const ResourceConfigValue* config_value_b : entry_b.values) {
143     auto config_value_a = entry_a.FindValue(config_value_b->config);
144     if (!config_value_a) {
145       std::stringstream str_stream;
146       str_stream << "new config " << pkg_b.name << ":" << type_b.named_type << "/" << entry_b.name
147                  << " config=" << config_value_b->config;
148       EmitDiffLine(apk_b->GetSource(), str_stream.str());
149       diff = true;
150     }
151   }
152   return diff;
153 }
154 
EmitResourceTypeDiff(IAaptContext * context,LoadedApk * apk_a,const ResourceTablePackageView & pkg_a,const ResourceTableTypeView & type_a,LoadedApk * apk_b,const ResourceTablePackageView & pkg_b,const ResourceTableTypeView & type_b)155 static bool EmitResourceTypeDiff(IAaptContext* context, LoadedApk* apk_a,
156                                  const ResourceTablePackageView& pkg_a,
157                                  const ResourceTableTypeView& type_a, LoadedApk* apk_b,
158                                  const ResourceTablePackageView& pkg_b,
159                                  const ResourceTableTypeView& type_b) {
160   bool diff = false;
161   auto entry_a_iter = type_a.entries.begin();
162   auto entry_b_iter = type_b.entries.begin();
163   while (entry_a_iter != type_a.entries.end() || entry_b_iter != type_b.entries.end()) {
164     if (entry_b_iter == type_b.entries.end()) {
165       // Type A contains a type that type B does not have.
166       std::stringstream str_stream;
167       str_stream << "missing " << pkg_a.name << ":" << type_a.named_type << "/"
168                  << entry_a_iter->name;
169       EmitDiffLine(apk_a->GetSource(), str_stream.str());
170       diff = true;
171     } else if (entry_a_iter == type_a.entries.end()) {
172       // Type B contains a type that type A does not have.
173       std::stringstream str_stream;
174       str_stream << "new entry " << pkg_b.name << ":" << type_b.named_type << "/"
175                  << entry_b_iter->name;
176       EmitDiffLine(apk_b->GetSource(), str_stream.str());
177       diff = true;
178     } else {
179       const auto& entry_a = *entry_a_iter;
180       const auto& entry_b = *entry_b_iter;
181       if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
182         std::stringstream str_stream;
183         str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
184                    << " has different visibility (";
185         if (entry_b.visibility.staged_api) {
186           str_stream << "STAGED ";
187         }
188         if (entry_b.visibility.level == Visibility::Level::kPublic) {
189           str_stream << "PUBLIC";
190         } else {
191           str_stream << "PRIVATE";
192         }
193         str_stream << " vs ";
194         if (entry_a.visibility.staged_api) {
195           str_stream << "STAGED ";
196         }
197         if (entry_a.visibility.level == Visibility::Level::kPublic) {
198           str_stream << "PUBLIC";
199         } else {
200           str_stream << "PRIVATE";
201         }
202         str_stream << ")";
203         EmitDiffLine(apk_b->GetSource(), str_stream.str());
204         diff = true;
205       } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
206                           entry_b.id)) {
207         std::stringstream str_stream;
208         str_stream << pkg_a.name << ":" << type_a.named_type << "/" << entry_a.name
209                    << " has different public ID (";
210         if (entry_b.id) {
211           str_stream << "0x" << std::hex << entry_b.id.value();
212         } else {
213           str_stream << "none";
214         }
215         str_stream << " vs ";
216         if (entry_a.id) {
217           str_stream << "0x " << std::hex << entry_a.id.value();
218         } else {
219           str_stream << "none";
220         }
221         str_stream << ")";
222         EmitDiffLine(apk_b->GetSource(), str_stream.str());
223         diff = true;
224       }
225       diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a, apk_b, pkg_b, type_b,
226                                     entry_b);
227     }
228     if (entry_a_iter != type_a.entries.end()) {
229       ++entry_a_iter;
230     }
231     if (entry_b_iter != type_b.entries.end()) {
232       ++entry_b_iter;
233     }
234   }
235   return diff;
236 }
237 
EmitResourcePackageDiff(IAaptContext * context,LoadedApk * apk_a,const ResourceTablePackageView & pkg_a,LoadedApk * apk_b,const ResourceTablePackageView & pkg_b)238 static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
239                                     const ResourceTablePackageView& pkg_a, LoadedApk* apk_b,
240                                     const ResourceTablePackageView& pkg_b) {
241   bool diff = false;
242   auto type_a_iter = pkg_a.types.begin();
243   auto type_b_iter = pkg_b.types.begin();
244   while (type_a_iter != pkg_a.types.end() || type_b_iter != pkg_b.types.end()) {
245     if (type_b_iter == pkg_b.types.end()) {
246       // Type A contains a type that type B does not have.
247       std::stringstream str_stream;
248       str_stream << "missing " << pkg_a.name << ":" << type_a_iter->named_type;
249       EmitDiffLine(apk_a->GetSource(), str_stream.str());
250       diff = true;
251     } else if (type_a_iter == pkg_a.types.end()) {
252       // Type B contains a type that type A does not have.
253       std::stringstream str_stream;
254       str_stream << "new type " << pkg_b.name << ":" << type_b_iter->named_type;
255       EmitDiffLine(apk_b->GetSource(), str_stream.str());
256       diff = true;
257     } else {
258       const auto& type_a = *type_a_iter;
259       const auto& type_b = *type_b_iter;
260       if (type_a.visibility_level != type_b.visibility_level) {
261         std::stringstream str_stream;
262         str_stream << pkg_a.name << ":" << type_a.named_type << " has different visibility (";
263         if (type_b.visibility_level == Visibility::Level::kPublic) {
264           str_stream << "PUBLIC";
265         } else {
266           str_stream << "PRIVATE";
267         }
268         str_stream << " vs ";
269         if (type_a.visibility_level == Visibility::Level::kPublic) {
270           str_stream << "PUBLIC";
271         } else {
272           str_stream << "PRIVATE";
273         }
274         str_stream << ")";
275         EmitDiffLine(apk_b->GetSource(), str_stream.str());
276         diff = true;
277       } else if (IsIdDiff(type_a.visibility_level, type_a.id, type_b.visibility_level, type_b.id)) {
278         std::stringstream str_stream;
279         str_stream << pkg_a.name << ":" << type_a.named_type << " has different public ID (";
280         if (type_b.id) {
281           str_stream << "0x" << std::hex << type_b.id.value();
282         } else {
283           str_stream << "none";
284         }
285         str_stream << " vs ";
286         if (type_a.id) {
287           str_stream << "0x " << std::hex << type_a.id.value();
288         } else {
289           str_stream << "none";
290         }
291         str_stream << ")";
292         EmitDiffLine(apk_b->GetSource(), str_stream.str());
293         diff = true;
294       }
295       diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a, apk_b, pkg_b, type_b);
296     }
297     if (type_a_iter != pkg_a.types.end()) {
298       ++type_a_iter;
299     }
300     if (type_b_iter != pkg_b.types.end()) {
301       ++type_b_iter;
302     }
303   }
304   return diff;
305 }
306 
EmitResourceTableDiff(IAaptContext * context,LoadedApk * apk_a,LoadedApk * apk_b)307 static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
308   const auto table_a = apk_a->GetResourceTable()->GetPartitionedView();
309   const auto table_b = apk_b->GetResourceTable()->GetPartitionedView();
310 
311   bool diff = false;
312   auto package_a_iter = table_a.packages.begin();
313   auto package_b_iter = table_b.packages.begin();
314   while (package_a_iter != table_a.packages.end() || package_b_iter != table_b.packages.end()) {
315     if (package_b_iter == table_b.packages.end()) {
316       // Table A contains a package that table B does not have.
317       std::stringstream str_stream;
318       str_stream << "missing package " << package_a_iter->name;
319       EmitDiffLine(apk_a->GetSource(), str_stream.str());
320       diff = true;
321     } else if (package_a_iter == table_a.packages.end()) {
322       // Table B contains a package that table A does not have.
323       std::stringstream str_stream;
324       str_stream << "new package " << package_b_iter->name;
325       EmitDiffLine(apk_b->GetSource(), str_stream.str());
326       diff = true;
327     } else {
328       const auto& package_a = *package_a_iter;
329       const auto& package_b = *package_b_iter;
330       if (package_a.id != package_b.id) {
331         std::stringstream str_stream;
332         str_stream << "package '" << package_a.name << "' has different id (";
333         if (package_b.id) {
334           str_stream << "0x" << std::hex << package_b.id.value();
335         } else {
336           str_stream << "none";
337         }
338         str_stream << " vs ";
339         if (package_a.id) {
340           str_stream << "0x" << std::hex << package_b.id.value();
341         } else {
342           str_stream << "none";
343         }
344         str_stream << ")";
345         EmitDiffLine(apk_b->GetSource(), str_stream.str());
346         diff = true;
347       }
348       diff |= EmitResourcePackageDiff(context, apk_a, package_a, apk_b, package_b);
349     }
350     if (package_a_iter != table_a.packages.end()) {
351       ++package_a_iter;
352     }
353     if (package_b_iter != table_b.packages.end()) {
354       ++package_b_iter;
355     }
356   }
357 
358   return diff;
359 }
360 
361 class ZeroingReferenceVisitor : public DescendingValueVisitor {
362  public:
363   using DescendingValueVisitor::Visit;
364 
Visit(Reference * ref)365   void Visit(Reference* ref) override {
366     if (ref->name && ref->id) {
367       if (ref->id.value().package_id() == kAppPackageId) {
368         ref->id = {};
369       }
370     }
371   }
372 };
373 
ZeroOutAppReferences(ResourceTable * table)374 static void ZeroOutAppReferences(ResourceTable* table) {
375   ZeroingReferenceVisitor visitor;
376   VisitAllValuesInTable(table, &visitor);
377 }
378 
Action(const std::vector<std::string> & args)379 int DiffCommand::Action(const std::vector<std::string>& args) {
380   DiffContext context;
381 
382   if (args.size() != 2u) {
383     std::cerr << "must have two apks as arguments.\n\n";
384     Usage(&std::cerr);
385     return 1;
386   }
387 
388   android::IDiagnostics* diag = context.GetDiagnostics();
389   std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
390   std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
391   if (!apk_a || !apk_b) {
392     return 1;
393   }
394 
395   // Zero out Application IDs in references.
396   ZeroOutAppReferences(apk_a->GetResourceTable());
397   ZeroOutAppReferences(apk_b->GetResourceTable());
398 
399   if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
400     // We emitted a diff, so return 1 (failure).
401     return 1;
402   }
403   return 0;
404 }
405 
406 }  // namespace aapt
407