diff options
-rw-r--r-- | cmds/idmap2/Android.bp | 4 | ||||
-rw-r--r-- | cmds/idmap2/idmap2/Lookup.cpp | 39 | ||||
-rw-r--r-- | cmds/idmap2/idmap2/Scan.cpp | 2 | ||||
-rw-r--r-- | cmds/idmap2/include/idmap2/Xml.h | 49 | ||||
-rw-r--r-- | cmds/idmap2/include/idmap2/XmlParser.h | 147 | ||||
-rw-r--r-- | cmds/idmap2/libidmap2/ResourceUtils.cpp | 61 | ||||
-rw-r--r-- | cmds/idmap2/libidmap2/Xml.cpp | 80 | ||||
-rw-r--r-- | cmds/idmap2/libidmap2/XmlParser.cpp | 163 | ||||
-rw-r--r-- | cmds/idmap2/tests/XmlParserTests.cpp | 174 | ||||
-rw-r--r-- | cmds/idmap2/tests/XmlTests.cpp | 68 | ||||
-rw-r--r-- | cmds/idmap2/tests/data/target/res/xml/test.xml | 19 | ||||
-rw-r--r-- | cmds/idmap2/tests/data/target/target-no-overlayable.apk | bin | 2749 -> 2263 bytes | |||
-rw-r--r-- | cmds/idmap2/tests/data/target/target.apk | bin | 5017 -> 5017 bytes |
13 files changed, 263 insertions, 543 deletions
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp index 60d64149510c..4c77ba402595 100644 --- a/cmds/idmap2/Android.bp +++ b/cmds/idmap2/Android.bp @@ -45,7 +45,7 @@ cc_library { "libidmap2/RawPrintVisitor.cpp", "libidmap2/ResourceUtils.cpp", "libidmap2/Result.cpp", - "libidmap2/XmlParser.cpp", + "libidmap2/Xml.cpp", "libidmap2/ZipFile.cpp", ], export_include_dirs: ["include"], @@ -99,7 +99,7 @@ cc_test { "tests/RawPrintVisitorTests.cpp", "tests/ResourceUtilsTests.cpp", "tests/ResultTests.cpp", - "tests/XmlParserTests.cpp", + "tests/XmlTests.cpp", "tests/ZipFileTests.cpp", ], required: [ diff --git a/cmds/idmap2/idmap2/Lookup.cpp b/cmds/idmap2/idmap2/Lookup.cpp index c5cf9807b689..b7ae9d090cee 100644 --- a/cmds/idmap2/idmap2/Lookup.cpp +++ b/cmds/idmap2/idmap2/Lookup.cpp @@ -33,10 +33,9 @@ #include "androidfw/Util.h" #include "idmap2/CommandLineOptions.h" #include "idmap2/Idmap.h" -#include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/XmlParser.h" +#include "idmap2/Xml.h" #include "idmap2/ZipFile.h" #include "utils/String16.h" #include "utils/String8.h" @@ -58,7 +57,8 @@ using android::idmap2::IdmapHeader; using android::idmap2::ResourceId; using android::idmap2::Result; using android::idmap2::Unit; -using android::idmap2::utils::ExtractOverlayManifestInfo; +using android::idmap2::Xml; +using android::idmap2::ZipFile; using android::util::Utf16ToUtf8; namespace { @@ -132,6 +132,29 @@ Result<std::string> WARN_UNUSED GetValue(const AssetManager2& am, ResourceId res return out; } +Result<std::string> GetTargetPackageNameFromManifest(const std::string& apk_path) { + const auto zip = ZipFile::Open(apk_path); + if (!zip) { + return Error("failed to open %s as zip", apk_path.c_str()); + } + const auto entry = zip->Uncompress("AndroidManifest.xml"); + if (!entry) { + return Error("failed to uncompress AndroidManifest.xml in %s", apk_path.c_str()); + } + const auto xml = Xml::Create(entry->buf, entry->size); + if (!xml) { + return Error("failed to create XML buffer"); + } + const auto tag = xml->FindTag("overlay"); + if (!tag) { + return Error("failed to find <overlay> tag"); + } + const auto iter = tag->find("targetPackage"); + if (iter == tag->end()) { + return Error("failed to find targetPackage attribute"); + } + return iter->second; +} } // namespace Result<Unit> Lookup(const std::vector<std::string>& args) { @@ -179,12 +202,12 @@ Result<Unit> Lookup(const std::vector<std::string>& args) { } apk_assets.push_back(std::move(target_apk)); - auto manifest_info = ExtractOverlayManifestInfo(idmap_header->GetOverlayPath().to_string(), - true /* assert_overlay */); - if (!manifest_info) { - return manifest_info.GetError(); + const Result<std::string> package_name = + GetTargetPackageNameFromManifest(idmap_header->GetOverlayPath().to_string()); + if (!package_name) { + return Error("failed to parse android:targetPackage from overlay manifest"); } - target_package_name = (*manifest_info).target_package; + target_package_name = *package_name; } else if (target_path != idmap_header->GetTargetPath()) { return Error("different target APKs (expected target APK %s but %s has target APK %s)", target_path.c_str(), idmap_path.c_str(), diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp index 1be8274f691c..053b7bc9afa3 100644 --- a/cmds/idmap2/idmap2/Scan.cpp +++ b/cmds/idmap2/idmap2/Scan.cpp @@ -33,7 +33,7 @@ #include "idmap2/ResourceUtils.h" #include "idmap2/Result.h" #include "idmap2/SysTrace.h" -#include "idmap2/XmlParser.h" +#include "idmap2/Xml.h" #include "idmap2/ZipFile.h" using android::idmap2::CommandLineOptions; diff --git a/cmds/idmap2/include/idmap2/Xml.h b/cmds/idmap2/include/idmap2/Xml.h new file mode 100644 index 000000000000..dd89dee0f64f --- /dev/null +++ b/cmds/idmap2/include/idmap2/Xml.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IDMAP2_INCLUDE_IDMAP2_XML_H_ +#define IDMAP2_INCLUDE_IDMAP2_XML_H_ + +#include <map> +#include <memory> +#include <string> + +#include "android-base/macros.h" +#include "androidfw/ResourceTypes.h" +#include "utils/String16.h" + +namespace android::idmap2 { + +class Xml { + public: + static std::unique_ptr<const Xml> Create(const uint8_t* data, size_t size, bool copyData = false); + + std::unique_ptr<std::map<std::string, std::string>> FindTag(const std::string& name) const; + + ~Xml(); + + private: + Xml() { + } + + mutable ResXMLTree xml_; + + DISALLOW_COPY_AND_ASSIGN(Xml); +}; + +} // namespace android::idmap2 + +#endif // IDMAP2_INCLUDE_IDMAP2_XML_H_ diff --git a/cmds/idmap2/include/idmap2/XmlParser.h b/cmds/idmap2/include/idmap2/XmlParser.h deleted file mode 100644 index 972a6d7e3427..000000000000 --- a/cmds/idmap2/include/idmap2/XmlParser.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ -#define IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ - -#include <iostream> -#include <map> -#include <memory> -#include <string> - -#include "Result.h" -#include "android-base/macros.h" -#include "androidfw/ResourceTypes.h" -#include "utils/String16.h" - -namespace android::idmap2 { - -class XmlParser { - public: - using Event = ResXMLParser::event_code_t; - class iterator; - - class Node { - public: - Event event() const; - std::string name() const; - - Result<std::string> GetAttributeStringValue(const std::string& name) const; - Result<Res_value> GetAttributeValue(const std::string& name) const; - - bool operator==(const Node& rhs) const; - bool operator!=(const Node& rhs) const; - - private: - explicit Node(const ResXMLTree& tree); - Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos); - - // Retrieves/Sets the position of the position of the xml parser in the xml tree. - ResXMLParser::ResXMLPosition get_position() const; - void set_position(const ResXMLParser::ResXMLPosition& pos); - - // If `inner_child` is true, seek advances the parser to the first inner child of the current - // node. Otherwise, seek advances the parser to the following node. Returns false if there is - // no node to seek to. - bool Seek(bool inner_child); - - ResXMLParser parser_; - friend iterator; - }; - - class iterator { - public: - iterator(const iterator& other) : iterator(other.tree_, other.iter_) { - } - - inline iterator& operator=(const iterator& rhs) { - iter_.set_position(rhs.iter_.get_position()); - return *this; - } - - inline bool operator==(const iterator& rhs) const { - return iter_ == rhs.iter_; - } - - inline bool operator!=(const iterator& rhs) const { - return !(*this == rhs); - } - - inline iterator operator++() { - // Seek to the following xml node. - iter_.Seek(false /* inner_child */); - return *this; - } - - iterator begin() const { - iterator child_it(*this); - // Seek to the first inner child of the current node. - child_it.iter_.Seek(true /* inner_child */); - return child_it; - } - - iterator end() const { - iterator child_it = begin(); - while (child_it.iter_.Seek(false /* inner_child */)) { - // Continue iterating until the end tag is found. - } - - return child_it; - } - - inline const Node operator*() { - return Node(tree_, iter_.get_position()); - } - - inline const Node* operator->() { - return &iter_; - } - - private: - explicit iterator(const ResXMLTree& tree) : tree_(tree), iter_(Node(tree)) { - } - iterator(const ResXMLTree& tree, const Node& node) - : tree_(tree), iter_(Node(tree, node.get_position())) { - } - - const ResXMLTree& tree_; - Node iter_; - friend XmlParser; - }; - - // Creates a new xml parser beginning at the first tag. - static Result<std::unique_ptr<const XmlParser>> Create(const void* data, size_t size, - bool copy_data = false); - ~XmlParser(); - - inline iterator tree_iterator() const { - return iterator(tree_); - } - - inline const ResStringPool& get_strings() const { - return tree_.getStrings(); - } - - private: - XmlParser() = default; - mutable ResXMLTree tree_; - - DISALLOW_COPY_AND_ASSIGN(XmlParser); -}; - -} // namespace android::idmap2 - -#endif // IDMAP2_INCLUDE_IDMAP2_XMLPARSER_H_ diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp index 018d3b4df110..71ba3f0f1ac2 100644 --- a/cmds/idmap2/libidmap2/ResourceUtils.cpp +++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp @@ -22,18 +22,18 @@ #include "androidfw/StringPiece.h" #include "androidfw/Util.h" #include "idmap2/Result.h" -#include "idmap2/XmlParser.h" +#include "idmap2/Xml.h" #include "idmap2/ZipFile.h" using android::StringPiece16; using android::idmap2::Result; -using android::idmap2::XmlParser; +using android::idmap2::Xml; using android::idmap2::ZipFile; using android::util::Utf16ToUtf8; namespace android::idmap2::utils { -Result<std::string> ResToTypeEntryName(const AssetManager2& am, uint32_t resid) { +Result<std::string> ResToTypeEntryName(const AssetManager2& am, ResourceId resid) { AssetManager2::ResourceName name; if (!am.GetResourceName(resid, &name)) { return Error("no resource 0x%08x in asset manager", resid); @@ -65,55 +65,42 @@ Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path, return Error("failed to uncompress AndroidManifest.xml from %s", path.c_str()); } - Result<std::unique_ptr<const XmlParser>> xml = XmlParser::Create(entry->buf, entry->size); + std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size); if (!xml) { return Error("failed to parse AndroidManifest.xml from %s", path.c_str()); } - auto manifest_it = (*xml)->tree_iterator(); - if (manifest_it->event() != XmlParser::Event::START_TAG || manifest_it->name() != "manifest") { - return Error("root element tag is not <manifest> in AndroidManifest.xml of %s", path.c_str()); - } - - auto overlay_it = std::find_if(manifest_it.begin(), manifest_it.end(), [](const auto& it) { - return it.event() == XmlParser::Event::START_TAG && it.name() == "overlay"; - }); - OverlayManifestInfo info{}; - if (overlay_it == manifest_it.end()) { - if (!assert_overlay) { - return info; + const auto tag = xml->FindTag("overlay"); + if (!tag) { + if (assert_overlay) { + return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str()); } - return Error("<overlay> missing from AndroidManifest.xml of %s", path.c_str()); + return info; } - if (auto result_str = overlay_it->GetAttributeStringValue("targetPackage")) { - info.target_package = *result_str; + auto iter = tag->find("targetPackage"); + if (iter == tag->end()) { + if (assert_overlay) { + return Error("android:targetPackage missing from <overlay> of %s", path.c_str()); + } } else { - return Error("android:targetPackage missing from <overlay> of %s: %s", path.c_str(), - result_str.GetErrorMessage().c_str()); + info.target_package = iter->second; } - if (auto result_str = overlay_it->GetAttributeStringValue("targetName")) { - info.target_name = *result_str; + iter = tag->find("targetName"); + if (iter != tag->end()) { + info.target_name = iter->second; } - if (auto result_value = overlay_it->GetAttributeValue("isStatic")) { - if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT && - (*result_value).dataType <= Res_value::TYPE_LAST_INT) { - info.is_static = (*result_value).data != 0U; - } else { - return Error("android:isStatic is not a boolean in AndroidManifest.xml of %s", path.c_str()); - } + iter = tag->find("isStatic"); + if (iter != tag->end()) { + info.is_static = std::stoul(iter->second) != 0U; } - if (auto result_value = overlay_it->GetAttributeValue("priority")) { - if ((*result_value).dataType >= Res_value::TYPE_FIRST_INT && - (*result_value).dataType <= Res_value::TYPE_LAST_INT) { - info.priority = (*result_value).data; - } else { - return Error("android:priority is not an integer in AndroidManifest.xml of %s", path.c_str()); - } + iter = tag->find("priority"); + if (iter != tag->end()) { + info.priority = std::stoi(iter->second); } return info; diff --git a/cmds/idmap2/libidmap2/Xml.cpp b/cmds/idmap2/libidmap2/Xml.cpp new file mode 100644 index 000000000000..264586829c47 --- /dev/null +++ b/cmds/idmap2/libidmap2/Xml.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "idmap2/Xml.h" + +#include <map> +#include <memory> +#include <string> +#include <utility> + +namespace android::idmap2 { + +std::unique_ptr<const Xml> Xml::Create(const uint8_t* data, size_t size, bool copyData) { + std::unique_ptr<Xml> xml(new Xml()); + if (xml->xml_.setTo(data, size, copyData) != NO_ERROR) { + return nullptr; + } + return xml; +} + +std::unique_ptr<std::map<std::string, std::string>> Xml::FindTag(const std::string& name) const { + const String16 tag_to_find(name.c_str(), name.size()); + xml_.restart(); + ResXMLParser::event_code_t type; + do { + type = xml_.next(); + if (type == ResXMLParser::START_TAG) { + size_t len; + const String16 tag(xml_.getElementName(&len)); + if (tag == tag_to_find) { + std::unique_ptr<std::map<std::string, std::string>> map( + new std::map<std::string, std::string>()); + for (size_t i = 0; i < xml_.getAttributeCount(); i++) { + const String16 key16(xml_.getAttributeName(i, &len)); + std::string key = String8(key16).c_str(); + + std::string value; + switch (xml_.getAttributeDataType(i)) { + case Res_value::TYPE_STRING: { + const String16 value16(xml_.getAttributeStringValue(i, &len)); + value = String8(value16).c_str(); + } break; + case Res_value::TYPE_INT_DEC: + case Res_value::TYPE_INT_HEX: + case Res_value::TYPE_INT_BOOLEAN: { + Res_value resValue; + xml_.getAttributeValue(i, &resValue); + value = std::to_string(resValue.data); + } break; + default: + return nullptr; + } + + map->emplace(std::make_pair(key, value)); + } + return map; + } + } + } while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT); + return nullptr; +} + +Xml::~Xml() { + xml_.uninit(); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/XmlParser.cpp b/cmds/idmap2/libidmap2/XmlParser.cpp deleted file mode 100644 index 526a560907aa..000000000000 --- a/cmds/idmap2/libidmap2/XmlParser.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "idmap2/XmlParser.h" - -#include <iostream> -#include <map> -#include <memory> -#include <string> -#include <utility> - -namespace android::idmap2 { - -template <typename T> -ResXMLParser::ResXMLPosition get_tree_position(const T& tree) { - ResXMLParser::ResXMLPosition pos{}; - tree.getPosition(&pos); - return pos; -} - -XmlParser::Node::Node(const ResXMLTree& tree) : Node(tree, get_tree_position(tree)) { -} -XmlParser::Node::Node(const ResXMLTree& tree, const ResXMLParser::ResXMLPosition& pos) - : parser_(tree) { - set_position(pos); -} - -bool XmlParser::Node::operator==(const XmlParser::Node& rhs) const { - ResXMLParser::ResXMLPosition pos = get_position(); - ResXMLParser::ResXMLPosition rhs_pos = rhs.get_position(); - return pos.curExt == rhs_pos.curExt && pos.curNode == rhs_pos.curNode && - pos.eventCode == rhs_pos.eventCode; -} - -bool XmlParser::Node::operator!=(const XmlParser::Node& rhs) const { - return !(*this == rhs); -} - -ResXMLParser::ResXMLPosition XmlParser::Node::get_position() const { - return get_tree_position(parser_); -} - -void XmlParser::Node::set_position(const ResXMLParser::ResXMLPosition& pos) { - parser_.setPosition(pos); -} - -bool XmlParser::Node::Seek(bool inner_child) { - if (parser_.getEventType() == XmlParser::Event::END_TAG) { - return false; - } - - ssize_t depth = 0; - XmlParser::Event code; - while ((code = parser_.next()) != XmlParser::Event::BAD_DOCUMENT && - code != XmlParser::Event::END_DOCUMENT) { - if (code == XmlParser::Event::START_TAG) { - if (++depth == (inner_child ? 1 : 0)) { - return true; - } - } else if (code == XmlParser::Event::END_TAG) { - if (--depth == (inner_child ? -1 : -2)) { - return false; - } - } - } - - return false; -} - -XmlParser::Event XmlParser::Node::event() const { - return parser_.getEventType(); -} - -std::string XmlParser::Node::name() const { - size_t len; - const String16 key16(parser_.getElementName(&len)); - return String8(key16).c_str(); -} - -Result<std::string> XmlParser::Node::GetAttributeStringValue(const std::string& name) const { - auto value = GetAttributeValue(name); - if (!value) { - return value.GetError(); - } - - switch ((*value).dataType) { - case Res_value::TYPE_STRING: { - size_t len; - const String16 value16(parser_.getStrings().stringAt((*value).data, &len)); - return std::string(String8(value16).c_str()); - } - case Res_value::TYPE_INT_DEC: - case Res_value::TYPE_INT_HEX: - case Res_value::TYPE_INT_BOOLEAN: { - return std::to_string((*value).data); - } - default: - return Error(R"(Failed to convert attribute "%s" value to a string)", name.c_str()); - } -} - -Result<Res_value> XmlParser::Node::GetAttributeValue(const std::string& name) const { - size_t len; - for (size_t i = 0; i < parser_.getAttributeCount(); i++) { - const String16 key16(parser_.getAttributeName(i, &len)); - std::string key = String8(key16).c_str(); - if (key != name) { - continue; - } - - Res_value res_value{}; - if (parser_.getAttributeValue(i, &res_value) == BAD_TYPE) { - return Error(R"(Bad value for attribute "%s")", name.c_str()); - } - - return res_value; - } - - return Error(R"(Failed to find attribute "%s")", name.c_str()); -} - -Result<std::unique_ptr<const XmlParser>> XmlParser::Create(const void* data, size_t size, - bool copy_data) { - auto parser = std::unique_ptr<const XmlParser>(new XmlParser()); - if (parser->tree_.setTo(data, size, copy_data) != NO_ERROR) { - return Error("Malformed xml block"); - } - - // Find the beginning of the first tag. - XmlParser::Event event; - while ((event = parser->tree_.next()) != XmlParser::Event::BAD_DOCUMENT && - event != XmlParser::Event::END_DOCUMENT && event != XmlParser::Event::START_TAG) { - } - - if (event == XmlParser::Event::END_DOCUMENT) { - return Error("Root tag was not be found"); - } - - if (event == XmlParser::Event::BAD_DOCUMENT) { - return Error("Bad xml document"); - } - - return parser; -} - -XmlParser::~XmlParser() { - tree_.uninit(); -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/XmlParserTests.cpp b/cmds/idmap2/tests/XmlParserTests.cpp deleted file mode 100644 index 1a7eaca4d67b..000000000000 --- a/cmds/idmap2/tests/XmlParserTests.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <cstdio> // fclose -#include <memory> -#include <string> - -#include "TestHelpers.h" -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "idmap2/XmlParser.h" -#include "idmap2/ZipFile.h" - -namespace android::idmap2 { - -Result<std::unique_ptr<const XmlParser>> CreateTestParser(const std::string& test_file) { - auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); - if (zip == nullptr) { - return Error("Failed to open zip file"); - } - - auto data = zip->Uncompress(test_file); - if (data == nullptr) { - return Error("Failed to open xml file"); - } - - return XmlParser::Create(data->buf, data->size, /* copy_data */ true); -} - -TEST(XmlParserTests, Create) { - auto xml = CreateTestParser("AndroidManifest.xml"); - ASSERT_TRUE(xml) << xml.GetErrorMessage(); - - fclose(stderr); // silence expected warnings from libandroidfw - const char* not_xml = "foo"; - auto fail = XmlParser::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml)); - ASSERT_FALSE(fail); -} - -TEST(XmlParserTests, NextChild) { - auto xml = CreateTestParser("res/xml/test.xml"); - ASSERT_TRUE(xml) << xml.GetErrorMessage(); - - auto root_iter = (*xml)->tree_iterator(); - ASSERT_EQ(root_iter->event(), XmlParser::Event::START_TAG); - ASSERT_EQ(root_iter->name(), "a"); - - auto a_iter = root_iter.begin(); - ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG); - ASSERT_EQ(a_iter->name(), "b"); - - auto c_iter = a_iter.begin(); - ASSERT_EQ(c_iter->event(), XmlParser::Event::START_TAG); - ASSERT_EQ(c_iter->name(), "c"); - - ++c_iter; - ASSERT_EQ(c_iter->event(), XmlParser::Event::END_TAG); - ASSERT_EQ(c_iter, a_iter.end()); - - ++a_iter; - ASSERT_EQ(a_iter->event(), XmlParser::Event::START_TAG); - ASSERT_EQ(a_iter->name(), "d"); - - // Skip the <e> tag. - ++a_iter; - ASSERT_EQ(a_iter->event(), XmlParser::Event::END_TAG); - ASSERT_EQ(a_iter, root_iter.end()); -} - -TEST(XmlParserTests, AttributeValues) { - auto xml = CreateTestParser("res/xml/test.xml"); - ASSERT_TRUE(xml) << xml.GetErrorMessage(); - - // Start at the <a> tag. - auto root_iter = (*xml)->tree_iterator(); - - // Start at the <b> tag. - auto a_iter = root_iter.begin(); - auto attribute_str = a_iter->GetAttributeStringValue("type_string"); - ASSERT_TRUE(attribute_str); - ASSERT_EQ(*attribute_str, "fortytwo"); - - auto attribute_value = a_iter->GetAttributeValue("type_int_dec"); - ASSERT_TRUE(attribute_value); - ASSERT_EQ(attribute_value->data, 42); - - attribute_value = a_iter->GetAttributeValue("type_int_hex"); - ASSERT_TRUE(attribute_value); - ASSERT_EQ(attribute_value->data, 42); - - attribute_value = a_iter->GetAttributeValue("type_int_boolean"); - ASSERT_TRUE(attribute_value); - ASSERT_EQ(attribute_value->data, 0xffffffff); -} - -TEST(XmlParserTests, IteratorEquality) { - auto xml = CreateTestParser("res/xml/test.xml"); - ASSERT_TRUE(xml) << xml.GetErrorMessage(); - - // Start at the <a> tag. - auto root_iter_1 = (*xml)->tree_iterator(); - auto root_iter_2 = (*xml)->tree_iterator(); - ASSERT_EQ(root_iter_1, root_iter_2); - ASSERT_EQ(*root_iter_1, *root_iter_2); - - // Start at the <b> tag. - auto a_iter_1 = root_iter_1.begin(); - auto a_iter_2 = root_iter_2.begin(); - ASSERT_NE(a_iter_1, root_iter_1.end()); - ASSERT_NE(a_iter_2, root_iter_2.end()); - ASSERT_EQ(a_iter_1, a_iter_2); - ASSERT_EQ(*a_iter_1, *a_iter_2); - - // Move to the <d> tag. - ++a_iter_1; - ++a_iter_2; - ASSERT_NE(a_iter_1, root_iter_1.end()); - ASSERT_NE(a_iter_2, root_iter_2.end()); - ASSERT_EQ(a_iter_1, a_iter_2); - ASSERT_EQ(*a_iter_1, *a_iter_2); - - // Move to the end of the <a> tag. - ++a_iter_1; - ++a_iter_2; - ASSERT_EQ(a_iter_1, root_iter_1.end()); - ASSERT_EQ(a_iter_2, root_iter_2.end()); - ASSERT_EQ(a_iter_1, a_iter_2); - ASSERT_EQ(*a_iter_1, *a_iter_2); -} - -TEST(XmlParserTests, Backtracking) { - auto xml = CreateTestParser("res/xml/test.xml"); - ASSERT_TRUE(xml) << xml.GetErrorMessage(); - - // Start at the <a> tag. - auto root_iter_1 = (*xml)->tree_iterator(); - - // Start at the <b> tag. - auto a_iter_1 = root_iter_1.begin(); - - // Start a second iterator at the <a> tag. - auto root_iter_2 = root_iter_1; - ASSERT_EQ(root_iter_1, root_iter_2); - ASSERT_EQ(*root_iter_1, *root_iter_2); - - // Move the first iterator to the end of the <a> tag. - auto root_iter_end_1 = root_iter_1.end(); - ++root_iter_1; - ASSERT_NE(root_iter_1, root_iter_2); - ASSERT_NE(*root_iter_1, *root_iter_2); - - // Move to the <d> tag. - ++a_iter_1; - ASSERT_NE(a_iter_1, root_iter_end_1); - - // Move to the end of the <a> tag. - ++a_iter_1; - ASSERT_EQ(a_iter_1, root_iter_end_1); -} - -} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/XmlTests.cpp b/cmds/idmap2/tests/XmlTests.cpp new file mode 100644 index 000000000000..df63211a9209 --- /dev/null +++ b/cmds/idmap2/tests/XmlTests.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cstdio> // fclose + +#include "TestHelpers.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "idmap2/Xml.h" +#include "idmap2/ZipFile.h" + +using ::testing::IsNull; +using ::testing::NotNull; + +namespace android::idmap2 { + +TEST(XmlTests, Create) { + auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); + ASSERT_THAT(zip, NotNull()); + + auto data = zip->Uncompress("AndroidManifest.xml"); + ASSERT_THAT(data, NotNull()); + + auto xml = Xml::Create(data->buf, data->size); + ASSERT_THAT(xml, NotNull()); + + fclose(stderr); // silence expected warnings from libandroidfw + const char* not_xml = "foo"; + auto fail = Xml::Create(reinterpret_cast<const uint8_t*>(not_xml), strlen(not_xml)); + ASSERT_THAT(fail, IsNull()); +} + +TEST(XmlTests, FindTag) { + auto zip = ZipFile::Open(GetTestDataPath() + "/target/target.apk"); + ASSERT_THAT(zip, NotNull()); + + auto data = zip->Uncompress("res/xml/test.xml"); + ASSERT_THAT(data, NotNull()); + + auto xml = Xml::Create(data->buf, data->size); + ASSERT_THAT(xml, NotNull()); + + auto attrs = xml->FindTag("c"); + ASSERT_THAT(attrs, NotNull()); + ASSERT_EQ(attrs->size(), 4U); + ASSERT_EQ(attrs->at("type_string"), "fortytwo"); + ASSERT_EQ(std::stoi(attrs->at("type_int_dec")), 42); + ASSERT_EQ(std::stoi(attrs->at("type_int_hex")), 42); + ASSERT_NE(std::stoul(attrs->at("type_int_boolean")), 0U); + + auto fail = xml->FindTag("does-not-exist"); + ASSERT_THAT(fail, IsNull()); +} + +} // namespace android::idmap2 diff --git a/cmds/idmap2/tests/data/target/res/xml/test.xml b/cmds/idmap2/tests/data/target/res/xml/test.xml index 56a3f7f0b13a..0fe21c6b6d0a 100644 --- a/cmds/idmap2/tests/data/target/res/xml/test.xml +++ b/cmds/idmap2/tests/data/target/res/xml/test.xml @@ -14,15 +14,12 @@ limitations under the License. --> <a> - <b type_string="fortytwo" - type_int_dec="42" - type_int_hex="0x2a" - type_int_boolean="true"> - - <c /> + <b> + <c + type_string="fortytwo" + type_int_dec="42" + type_int_hex="0x2a" + type_int_boolean="true" + /> </b> - - <d> - <e /> - </d> -</a>
\ No newline at end of file +</a> diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk Binary files differindex 2eb7c477c3b4..033305aaed4f 100644 --- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk +++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk Binary files differindex 251cf46f969d..9bcd6dcabcde 100644 --- a/cmds/idmap2/tests/data/target/target.apk +++ b/cmds/idmap2/tests/data/target/target.apk |