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