blob: 9baf1d86795c17d4091e1ffa26309d1826e81f78 [file] [log] [blame]
Adam Lesinski2ae4a872015-11-02 16:10:55 -08001/*
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"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080018#include "link/ManifestFixer.h"
19#include "util/Util.h"
Adam Lesinski467f1712015-11-16 17:35:44 -080020#include "xml/XmlDom.h"
Adam Lesinski2ae4a872015-11-02 16:10:55 -080021
22namespace aapt {
23
24static bool verifyManifest(IAaptContext* context, const Source& source, xml::Element* manifestEl) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -080025 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
26 if (!attr) {
27 context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
28 << "missing 'package' attribute");
Adam Lesinski2ae4a872015-11-02 16:10:55 -080029 } else if (ResourceUtils::isReference(attr->value)) {
30 context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
31 << "value for attribute 'package' must not be a "
32 "reference");
Adam Lesinski2ae4a872015-11-02 16:10:55 -080033 } else if (!util::isJavaPackageName(attr->value)) {
34 context->getDiagnostics()->error(DiagMessage(source.withLine(manifestEl->lineNumber))
35 << "invalid package name '" << attr->value << "'");
Adam Lesinski52364f72016-01-11 13:10:24 -080036 } else {
37 return true;
38 }
39 return false;
40}
41
42static bool includeVersionName(IAaptContext* context, const Source& source,
43 const StringPiece16& versionName, xml::Element* manifestEl) {
44 if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionName")) {
45 return true;
Adam Lesinski2ae4a872015-11-02 16:10:55 -080046 }
47
Adam Lesinski52364f72016-01-11 13:10:24 -080048 manifestEl->attributes.push_back(xml::Attribute{
49 xml::kSchemaAndroid, u"versionName", versionName.toString() });
50 return true;
51}
52
53static bool includeVersionCode(IAaptContext* context, const Source& source,
54 const StringPiece16& versionCode, xml::Element* manifestEl) {
55 if (manifestEl->findAttribute(xml::kSchemaAndroid, u"versionCode")) {
56 return true;
57 }
58
59 manifestEl->attributes.push_back(xml::Attribute{
60 xml::kSchemaAndroid, u"versionCode", versionCode.toString() });
61 return true;
Adam Lesinski2ae4a872015-11-02 16:10:55 -080062}
63
64static bool fixUsesSdk(IAaptContext* context, const Source& source, xml::Element* el,
65 const ManifestFixerOptions& options) {
66 if (options.minSdkVersionDefault &&
67 el->findAttribute(xml::kSchemaAndroid, u"minSdkVersion") == nullptr) {
68 // There was no minSdkVersion defined and we have a default to assign.
69 el->attributes.push_back(xml::Attribute{
70 xml::kSchemaAndroid, u"minSdkVersion", options.minSdkVersionDefault.value() });
71 }
72
73 if (options.targetSdkVersionDefault &&
74 el->findAttribute(xml::kSchemaAndroid, u"targetSdkVersion") == nullptr) {
75 // There was no targetSdkVersion defined and we have a default to assign.
76 el->attributes.push_back(xml::Attribute{
77 xml::kSchemaAndroid, u"targetSdkVersion",
78 options.targetSdkVersionDefault.value() });
79 }
80 return true;
81}
82
Adam Lesinski52364f72016-01-11 13:10:24 -080083class FullyQualifiedClassNameVisitor : public xml::Visitor {
84public:
85 using xml::Visitor::visit;
86
87 FullyQualifiedClassNameVisitor(const StringPiece16& package) : mPackage(package) {
88 }
89
90 void visit(xml::Element* el) override {
91 for (xml::Attribute& attr : el->attributes) {
92 if (Maybe<std::u16string> newValue =
93 util::getFullyQualifiedClassName(mPackage, attr.value)) {
94 attr.value = std::move(newValue.value());
95 }
96 }
97
98 // Super implementation to iterate over the children.
99 xml::Visitor::visit(el);
100 }
101
102private:
103 StringPiece16 mPackage;
104};
105
106static bool renameManifestPackage(IAaptContext* context, const Source& source,
107 const StringPiece16& packageOverride, xml::Element* manifestEl) {
108 if (!util::isJavaPackageName(packageOverride)) {
109 context->getDiagnostics()->error(DiagMessage() << "invalid manifest package override '"
110 << packageOverride << "'");
111 return false;
112 }
113
114 xml::Attribute* attr = manifestEl->findAttribute({}, u"package");
115
116 // We've already verified that the manifest element is present, with a package name specified.
117 assert(attr);
118
119 std::u16string originalPackage = std::move(attr->value);
120 attr->value = packageOverride.toString();
121
122 FullyQualifiedClassNameVisitor visitor(originalPackage);
123 manifestEl->accept(&visitor);
124 return true;
125}
126
127static bool renameInstrumentationTargetPackage(IAaptContext* context, const Source& source,
128 const StringPiece16& packageOverride,
129 xml::Element* manifestEl) {
130 if (!util::isJavaPackageName(packageOverride)) {
131 context->getDiagnostics()->error(DiagMessage()
132 << "invalid instrumentation target package override '"
133 << packageOverride << "'");
134 return false;
135 }
136
137 xml::Element* instrumentationEl = manifestEl->findChild({}, u"instrumentation");
138 if (!instrumentationEl) {
139 // No error if there is no work to be done.
140 return true;
141 }
142
143 xml::Attribute* attr = instrumentationEl->findAttribute(xml::kSchemaAndroid, u"targetPackage");
144 if (!attr) {
145 // No error if there is no work to be done.
146 return true;
147 }
148
149 attr->value = packageOverride.toString();
150 return true;
151}
152
Adam Lesinski467f1712015-11-16 17:35:44 -0800153bool ManifestFixer::consume(IAaptContext* context, xml::XmlResource* doc) {
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800154 xml::Element* root = xml::findRootElement(doc->root.get());
155 if (!root || !root->namespaceUri.empty() || root->name != u"manifest") {
156 context->getDiagnostics()->error(DiagMessage(doc->file.source)
157 << "root tag must be <manifest>");
158 return false;
159 }
160
161 if (!verifyManifest(context, doc->file.source, root)) {
162 return false;
163 }
164
Adam Lesinski52364f72016-01-11 13:10:24 -0800165 if (mOptions.versionCodeDefault) {
166 if (!includeVersionCode(context, doc->file.source, mOptions.versionCodeDefault.value(),
167 root)) {
168 return false;
169 }
170 }
171
172 if (mOptions.versionNameDefault) {
173 if (!includeVersionName(context, doc->file.source, mOptions.versionNameDefault.value(),
174 root)) {
175 return false;
176 }
177 }
178
179 if (mOptions.renameManifestPackage) {
180 // Rename manifest package.
181 if (!renameManifestPackage(context, doc->file.source,
182 mOptions.renameManifestPackage.value(), root)) {
183 return false;
184 }
185 }
186
187 if (mOptions.renameInstrumentationTargetPackage) {
188 if (!renameInstrumentationTargetPackage(context, doc->file.source,
189 mOptions.renameInstrumentationTargetPackage.value(),
190 root)) {
191 return false;
192 }
193 }
194
Adam Lesinski2ae4a872015-11-02 16:10:55 -0800195 bool foundUsesSdk = false;
196 for (xml::Element* el : root->getChildElements()) {
197 if (!el->namespaceUri.empty()) {
198 continue;
199 }
200
201 if (el->name == u"uses-sdk") {
202 foundUsesSdk = true;
203 fixUsesSdk(context, doc->file.source, el, mOptions);
204 }
205 }
206
207 if (!foundUsesSdk && (mOptions.minSdkVersionDefault || mOptions.targetSdkVersionDefault)) {
208 std::unique_ptr<xml::Element> usesSdk = util::make_unique<xml::Element>();
209 usesSdk->name = u"uses-sdk";
210 fixUsesSdk(context, doc->file.source, usesSdk.get(), mOptions);
211 root->addChild(std::move(usesSdk));
212 }
213
214 return true;
215}
216
217} // namespace aapt