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 
23 namespace aapt {
24 
25 /**
26  * This is how PackageManager builds class names from AndroidManifest.xml entries.
27  */
nameIsJavaClassName(xml::Element * el,xml::Attribute * attr,SourcePathDiagnostics * diag)28 static 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 
optionalNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * diag)54 static 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 
requiredNameIsJavaClassName(xml::Element * el,SourcePathDiagnostics * diag)61 static 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 
verifyManifest(xml::Element * el,SourcePathDiagnostics * diag)70 static 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 
buildRules(xml::XmlActionExecutor * executor,IDiagnostics * diag)88 bool 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 
219 class FullyQualifiedClassNameVisitor : public xml::Visitor {
220 public:
221     using xml::Visitor::visit;
222 
FullyQualifiedClassNameVisitor(const StringPiece16 & package)223     FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
224     }
225 
visit(xml::Element * el)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 
238 private:
239     StringPiece16 mPackage;
240 };
241 
renameManifestPackage(const StringPiece16 & packageOverride,xml::Element * manifestEl)242 static 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 
consume(IAaptContext * context,xml::XmlResource * doc)256 bool 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