1/*
2 * Copyright (C) 2015 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 "ResourceUtils.h"
18#include "link/ManifestFixer.h"
19#include "util/Util.h"
20#include "xml/XmlActionExecutor.h"
21#include "xml/XmlDom.h"
22
23namespace aapt {
24
25/**
26 * This is how PackageManager builds class names from AndroidManifest.xml entries.
27 */
28static bool nameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
29                                SourcePathDiagnostics* diag) {
30    std::u16string className = attr->value;
31    if (className.find(u'.') == std::u16string::npos) {
32        // There is no '.', so add one to the beginning.
33        className = u".";
34        className += attr->value;
35    }
36
37    // We allow unqualified class names (ie: .HelloActivity)
38    // Since we don't know the package name, we can just make a fake one here and
39    // the test will be identical as long as the real package name is valid too.
40    Maybe<std::u16string> fullyQualifiedClassName =
41            util::getFullyQualifiedClassName(u"a", className);
42
43    StringPiece16 qualifiedClassName = fullyQualifiedClassName
44            ? fullyQualifiedClassName.value() : className;
45    if (!util::isJavaClassName(qualifiedClassName)) {
46        diag->error(DiagMessage(el->lineNumber)
47                    << "attribute 'android:name' in <"
48                    << el->name << "> tag must be a valid Java class name");
49        return false;
50    }
51    return true;
52}
53
54static bool optionalNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
55    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
56        return nameIsJavaClassName(el, attr, diag);
57    }
58    return true;
59}
60
61static bool requiredNameIsJavaClassName(xml::Element* el, SourcePathDiagnostics* diag) {
62    if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"name")) {
63        return nameIsJavaClassName(el, attr, diag);
64    }
65    diag->error(DiagMessage(el->lineNumber)
66                << "<" << el->name << "> is missing attribute 'android:name'");
67    return false;
68}
69
70static bool verifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
71    xml::Attribute* attr = el->findAttribute({}, u"package");
72    if (!attr) {
73        diag->error(DiagMessage(el->lineNumber) << "<manifest> tag is missing 'package' attribute");
74        return false;
75    } else if (ResourceUtils::isReference(attr->value)) {
76        diag->error(DiagMessage(el->lineNumber)
77                    << "attribute 'package' in <manifest> tag must not be a reference");
78        return false;
79    } else if (!util::isJavaPackageName(attr->value)) {
80        diag->error(DiagMessage(el->lineNumber)
81                    << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
82                    << attr->value << "'");
83        return false;
84    }
85    return true;
86}
87
88bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* diag) {
89    // First verify some options.
90    if (mOptions.renameManifestPackage) {
91        if (!util::isJavaPackageName(mOptions.renameManifestPackage.value())) {
92            diag->error(DiagMessage() << "invalid manifest package override '"
93                        << mOptions.renameManifestPackage.value() << "'");
94            return false;
95        }
96    }
97
98    if (mOptions.renameInstrumentationTargetPackage) {
99        if (!util::isJavaPackageName(mOptions.renameInstrumentationTargetPackage.value())) {
100            diag->error(DiagMessage() << "invalid instrumentation target package override '"
101                        << mOptions.renameInstrumentationTargetPackage.value() << "'");
102            return false;
103        }
104    }
105
106    // Common intent-filter actions.
107    xml::XmlNodeAction intentFilterAction;
108    intentFilterAction[u"action"];
109    intentFilterAction[u"category"];
110    intentFilterAction[u"data"];
111
112    // Common meta-data actions.
113    xml::XmlNodeAction metaDataAction;
114
115    // Manifest actions.
116    xml::XmlNodeAction& manifestAction = (*executor)[u"manifest"];
117    manifestAction.action(verifyManifest);
118    manifestAction.action([&](xml::Element* el) -> bool {
119        if (mOptions.versionNameDefault) {
120            if (el->findAttribute(xml::kSchemaAndroid, u"versionName") == nullptr) {
121                el->attributes.push_back(xml::Attribute{
122                        xml::kSchemaAndroid,
123                        u"versionName",
124                        mOptions.versionNameDefault.value() });
125            }
126        }
127
128        if (mOptions.versionCodeDefault) {
129            if (el->findAttribute(xml::kSchemaAndroid, u"versionCode") == nullptr) {
130                el->attributes.push_back(xml::Attribute{
131                        xml::kSchemaAndroid,
132                        u"versionCode",
133                        mOptions.versionCodeDefault.value() });
134            }
135        }
136        return true;
137    });
138
139    // Meta tags.
140    manifestAction[u"eat-comment"];
141
142    // Uses-sdk actions.
143    manifestAction[u"uses-sdk"].action([&](xml::Element* el) -> bool {
144        if (mOptions.minSdkVersionDefault &&
145                el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
146            // There was no minSdkVersion defined and we have a default to assign.
147            el->attributes.push_back(xml::Attribute{
148                    xml::kSchemaAndroid, u"minSdkVersion",
149                    mOptions.minSdkVersionDefault.value() });
150        }
151
152        if (mOptions.targetSdkVersionDefault &&
153                el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
154            // There was no targetSdkVersion defined and we have a default to assign.
155            el->attributes.push_back(xml::Attribute{
156                    xml::kSchemaAndroid, u"targetSdkVersion",
157                    mOptions.targetSdkVersionDefault.value() });
158        }
159        return true;
160    });
161
162    // Instrumentation actions.
163    manifestAction[u"instrumentation"].action([&](xml::Element* el) -> bool {
164        if (!mOptions.renameInstrumentationTargetPackage) {
165            return true;
166        }
167
168        if (xml::Attribute* attr = el->findAttribute(xml::kSchemaAndroid, u"targetPackage")) {
169            attr->value = mOptions.renameInstrumentationTargetPackage.value();
170        }
171        return true;
172    });
173
174    manifestAction[u"original-package"];
175    manifestAction[u"protected-broadcast"];
176    manifestAction[u"uses-permission"];
177    manifestAction[u"permission"];
178    manifestAction[u"permission-tree"];
179    manifestAction[u"permission-group"];
180
181    manifestAction[u"uses-configuration"];
182    manifestAction[u"uses-feature"];
183    manifestAction[u"uses-library"];
184    manifestAction[u"supports-screens"];
185    manifestAction[u"compatible-screens"];
186    manifestAction[u"supports-gl-texture"];
187
188    // Application actions.
189    xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"];
190    applicationAction.action(optionalNameIsJavaClassName);
191
192    // Activity actions.
193    applicationAction[u"activity"].action(requiredNameIsJavaClassName);
194    applicationAction[u"activity"][u"intent-filter"] = intentFilterAction;
195    applicationAction[u"activity"][u"meta-data"] = metaDataAction;
196
197    // Activity alias actions.
198    applicationAction[u"activity-alias"][u"intent-filter"] = intentFilterAction;
199    applicationAction[u"activity-alias"][u"meta-data"] = metaDataAction;
200
201    // Service actions.
202    applicationAction[u"service"].action(requiredNameIsJavaClassName);
203    applicationAction[u"service"][u"intent-filter"] = intentFilterAction;
204    applicationAction[u"service"][u"meta-data"] = metaDataAction;
205
206    // Receiver actions.
207    applicationAction[u"receiver"].action(requiredNameIsJavaClassName);
208    applicationAction[u"receiver"][u"intent-filter"] = intentFilterAction;
209    applicationAction[u"receiver"][u"meta-data"] = metaDataAction;
210
211    // Provider actions.
212    applicationAction[u"provider"].action(requiredNameIsJavaClassName);
213    applicationAction[u"provider"][u"grant-uri-permissions"];
214    applicationAction[u"provider"][u"meta-data"] = metaDataAction;
215    applicationAction[u"provider"][u"path-permissions"];
216    return true;
217}
218
219class FullyQualifiedClassNameVisitor : public xml::Visitor {
220public:
221    using xml::Visitor::visit;
222
223    FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
224    }
225
226    void visit(xml::Element* el) override {
227        for (xml::Attribute& attr : el->attributes) {
228            if (Maybe<std::u16string> newValue =
229                    util::getFullyQualifiedClassName(mPackage, attr.value)) {
230                attr.value = std::move(newValue.value());
231            }
232        }
233
234        // Super implementation to iterate over the children.
235        xml::Visitor::visit(el);
236    }
237
238private:
239    StringPiece16 mPackage;
240};
241
242static bool renameManifestPackage(const StringPiece16& packageOverride, xml::Element* manifestEl) {
243    xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
244
245    // We've already verified that the manifest element is present, with a package name specified.
246    assert(attr);
247
248    std::u16string originalPackage = std::move(attr->value);
249    attr->value = packageOverride.toString();
250
251    FullyQualifiedClassNameVisitor visitor(originalPackage);
252    manifestEl->accept(&visitor);
253    return true;
254}
255
256bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
257    xml::Element* root = xml::findRootElement(doc->root.get());
258    if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
259        context->getDiagnostics()->error(DiagMessage(doc->file.source)
260                                         << "root tag must be <manifest>");
261        return false;
262    }
263
264    if ((mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)
265            && root->findChild({}, u"uses-sdk") == nullptr) {
266        // Auto insert a <uses-sdk> element.
267        std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
268        usesSdk->name = u"uses-sdk";
269        root->addChild(std::move(usesSdk));
270    }
271
272    xml::XmlActionExecutor executor;
273    if (!buildRules(&executor, context->getDiagnostics())) {
274        return false;
275    }
276
277    if (!executor.execute(xml::XmlActionExecutorPolicy::Whitelist, context->getDiagnostics(),
278                          doc)) {
279        return false;
280    }
281
282    if (mOptions.renameManifestPackage) {
283        // Rename manifest package outside of the XmlActionExecutor.
284        // We need to extract the old package name and FullyQualify all class names.
285        if (!renameManifestPackage(mOptions.renameManifestPackage.value(), root)) {
286            return false;
287        }
288    }
289    return true;
290}
291
292} // namespace aapt
293