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
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
377