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