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