Files
android_frameworks_base/tools/aapt2/link/FlaggedXmlVersioner.cpp
Jeremy Meyer 741b68abb5 Only check for xml flag attributes when we know there is one there
This makes it so that when we are flattening xml files we figure out
which elements have the featureFlag attribute and persist that in the
binary xml. Then at runtime we can quickly check that before we try and
find the actual attribute.

Test: Automation
Flag: android.content.res.layout_readwrite_flags
Bug: 398541237
Change-Id: I1015dd45c4a0ba38bea75cad30b59cec4a05d825
2025-04-29 22:44:09 +00:00

105 lines
3.9 KiB
C++

/*
* Copyright (C) 2025 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 "link/FlaggedXmlVersioner.h"
#include "SdkConstants.h"
#include "androidfw/Util.h"
#include "cmd/Util.h"
using ::aapt::xml::Element;
using ::aapt::xml::NodeCast;
namespace aapt {
// An xml visitor that goes through the a doc and removes any elements that are behind non-negated
// flags. It also removes the featureFlag attribute from elements behind negated flags.
class AllDisabledFlagsVisitor : public xml::Visitor {
public:
void Visit(xml::Element* node) override {
std::erase_if(node->children, [this](const std::unique_ptr<xml::Node>& node) {
return FixupOrShouldRemove(node);
});
VisitChildren(node);
}
private:
bool FixupOrShouldRemove(const std::unique_ptr<xml::Node>& node) {
if (auto* el = NodeCast<Element>(node.get())) {
auto* attr = el->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
if (attr == nullptr) {
return false;
}
// This class assumes all flags are disabled so we want to remove any elements behind flags
// unless the flag specification is negated. In the negated case we remove the featureFlag
// attribute because we have already determined whether we are keeping the element or not.
std::string_view flag_name = util::TrimWhitespace(attr->value);
if (flag_name.starts_with('!')) {
el->RemoveAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
return false;
} else {
return true;
}
}
return false;
}
};
// An xml visitor that finds all elements with flags and moves them from xml attributes to a struct
// on the element.
class FlagMarkingVisitor : public xml::Visitor {
public:
void Visit(xml::Element* node) override {
if (auto attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag)) {
auto flag = ParseFlag(attr->value);
node->flag = flag;
node->RemoveAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
}
VisitChildren(node);
}
};
std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context,
xml::XmlResource* doc) {
std::vector<std::unique_ptr<xml::XmlResource>> docs;
if (!doc->file.uses_readwrite_feature_flags) {
docs.push_back(doc->Clone());
} else if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) ||
(static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) {
// Support for read/write flags was added in baklava so if the doc will only get used on
// baklava or later we can just return the original doc.
auto clonedVersion = doc->Clone();
FlagMarkingVisitor flag_marking_visitor;
clonedVersion->root->Accept(&flag_marking_visitor);
docs.push_back(std::move(clonedVersion));
} else {
auto preBaklavaVersion = doc->Clone();
AllDisabledFlagsVisitor disabled_flag_visitor;
preBaklavaVersion->root->Accept(&disabled_flag_visitor);
docs.push_back(std::move(preBaklavaVersion));
auto baklavaVersion = doc->Clone();
baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA;
FlagMarkingVisitor flag_marking_visitor;
baklavaVersion->root->Accept(&flag_marking_visitor);
docs.push_back(std::move(baklavaVersion));
}
return docs;
}
} // namespace aapt