1 #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 
13 namespace aapt {
14 
15 constexpr const char16_t* kSchemaAndroid = u"http://schemas.android.com/apk/res/android";
16 
findManifest(xml::Node * root)17 static 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 
findChildWithSameName(xml::Element * parent,xml::Element * src)38 static 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 
attrLess(const xml::Attribute & lhs,const xml::Attribute & rhs)46 static 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 
compare(xml::Element * lhs,xml::Element * rhs)51 static 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 
ManifestMerger(const Options & options)73 ManifestMerger::ManifestMerger(const Options& options) :
74         mOptions(options), mAppLogger({}), mLogger({}) {
75 }
76 
setAppManifest(const Source & source,const std::u16string & package,std::unique_ptr<xml::Node> root)77 bool 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 
checkEqual(xml::Element * elA,xml::Element * elB)85 bool 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 
mergeNewOrEqual(xml::Element * parentA,xml::Element * elA,xml::Element * elB)124 bool 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 
mergePreferRequired(xml::Element * parentA,xml::Element * elA,xml::Element * elB)132 bool 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 
findIntegerValue(xml::Attribute * attr,int defaultValue)153 static 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 
mergeUsesSdk(xml::Element * elA,xml::Element * elB)163 bool 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 
mergeApplication(xml::Element * applicationA,xml::Element * applicationB)233 bool 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 
mergeLibraryManifest(const Source & source,const std::u16string & package,std::unique_ptr<xml::Node> libRoot)278 bool 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 
printMerged(xml::Node * node,int depth)326 static 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 
getMergedXml()363 xml::Node* ManifestMerger::getMergedXml() {
364     return mRoot.get();
365 }
366 
printMerged()367 bool ManifestMerger::printMerged() {
368     if (!mRoot) {
369         return false;
370     }
371 
372     ::aapt::printMerged(mRoot.get(), 0);
373     return true;
374 }
375 
376 } // namespace aapt
377