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