• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "android-base/macros.h"
20 
21 #include "LoadedApk.h"
22 #include "ValueVisitor.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   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 Source & source,const StringPiece & message)81 static void EmitDiffLine(const Source& source, const 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.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.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.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.type << "/" << entry_a_iter->name;
168       EmitDiffLine(apk_a->GetSource(), str_stream.str());
169       diff = true;
170     } else if (entry_a_iter == type_a.entries.end()) {
171       // Type B contains a type that type A does not have.
172       std::stringstream str_stream;
173       str_stream << "new entry " << pkg_b.name << ":" << type_b.type << "/" << entry_b_iter->name;
174       EmitDiffLine(apk_b->GetSource(), str_stream.str());
175       diff = true;
176     } else {
177       const auto& entry_a = *entry_a_iter;
178       const auto& entry_b = *entry_b_iter;
179       if (IsSymbolVisibilityDifferent(entry_a.visibility, entry_b.visibility)) {
180         std::stringstream str_stream;
181         str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
182                    << " has different visibility (";
183         if (entry_b.visibility.staged_api) {
184           str_stream << "STAGED ";
185         }
186         if (entry_b.visibility.level == Visibility::Level::kPublic) {
187           str_stream << "PUBLIC";
188         } else {
189           str_stream << "PRIVATE";
190         }
191         str_stream << " vs ";
192         if (entry_a.visibility.staged_api) {
193           str_stream << "STAGED ";
194         }
195         if (entry_a.visibility.level == Visibility::Level::kPublic) {
196           str_stream << "PUBLIC";
197         } else {
198           str_stream << "PRIVATE";
199         }
200         str_stream << ")";
201         EmitDiffLine(apk_b->GetSource(), str_stream.str());
202         diff = true;
203       } else if (IsIdDiff(entry_a.visibility.level, entry_a.id, entry_b.visibility.level,
204                           entry_b.id)) {
205         std::stringstream str_stream;
206         str_stream << pkg_a.name << ":" << type_a.type << "/" << entry_a.name
207                    << " has different public ID (";
208         if (entry_b.id) {
209           str_stream << "0x" << std::hex << entry_b.id.value();
210         } else {
211           str_stream << "none";
212         }
213         str_stream << " vs ";
214         if (entry_a.id) {
215           str_stream << "0x " << std::hex << entry_a.id.value();
216         } else {
217           str_stream << "none";
218         }
219         str_stream << ")";
220         EmitDiffLine(apk_b->GetSource(), str_stream.str());
221         diff = true;
222       }
223       diff |= EmitResourceEntryDiff(context, apk_a, pkg_a, type_a, entry_a, apk_b, pkg_b, type_b,
224                                     entry_b);
225     }
226     if (entry_a_iter != type_a.entries.end()) {
227       ++entry_a_iter;
228     }
229     if (entry_b_iter != type_b.entries.end()) {
230       ++entry_b_iter;
231     }
232   }
233   return diff;
234 }
235 
EmitResourcePackageDiff(IAaptContext * context,LoadedApk * apk_a,const ResourceTablePackageView & pkg_a,LoadedApk * apk_b,const ResourceTablePackageView & pkg_b)236 static bool EmitResourcePackageDiff(IAaptContext* context, LoadedApk* apk_a,
237                                     const ResourceTablePackageView& pkg_a, LoadedApk* apk_b,
238                                     const ResourceTablePackageView& pkg_b) {
239   bool diff = false;
240   auto type_a_iter = pkg_a.types.begin();
241   auto type_b_iter = pkg_b.types.begin();
242   while (type_a_iter != pkg_a.types.end() || type_b_iter != pkg_b.types.end()) {
243     if (type_b_iter == pkg_b.types.end()) {
244       // Type A contains a type that type B does not have.
245       std::stringstream str_stream;
246       str_stream << "missing " << pkg_a.name << ":" << type_a_iter->type;
247       EmitDiffLine(apk_a->GetSource(), str_stream.str());
248       diff = true;
249     } else if (type_a_iter == pkg_a.types.end()) {
250       // Type B contains a type that type A does not have.
251       std::stringstream str_stream;
252       str_stream << "new type " << pkg_b.name << ":" << type_b_iter->type;
253       EmitDiffLine(apk_b->GetSource(), str_stream.str());
254       diff = true;
255     } else {
256       const auto& type_a = *type_a_iter;
257       const auto& type_b = *type_b_iter;
258       if (type_a.visibility_level != type_b.visibility_level) {
259         std::stringstream str_stream;
260         str_stream << pkg_a.name << ":" << type_a.type << " has different visibility (";
261         if (type_b.visibility_level == Visibility::Level::kPublic) {
262           str_stream << "PUBLIC";
263         } else {
264           str_stream << "PRIVATE";
265         }
266         str_stream << " vs ";
267         if (type_a.visibility_level == Visibility::Level::kPublic) {
268           str_stream << "PUBLIC";
269         } else {
270           str_stream << "PRIVATE";
271         }
272         str_stream << ")";
273         EmitDiffLine(apk_b->GetSource(), str_stream.str());
274         diff = true;
275       } else if (IsIdDiff(type_a.visibility_level, type_a.id, type_b.visibility_level, type_b.id)) {
276         std::stringstream str_stream;
277         str_stream << pkg_a.name << ":" << type_a.type << " has different public ID (";
278         if (type_b.id) {
279           str_stream << "0x" << std::hex << type_b.id.value();
280         } else {
281           str_stream << "none";
282         }
283         str_stream << " vs ";
284         if (type_a.id) {
285           str_stream << "0x " << std::hex << type_a.id.value();
286         } else {
287           str_stream << "none";
288         }
289         str_stream << ")";
290         EmitDiffLine(apk_b->GetSource(), str_stream.str());
291         diff = true;
292       }
293       diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a, apk_b, pkg_b, type_b);
294     }
295     if (type_a_iter != pkg_a.types.end()) {
296       ++type_a_iter;
297     }
298     if (type_b_iter != pkg_b.types.end()) {
299       ++type_b_iter;
300     }
301   }
302   return diff;
303 }
304 
EmitResourceTableDiff(IAaptContext * context,LoadedApk * apk_a,LoadedApk * apk_b)305 static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
306   const auto table_a = apk_a->GetResourceTable()->GetPartitionedView();
307   const auto table_b = apk_b->GetResourceTable()->GetPartitionedView();
308 
309   bool diff = false;
310   auto package_a_iter = table_a.packages.begin();
311   auto package_b_iter = table_b.packages.begin();
312   while (package_a_iter != table_a.packages.end() || package_b_iter != table_b.packages.end()) {
313     if (package_b_iter == table_b.packages.end()) {
314       // Table A contains a package that table B does not have.
315       std::stringstream str_stream;
316       str_stream << "missing package " << package_a_iter->name;
317       EmitDiffLine(apk_a->GetSource(), str_stream.str());
318       diff = true;
319     } else if (package_a_iter == table_a.packages.end()) {
320       // Table B contains a package that table A does not have.
321       std::stringstream str_stream;
322       str_stream << "new package " << package_b_iter->name;
323       EmitDiffLine(apk_b->GetSource(), str_stream.str());
324       diff = true;
325     } else {
326       const auto& package_a = *package_a_iter;
327       const auto& package_b = *package_b_iter;
328       if (package_a.id != package_b.id) {
329         std::stringstream str_stream;
330         str_stream << "package '" << package_a.name << "' has different id (";
331         if (package_b.id) {
332           str_stream << "0x" << std::hex << package_b.id.value();
333         } else {
334           str_stream << "none";
335         }
336         str_stream << " vs ";
337         if (package_a.id) {
338           str_stream << "0x" << std::hex << package_b.id.value();
339         } else {
340           str_stream << "none";
341         }
342         str_stream << ")";
343         EmitDiffLine(apk_b->GetSource(), str_stream.str());
344         diff = true;
345       }
346       diff |= EmitResourcePackageDiff(context, apk_a, package_a, apk_b, package_b);
347     }
348     if (package_a_iter != table_a.packages.end()) {
349       ++package_a_iter;
350     }
351     if (package_b_iter != table_b.packages.end()) {
352       ++package_b_iter;
353     }
354   }
355 
356   return diff;
357 }
358 
359 class ZeroingReferenceVisitor : public DescendingValueVisitor {
360  public:
361   using DescendingValueVisitor::Visit;
362 
Visit(Reference * ref)363   void Visit(Reference* ref) override {
364     if (ref->name && ref->id) {
365       if (ref->id.value().package_id() == kAppPackageId) {
366         ref->id = {};
367       }
368     }
369   }
370 };
371 
ZeroOutAppReferences(ResourceTable * table)372 static void ZeroOutAppReferences(ResourceTable* table) {
373   ZeroingReferenceVisitor visitor;
374   VisitAllValuesInTable(table, &visitor);
375 }
376 
Action(const std::vector<std::string> & args)377 int DiffCommand::Action(const std::vector<std::string>& args) {
378   DiffContext context;
379 
380   if (args.size() != 2u) {
381     std::cerr << "must have two apks as arguments.\n\n";
382     Usage(&std::cerr);
383     return 1;
384   }
385 
386   IDiagnostics* diag = context.GetDiagnostics();
387   std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(args[0], diag);
388   std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(args[1], diag);
389   if (!apk_a || !apk_b) {
390     return 1;
391   }
392 
393   // Zero out Application IDs in references.
394   ZeroOutAppReferences(apk_a->GetResourceTable());
395   ZeroOutAppReferences(apk_b->GetResourceTable());
396 
397   if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
398     // We emitted a diff, so return 1 (failure).
399     return 1;
400   }
401   return 0;
402 }
403 
404 }  // namespace aapt
405