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