ManifestFixer.cpp revision c10c0d0db24f2d3fbdeabf7aebaabf527786bba5
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 "link/ManifestFixer.h" 18 19#include <unordered_set> 20 21#include "android-base/logging.h" 22 23#include "ResourceUtils.h" 24#include "util/Util.h" 25#include "xml/XmlActionExecutor.h" 26#include "xml/XmlDom.h" 27 28using android::StringPiece; 29 30namespace aapt { 31 32// This is how PackageManager builds class names from AndroidManifest.xml entries. 33static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr, 34 SourcePathDiagnostics* diag) { 35 // We allow unqualified class names (ie: .HelloActivity) 36 // Since we don't know the package name, we can just make a fake one here and 37 // the test will be identical as long as the real package name is valid too. 38 Maybe<std::string> fully_qualified_class_name = 39 util::GetFullyQualifiedClassName("a", attr->value); 40 41 StringPiece qualified_class_name = fully_qualified_class_name 42 ? fully_qualified_class_name.value() 43 : attr->value; 44 45 if (!util::IsJavaClassName(qualified_class_name)) { 46 diag->Error(DiagMessage(el->line_number) 47 << "attribute 'android:name' in <" << el->name 48 << "> 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, "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, "name")) { 63 return NameIsJavaClassName(el, attr, diag); 64 } 65 diag->Error(DiagMessage(el->line_number) 66 << "<" << el->name << "> is missing attribute 'android:name'"); 67 return false; 68} 69 70static bool RequiredNameIsJavaPackage(xml::Element* el, SourcePathDiagnostics* diag) { 71 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { 72 return util::IsJavaPackageName(attr->value); 73 } 74 diag->Error(DiagMessage(el->line_number) 75 << "<" << el->name << "> is missing attribute 'android:name'"); 76 return false; 77} 78 79static xml::XmlNodeAction::ActionFuncWithDiag RequiredAndroidAttribute(const std::string& attr) { 80 return [=](xml::Element* el, SourcePathDiagnostics* diag) -> bool { 81 if (el->FindAttribute(xml::kSchemaAndroid, attr) == nullptr) { 82 diag->Error(DiagMessage(el->line_number) 83 << "<" << el->name << "> is missing required attribute 'android:" << attr << "'"); 84 return false; 85 } 86 return true; 87 }; 88} 89 90static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) { 91 constexpr const char* kFeatureSplit = "featureSplit"; 92 constexpr const char* kIsFeatureSplit = "isFeatureSplit"; 93 94 xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit); 95 if (attr != nullptr) { 96 // Rewrite the featureSplit attribute to be "split". This is what the 97 // platform recognizes. 98 attr->name = "split"; 99 100 // Now inject the android:isFeatureSplit="true" attribute. 101 xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit); 102 if (attr != nullptr) { 103 if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) { 104 // The isFeatureSplit attribute is false, which conflicts with the use 105 // of "featureSplit". 106 diag->Error(DiagMessage(el->line_number) 107 << "attribute 'featureSplit' used in <manifest> but 'android:isFeatureSplit' " 108 "is not 'true'"); 109 return false; 110 } 111 112 // The attribute is already there and set to true, nothing to do. 113 } else { 114 el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"}); 115 } 116 } 117 return true; 118} 119 120static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) { 121 xml::Attribute* attr = el->FindAttribute({}, "package"); 122 if (!attr) { 123 diag->Error(DiagMessage(el->line_number) 124 << "<manifest> tag is missing 'package' attribute"); 125 return false; 126 } else if (ResourceUtils::IsReference(attr->value)) { 127 diag->Error(DiagMessage(el->line_number) 128 << "attribute 'package' in <manifest> tag must not be a reference"); 129 return false; 130 } else if (!util::IsJavaPackageName(attr->value)) { 131 diag->Error(DiagMessage(el->line_number) 132 << "attribute 'package' in <manifest> tag is not a valid Java package name: '" 133 << attr->value << "'"); 134 return false; 135 } 136 137 attr = el->FindAttribute({}, "split"); 138 if (attr) { 139 if (!util::IsJavaPackageName(attr->value)) { 140 diag->Error(DiagMessage(el->line_number) << "attribute 'split' in <manifest> tag is not a " 141 "valid split name"); 142 return false; 143 } 144 } 145 return true; 146} 147 148// The coreApp attribute in <manifest> is not a regular AAPT attribute, so type 149// checking on it is manual. 150static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) { 151 if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) { 152 std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseBool(attr->value); 153 if (!result) { 154 diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean"); 155 return false; 156 } 157 attr->compiled_value = std::move(result); 158 } 159 return true; 160} 161 162// Checks that <uses-feature> has android:glEsVersion or android:name, not both (or neither). 163static bool VerifyUsesFeature(xml::Element* el, SourcePathDiagnostics* diag) { 164 bool has_name = false; 165 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "name")) { 166 if (attr->value.empty()) { 167 diag->Error(DiagMessage(el->line_number) 168 << "android:name in <uses-feature> must not be empty"); 169 return false; 170 } 171 has_name = true; 172 } 173 174 bool has_gl_es_version = false; 175 if (xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, "glEsVersion")) { 176 if (has_name) { 177 diag->Error(DiagMessage(el->line_number) 178 << "cannot define both android:name and android:glEsVersion in <uses-feature>"); 179 return false; 180 } 181 has_gl_es_version = true; 182 } 183 184 if (!has_name && !has_gl_es_version) { 185 diag->Error(DiagMessage(el->line_number) 186 << "<uses-feature> must have either android:name or android:glEsVersion attribute"); 187 return false; 188 } 189 return true; 190} 191 192bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor, 193 IDiagnostics* diag) { 194 // First verify some options. 195 if (options_.rename_manifest_package) { 196 if (!util::IsJavaPackageName(options_.rename_manifest_package.value())) { 197 diag->Error(DiagMessage() << "invalid manifest package override '" 198 << options_.rename_manifest_package.value() 199 << "'"); 200 return false; 201 } 202 } 203 204 if (options_.rename_instrumentation_target_package) { 205 if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) { 206 diag->Error(DiagMessage() 207 << "invalid instrumentation target package override '" 208 << options_.rename_instrumentation_target_package.value() 209 << "'"); 210 return false; 211 } 212 } 213 214 // Common <intent-filter> actions. 215 xml::XmlNodeAction intent_filter_action; 216 intent_filter_action["action"]; 217 intent_filter_action["category"]; 218 intent_filter_action["data"]; 219 220 // Common <meta-data> actions. 221 xml::XmlNodeAction meta_data_action; 222 223 // Common <uses-feature> actions. 224 xml::XmlNodeAction uses_feature_action; 225 uses_feature_action.Action(VerifyUsesFeature); 226 227 // Common component actions. 228 xml::XmlNodeAction component_action; 229 component_action.Action(RequiredNameIsJavaClassName); 230 component_action["intent-filter"] = intent_filter_action; 231 component_action["meta-data"] = meta_data_action; 232 233 // Manifest actions. 234 xml::XmlNodeAction& manifest_action = (*executor)["manifest"]; 235 manifest_action.Action(AutoGenerateIsFeatureSplit); 236 manifest_action.Action(VerifyManifest); 237 manifest_action.Action(FixCoreAppAttribute); 238 manifest_action.Action([&](xml::Element* el) -> bool { 239 if (options_.version_name_default) { 240 if (el->FindAttribute(xml::kSchemaAndroid, "versionName") == nullptr) { 241 el->attributes.push_back( 242 xml::Attribute{xml::kSchemaAndroid, "versionName", 243 options_.version_name_default.value()}); 244 } 245 } 246 247 if (options_.version_code_default) { 248 if (el->FindAttribute(xml::kSchemaAndroid, "versionCode") == nullptr) { 249 el->attributes.push_back( 250 xml::Attribute{xml::kSchemaAndroid, "versionCode", 251 options_.version_code_default.value()}); 252 } 253 } 254 return true; 255 }); 256 257 // Meta tags. 258 manifest_action["eat-comment"]; 259 260 // Uses-sdk actions. 261 manifest_action["uses-sdk"].Action([&](xml::Element* el) -> bool { 262 if (options_.min_sdk_version_default && 263 el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion") == nullptr) { 264 // There was no minSdkVersion defined and we have a default to assign. 265 el->attributes.push_back( 266 xml::Attribute{xml::kSchemaAndroid, "minSdkVersion", 267 options_.min_sdk_version_default.value()}); 268 } 269 270 if (options_.target_sdk_version_default && 271 el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion") == nullptr) { 272 // There was no targetSdkVersion defined and we have a default to assign. 273 el->attributes.push_back( 274 xml::Attribute{xml::kSchemaAndroid, "targetSdkVersion", 275 options_.target_sdk_version_default.value()}); 276 } 277 return true; 278 }); 279 280 // Instrumentation actions. 281 manifest_action["instrumentation"].Action(RequiredNameIsJavaClassName); 282 manifest_action["instrumentation"].Action([&](xml::Element* el) -> bool { 283 if (!options_.rename_instrumentation_target_package) { 284 return true; 285 } 286 287 if (xml::Attribute* attr = 288 el->FindAttribute(xml::kSchemaAndroid, "targetPackage")) { 289 attr->value = options_.rename_instrumentation_target_package.value(); 290 } 291 return true; 292 }); 293 manifest_action["instrumentation"]["meta-data"] = meta_data_action; 294 295 manifest_action["original-package"]; 296 manifest_action["protected-broadcast"]; 297 manifest_action["uses-permission"]; 298 manifest_action["permission"]; 299 manifest_action["permission-tree"]; 300 manifest_action["permission-group"]; 301 manifest_action["uses-configuration"]; 302 manifest_action["supports-screens"]; 303 manifest_action["uses-feature"] = uses_feature_action; 304 manifest_action["feature-group"]["uses-feature"] = uses_feature_action; 305 manifest_action["compatible-screens"]; 306 manifest_action["compatible-screens"]["screen"]; 307 manifest_action["supports-gl-texture"]; 308 manifest_action["meta-data"] = meta_data_action; 309 manifest_action["uses-split"].Action(RequiredNameIsJavaPackage); 310 311 // Application actions. 312 xml::XmlNodeAction& application_action = manifest_action["application"]; 313 application_action.Action(OptionalNameIsJavaClassName); 314 315 application_action["uses-library"].Action(RequiredNameIsJavaPackage); 316 application_action["library"].Action(RequiredNameIsJavaPackage); 317 318 xml::XmlNodeAction& static_library_action = application_action["static-library"]; 319 static_library_action.Action(RequiredNameIsJavaPackage); 320 static_library_action.Action(RequiredAndroidAttribute("version")); 321 322 xml::XmlNodeAction& uses_static_library_action = application_action["uses-static-library"]; 323 uses_static_library_action.Action(RequiredNameIsJavaPackage); 324 uses_static_library_action.Action(RequiredAndroidAttribute("version")); 325 uses_static_library_action.Action(RequiredAndroidAttribute("certDigest")); 326 327 application_action["meta-data"] = meta_data_action; 328 application_action["activity"] = component_action; 329 application_action["activity-alias"] = component_action; 330 application_action["service"] = component_action; 331 application_action["receiver"] = component_action; 332 333 // Provider actions. 334 application_action["provider"] = component_action; 335 application_action["provider"]["grant-uri-permission"]; 336 application_action["provider"]["path-permission"]; 337 338 return true; 339} 340 341class FullyQualifiedClassNameVisitor : public xml::Visitor { 342 public: 343 using xml::Visitor::Visit; 344 345 explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {} 346 347 void Visit(xml::Element* el) override { 348 for (xml::Attribute& attr : el->attributes) { 349 if (attr.namespace_uri == xml::kSchemaAndroid && 350 class_attributes_.find(attr.name) != class_attributes_.end()) { 351 if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) { 352 attr.value = std::move(new_value.value()); 353 } 354 } 355 } 356 357 // Super implementation to iterate over the children. 358 xml::Visitor::Visit(el); 359 } 360 361 private: 362 StringPiece package_; 363 std::unordered_set<StringPiece> class_attributes_ = {"name"}; 364}; 365 366static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) { 367 xml::Attribute* attr = manifest_el->FindAttribute({}, "package"); 368 369 // We've already verified that the manifest element is present, with a package 370 // name specified. 371 CHECK(attr != nullptr); 372 373 std::string original_package = std::move(attr->value); 374 attr->value = package_override.to_string(); 375 376 FullyQualifiedClassNameVisitor visitor(original_package); 377 manifest_el->Accept(&visitor); 378 return true; 379} 380 381bool ManifestFixer::Consume(IAaptContext* context, xml::XmlResource* doc) { 382 xml::Element* root = xml::FindRootElement(doc->root.get()); 383 if (!root || !root->namespace_uri.empty() || root->name != "manifest") { 384 context->GetDiagnostics()->Error(DiagMessage(doc->file.source) 385 << "root tag must be <manifest>"); 386 return false; 387 } 388 389 if ((options_.min_sdk_version_default || options_.target_sdk_version_default) && 390 root->FindChild({}, "uses-sdk") == nullptr) { 391 // Auto insert a <uses-sdk> element. This must be inserted before the 392 // <application> tag. The device runtime PackageParser will make SDK version 393 // decisions while parsing <application>. 394 std::unique_ptr<xml::Element> uses_sdk = util::make_unique<xml::Element>(); 395 uses_sdk->name = "uses-sdk"; 396 root->InsertChild(0, std::move(uses_sdk)); 397 } 398 399 xml::XmlActionExecutor executor; 400 if (!BuildRules(&executor, context->GetDiagnostics())) { 401 return false; 402 } 403 404 if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) { 405 return false; 406 } 407 408 if (options_.rename_manifest_package) { 409 // Rename manifest package outside of the XmlActionExecutor. 410 // We need to extract the old package name and FullyQualify all class 411 // names. 412 if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) { 413 return false; 414 } 415 } 416 return true; 417} 418 419} // namespace aapt 420