• 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 Symbol & symbol_a,const Symbol & symbol_b)78 static bool IsSymbolVisibilityDifferent(const Symbol& symbol_a, const Symbol& symbol_b) {
79   return symbol_a.state != symbol_b.state;
80 }
81 
82 template <typename Id>
IsIdDiff(const Symbol & symbol_a,const Maybe<Id> & id_a,const Symbol & symbol_b,const Maybe<Id> & id_b)83 static bool IsIdDiff(const Symbol& symbol_a, const Maybe<Id>& id_a, const Symbol& symbol_b,
84                      const Maybe<Id>& id_b) {
85   if (symbol_a.state == SymbolState::kPublic || symbol_b.state == SymbolState::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->symbol_status, entry_b->symbol_status)) {
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->symbol_status.state == SymbolState::kPublic) {
165           str_stream << "PUBLIC";
166         } else {
167           str_stream << "PRIVATE";
168         }
169         str_stream << " vs ";
170         if (entry_a->symbol_status.state == SymbolState::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->symbol_status, entry_a->id, entry_b->symbol_status,
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 (IsSymbolVisibilityDifferent(type_a->symbol_status, type_b->symbol_status)) {
229         std::stringstream str_stream;
230         str_stream << pkg_a->name << ":" << type_a->type << " has different visibility (";
231         if (type_b->symbol_status.state == SymbolState::kPublic) {
232           str_stream << "PUBLIC";
233         } else {
234           str_stream << "PRIVATE";
235         }
236         str_stream << " vs ";
237         if (type_a->symbol_status.state == SymbolState::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->symbol_status, type_a->id, type_b->symbol_status, type_b->id)) {
246         std::stringstream str_stream;
247         str_stream << pkg_a->name << ":" << type_a->type << " has different public ID (";
248         if (type_b->id) {
249           str_stream << "0x" << std::hex << type_b->id.value();
250         } else {
251           str_stream << "none";
252         }
253         str_stream << " vs ";
254         if (type_a->id) {
255           str_stream << "0x " << std::hex << type_a->id.value();
256         } else {
257           str_stream << "none";
258         }
259         str_stream << ")";
260         EmitDiffLine(apk_b->GetSource(), str_stream.str());
261         diff = true;
262       }
263       diff |= EmitResourceTypeDiff(context, apk_a, pkg_a, type_a.get(), apk_b, pkg_b, type_b);
264     }
265   }
266 
267   // Check for any newly added types.
268   for (std::unique_ptr<ResourceTableType>& type_b : pkg_b->types) {
269     ResourceTableType* type_a = pkg_a->FindType(type_b->type);
270     if (!type_a) {
271       std::stringstream str_stream;
272       str_stream << "new type " << pkg_b->name << ":" << type_b->type;
273       EmitDiffLine(apk_b->GetSource(), str_stream.str());
274       diff = true;
275     }
276   }
277   return diff;
278 }
279 
EmitResourceTableDiff(IAaptContext * context,LoadedApk * apk_a,LoadedApk * apk_b)280 static bool EmitResourceTableDiff(IAaptContext* context, LoadedApk* apk_a, LoadedApk* apk_b) {
281   ResourceTable* table_a = apk_a->GetResourceTable();
282   ResourceTable* table_b = apk_b->GetResourceTable();
283 
284   bool diff = false;
285   for (std::unique_ptr<ResourceTablePackage>& pkg_a : table_a->packages) {
286     ResourceTablePackage* pkg_b = table_b->FindPackage(pkg_a->name);
287     if (!pkg_b) {
288       std::stringstream str_stream;
289       str_stream << "missing package " << pkg_a->name;
290       EmitDiffLine(apk_b->GetSource(), str_stream.str());
291       diff = true;
292     } else {
293       if (pkg_a->id != pkg_b->id) {
294         std::stringstream str_stream;
295         str_stream << "package '" << pkg_a->name << "' has different id (";
296         if (pkg_b->id) {
297           str_stream << "0x" << std::hex << pkg_b->id.value();
298         } else {
299           str_stream << "none";
300         }
301         str_stream << " vs ";
302         if (pkg_a->id) {
303           str_stream << "0x" << std::hex << pkg_a->id.value();
304         } else {
305           str_stream << "none";
306         }
307         str_stream << ")";
308         EmitDiffLine(apk_b->GetSource(), str_stream.str());
309         diff = true;
310       }
311       diff |= EmitResourcePackageDiff(context, apk_a, pkg_a.get(), apk_b, pkg_b);
312     }
313   }
314 
315   // Check for any newly added packages.
316   for (std::unique_ptr<ResourceTablePackage>& pkg_b : table_b->packages) {
317     ResourceTablePackage* pkg_a = table_a->FindPackage(pkg_b->name);
318     if (!pkg_a) {
319       std::stringstream str_stream;
320       str_stream << "new package " << pkg_b->name;
321       EmitDiffLine(apk_b->GetSource(), str_stream.str());
322       diff = true;
323     }
324   }
325   return diff;
326 }
327 
328 class ZeroingReferenceVisitor : public ValueVisitor {
329  public:
330   using ValueVisitor::Visit;
331 
Visit(Reference * ref)332   void Visit(Reference* ref) override {
333     if (ref->name && ref->id) {
334       if (ref->id.value().package_id() == kAppPackageId) {
335         ref->id = {};
336       }
337     }
338   }
339 };
340 
ZeroOutAppReferences(ResourceTable * table)341 static void ZeroOutAppReferences(ResourceTable* table) {
342   ZeroingReferenceVisitor visitor;
343   VisitAllValuesInTable(table, &visitor);
344 }
345 
Diff(const std::vector<StringPiece> & args)346 int Diff(const std::vector<StringPiece>& args) {
347   DiffContext context;
348 
349   Flags flags;
350   if (!flags.Parse("aapt2 diff", args, &std::cerr)) {
351     return 1;
352   }
353 
354   if (flags.GetArgs().size() != 2u) {
355     std::cerr << "must have two apks as arguments.\n\n";
356     flags.Usage("aapt2 diff", &std::cerr);
357     return 1;
358   }
359 
360   std::unique_ptr<LoadedApk> apk_a = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[0]);
361   std::unique_ptr<LoadedApk> apk_b = LoadedApk::LoadApkFromPath(&context, flags.GetArgs()[1]);
362   if (!apk_a || !apk_b) {
363     return 1;
364   }
365 
366   // Zero out Application IDs in references.
367   ZeroOutAppReferences(apk_a->GetResourceTable());
368   ZeroOutAppReferences(apk_b->GetResourceTable());
369 
370   if (EmitResourceTableDiff(&context, apk_a.get(), apk_b.get())) {
371     // We emitted a diff, so return 1 (failure).
372     return 1;
373   }
374   return 0;
375 }
376 
377 }  // namespace aapt
378