blob: 71d3424c6ad85b90088cc8a372cf65c6aeaf57da [file] [log] [blame]
Adam Lesinski8c831ca2015-05-20 15:24:01 -07001#include "ManifestMerger.h"
2#include "Maybe.h"
3#include "ResourceParser.h"
4#include "Source.h"
5#include "Util.h"
6#include "XmlPullParser.h"
7
8#include <iostream>
9#include <memory>
10#include <set>
11#include <string>
12
13namespace aapt {
14
15constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
16
17static xml::Element* findManifest(xml::Node* root) {
18 if (!root) {
19 return nullptr;
20 }
21
22 while (root->type == xml::NodeType::kNamespace) {
23 if (root->children.empty()) {
24 break;
25 }
26 root = root->children[0].get();
27 }
28
29 if (root && root->type == xml::NodeType::kElement) {
30 xml::Element* el = static_cast<xml::Element*>(root);
31 if (el->namespaceUri.empty() && el->name == u"manifest") {
32 return el;
33 }
34 }
35 return nullptr;
36}
37
38static xml::Element* findChildWithSameName(xml::Element* parent, xml::Element* src) {
39 xml::Attribute* attrKey = src->findAttribute(kSchemaAndroid, u"name");
40 if (!attrKey) {
41 return nullptr;
42 }
43 return parent->findChildWithAttribute(src->namespaceUri, src->name, attrKey);
44}
45
46static bool attrLess(const xml::Attribute& lhs, const xml::Attribute& rhs) {
47 return std::tie(lhs.namespaceUri, lhs.name, lhs.value)
48 < std::tie(rhs.namespaceUri, rhs.name, rhs.value);
49}
50
51static int compare(xml::Element* lhs, xml::Element* rhs) {
52 int diff = lhs->attributes.size() - rhs->attributes.size();
53 if (diff != 0) {
54 return diff;
55 }
56
57 std::set<xml::Attribute, decltype(&attrLess)> lhsAttrs(&attrLess);
58 lhsAttrs.insert(lhs->attributes.begin(), lhs->attributes.end());
59 for (auto& attr : rhs->attributes) {
60 if (lhsAttrs.erase(attr) == 0) {
61 // The rhs attribute is not in the left.
62 return -1;
63 }
64 }
65
66 if (!lhsAttrs.empty()) {
67 // The lhs has attributes not in the rhs.
68 return 1;
69 }
70 return 0;
71}
72
73ManifestMerger::ManifestMerger(const Options& options) :
74 mOptions(options), mAppLogger({}), mLogger({}) {
75}
76
77bool ManifestMerger::setAppManifest(const Source& source, const std::u16string& package,
78 std::unique_ptr<xml::Node> root) {
79
80 mAppLogger = SourceLogger{ source };
81 mRoot = std::move(root);
82 return true;
83}
84
85bool ManifestMerger::checkEqual(xml::Element* elA, xml::Element* elB) {
86 if (compare(elA, elB) != 0) {
87 mLogger.error(elB->lineNumber)
88 << "library tag '" << elB->name << "' conflicts with app tag."
89 << std::endl;
90 mAppLogger.note(elA->lineNumber)
91 << "app tag '" << elA->name << "' defined here."
92 << std::endl;
93 return false;
94 }
95
96 std::vector<xml::Element*> childrenA = elA->getChildElements();
97 std::vector<xml::Element*> childrenB = elB->getChildElements();
98
99 if (childrenA.size() != childrenB.size()) {
100 mLogger.error(elB->lineNumber)
101 << "library tag '" << elB->name << "' children conflict with app tag."
102 << std::endl;
103 mAppLogger.note(elA->lineNumber)
104 << "app tag '" << elA->name << "' defined here."
105 << std::endl;
106 return false;
107 }
108
109 auto cmp = [](xml::Element* lhs, xml::Element* rhs) -> bool {
110 return compare(lhs, rhs) < 0;
111 };
112
113 std::sort(childrenA.begin(), childrenA.end(), cmp);
114 std::sort(childrenB.begin(), childrenB.end(), cmp);
115
116 for (size_t i = 0; i < childrenA.size(); i++) {
117 if (!checkEqual(childrenA[i], childrenB[i])) {
118 return false;
119 }
120 }
121 return true;
122}
123
124bool ManifestMerger::mergeNewOrEqual(xml::Element* parentA, xml::Element* elA, xml::Element* elB) {
125 if (!elA) {
126 parentA->addChild(elB->clone());
127 return true;
128 }
129 return checkEqual(elA, elB);
130}
131
132bool ManifestMerger::mergePreferRequired(xml::Element* parentA, xml::Element* elA,
133 xml::Element* elB) {
134 if (!elA) {
135 parentA->addChild(elB->clone());
136 return true;
137 }
138
139 xml::Attribute* reqA = elA->findAttribute(kSchemaAndroid, u"required");
140 xml::Attribute* reqB = elB->findAttribute(kSchemaAndroid, u"required");
141 bool requiredA = !reqA || (reqA->value != u"false" && reqA->value != u"FALSE");
142 bool requiredB = !reqB || (reqB->value != u"false" && reqB->value != u"FALSE");
143 if (!requiredA && requiredB) {
144 if (reqA) {
145 *reqA = xml::Attribute{ kSchemaAndroid, u"required", u"true" };
146 } else {
147 elA->attributes.push_back(xml::Attribute{ kSchemaAndroid, u"required", u"true" });
148 }
149 }
150 return true;
151}
152
153static int findIntegerValue(xml::Attribute* attr, int defaultValue) {
154 if (attr) {
155 std::unique_ptr<BinaryPrimitive> integer = ResourceParser::tryParseInt(attr->value);
156 if (integer) {
157 return integer->value.data;
158 }
159 }
160 return defaultValue;
161}
162
163bool ManifestMerger::mergeUsesSdk(xml::Element* elA, xml::Element* elB) {
164 bool error = false;
165 xml::Attribute* minAttrA = nullptr;
166 xml::Attribute* minAttrB = nullptr;
167 if (elA) {
168 minAttrA = elA->findAttribute(kSchemaAndroid, u"minSdkVersion");
169 }
170
171 if (elB) {
172 minAttrB = elB->findAttribute(kSchemaAndroid, u"minSdkVersion");
173 }
174
175 int minSdkA = findIntegerValue(minAttrA, 1);
176 int minSdkB = findIntegerValue(minAttrB, 1);
177
178 if (minSdkA < minSdkB) {
179 std::ostream* out;
180 if (minAttrA) {
181 out = &(mAppLogger.error(elA->lineNumber) << "app declares ");
182 } else if (elA) {
183 out = &(mAppLogger.error(elA->lineNumber) << "app has implied ");
184 } else {
185 out = &(mAppLogger.error() << "app has implied ");
186 }
187
188 *out << "minSdkVersion=" << minSdkA << " but library expects a higher SDK version."
189 << std::endl;
190
191 // elB is valid because minSdkB wouldn't be greater than minSdkA if it wasn't.
192 mLogger.note(elB->lineNumber)
193 << "library declares minSdkVersion=" << minSdkB << "."
194 << std::endl;
195 error = true;
196 }
197
198 xml::Attribute* targetAttrA = nullptr;
199 xml::Attribute* targetAttrB = nullptr;
200
201 if (elA) {
202 targetAttrA = elA->findAttribute(kSchemaAndroid, u"targetSdkVersion");
203 }
204
205 if (elB) {
206 targetAttrB = elB->findAttribute(kSchemaAndroid, u"targetSdkVersion");
207 }
208
209 int targetSdkA = findIntegerValue(targetAttrA, minSdkA);
210 int targetSdkB = findIntegerValue(targetAttrB, minSdkB);
211
212 if (targetSdkA < targetSdkB) {
213 std::ostream* out;
214 if (targetAttrA) {
215 out = &(mAppLogger.warn(elA->lineNumber) << "app declares ");
216 } else if (elA) {
217 out = &(mAppLogger.warn(elA->lineNumber) << "app has implied ");
218 } else {
219 out = &(mAppLogger.warn() << "app has implied ");
220 }
221
222 *out << "targetSdkVerion=" << targetSdkA << " but library expects target SDK "
223 << targetSdkB << "." << std::endl;
224
225 mLogger.note(elB->lineNumber)
226 << "library declares targetSdkVersion=" << targetSdkB << "."
227 << std::endl;
228 error = true;
229 }
230 return !error;
231}
232
233bool ManifestMerger::mergeApplication(xml::Element* applicationA, xml::Element* applicationB) {
234 if (!applicationA || !applicationB) {
235 return true;
236 }
237
238 bool error = false;
239
240 // First make sure that the names are identical.
241 xml::Attribute* nameA = applicationA->findAttribute(kSchemaAndroid, u"name");
242 xml::Attribute* nameB = applicationB->findAttribute(kSchemaAndroid, u"name");
243 if (nameB) {
244 if (!nameA) {
245 applicationA->attributes.push_back(*nameB);
246 } else if (nameA->value != nameB->value) {
247 mLogger.error(applicationB->lineNumber)
248 << "conflicting application name '"
249 << nameB->value
250 << "'." << std::endl;
251 mAppLogger.note(applicationA->lineNumber)
252 << "application defines application name '"
253 << nameA->value
254 << "'." << std::endl;
255 error = true;
256 }
257 }
258
259 // Now we descend into the activity/receiver/service/provider tags
260 for (xml::Element* elB : applicationB->getChildElements()) {
261 if (!elB->namespaceUri.empty()) {
262 continue;
263 }
264
265 if (elB->name == u"activity" || elB->name == u"activity-alias"
266 || elB->name == u"service" || elB->name == u"receiver"
267 || elB->name == u"provider" || elB->name == u"meta-data") {
268 xml::Element* elA = findChildWithSameName(applicationA, elB);
269 error |= !mergeNewOrEqual(applicationA, elA, elB);
270 } else if (elB->name == u"uses-library") {
271 xml::Element* elA = findChildWithSameName(applicationA, elB);
272 error |= !mergePreferRequired(applicationA, elA, elB);
273 }
274 }
275 return !error;
276}
277
278bool ManifestMerger::mergeLibraryManifest(const Source& source, const std::u16string& package,
279 std::unique_ptr<xml::Node> libRoot) {
280 mLogger = SourceLogger{ source };
281 xml::Element* manifestA = findManifest(mRoot.get());
282 xml::Element* manifestB = findManifest(libRoot.get());
283 if (!manifestA) {
284 mAppLogger.error() << "missing manifest tag." << std::endl;
285 return false;
286 }
287
288 if (!manifestB) {
289 mLogger.error() << "library missing manifest tag." << std::endl;
290 return false;
291 }
292
293 bool error = false;
294
295 // Do <application> first.
296 xml::Element* applicationA = manifestA->findChild({}, u"application");
297 xml::Element* applicationB = manifestB->findChild({}, u"application");
298 error |= !mergeApplication(applicationA, applicationB);
299
300 // Do <uses-sdk> next.
301 xml::Element* usesSdkA = manifestA->findChild({}, u"uses-sdk");
302 xml::Element* usesSdkB = manifestB->findChild({}, u"uses-sdk");
303 error |= !mergeUsesSdk(usesSdkA, usesSdkB);
304
305 for (xml::Element* elB : manifestB->getChildElements()) {
306 if (!elB->namespaceUri.empty()) {
307 continue;
308 }
309
310 if (elB->name == u"uses-permission" || elB->name == u"permission"
311 || elB->name == u"permission-group" || elB->name == u"permission-tree") {
312 xml::Element* elA = findChildWithSameName(manifestA, elB);
313 error |= !mergeNewOrEqual(manifestA, elA, elB);
314 } else if (elB->name == u"uses-feature") {
315 xml::Element* elA = findChildWithSameName(manifestA, elB);
316 error |= !mergePreferRequired(manifestA, elA, elB);
317 } else if (elB->name == u"uses-configuration" || elB->name == u"supports-screen"
318 || elB->name == u"compatible-screens" || elB->name == u"supports-gl-texture") {
319 xml::Element* elA = findChildWithSameName(manifestA, elB);
320 error |= !checkEqual(elA, elB);
321 }
322 }
323 return !error;
324}
325
326static void printMerged(xml::Node* node, int depth) {
327 std::string indent;
328 for (int i = 0; i < depth; i++) {
329 indent += " ";
330 }
331
332 switch (node->type) {
333 case xml::NodeType::kNamespace:
334 std::cerr << indent << "N: "
335 << "xmlns:" << static_cast<xml::Namespace*>(node)->namespacePrefix
336 << "=\"" << static_cast<xml::Namespace*>(node)->namespaceUri
337 << "\"\n";
338 break;
339
340 case xml::NodeType::kElement:
341 std::cerr << indent << "E: "
342 << static_cast<xml::Element*>(node)->namespaceUri
343 << ":" << static_cast<xml::Element*>(node)->name
344 << "\n";
345 for (const auto& attr : static_cast<xml::Element*>(node)->attributes) {
346 std::cerr << indent << " A: "
347 << attr.namespaceUri
348 << ":" << attr.name
349 << "=\"" << attr.value << "\"\n";
350 }
351 break;
352
353 case xml::NodeType::kText:
354 std::cerr << indent << "T: \"" << static_cast<xml::Text*>(node)->text << "\"\n";
355 break;
356 }
357
358 for (auto& child : node->children) {
359 printMerged(child.get(), depth + 1);
360 }
361}
362
363xml::Node* ManifestMerger::getMergedXml() {
364 return mRoot.get();
365}
366
367bool ManifestMerger::printMerged() {
368 if (!mRoot) {
369 return false;
370 }
371
372 ::aapt::printMerged(mRoot.get(), 0);
373 return true;
374}
375
376} // namespace aapt