Loading tools/aapt2/ResourceParser.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -446,11 +446,11 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou } } bool untranslateable = false; if (Maybe<StringPiece16> untranslateableAttr = findAttribute(parser, u"untranslateable")) { if (!ResourceUtils::tryParseBool(untranslateableAttr.value(), &untranslateable)) { bool translateable = mOptions.translatable; if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) { if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { mDiag->error(DiagMessage(source) << "invalid value for 'untranslateable'. Must be a boolean"); << "invalid value for 'translatable'. Must be a boolean"); return false; } } Loading @@ -461,10 +461,10 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou return false; } if (formatted || untranslateable) { if (formatted && translateable) { if (String* stringValue = valueCast<String>(outResource->value.get())) { if (!util::verifyJavaStringFormat(*stringValue->value)) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) mDiag->error(DiagMessage(source) << "multiple substitutions specified in non-positional format; " "did you mean to add the formatted=\"false\" attribute?"); return false; Loading Loading @@ -973,6 +973,9 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); // Declare-styleable is always public, because it technically only exists in R.java. outResource->symbolState = SymbolState::kPublic; std::u16string comment; bool error = false; const size_t depth = parser->getDepth(); Loading tools/aapt2/ResourceParser.h +5 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,11 @@ struct ResourceParserOptions { * that don't match before we compile them. */ Maybe<std::u16string> product; /** * Whether the default setting for this parser is to allow translation. */ bool translatable = true; }; /* Loading tools/aapt2/ResourceParser_test.cpp +16 −1 Original line number Diff line number Diff line Loading @@ -52,8 +52,10 @@ struct ResourceParserTest : public ::testing::Test { Maybe<std::u16string> product = {}) { std::stringstream input(kXmlPreamble); input << "<resources>\n" << str << "\n</resources>" << std::endl; ResourceParserOptions parserOptions; parserOptions.product = product; ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {}, ResourceParserOptions{ product }); parserOptions); XmlPullParser xmlParser(input); if (parser.parse(&xmlParser)) { return ::testing::AssertionSuccess(); Loading @@ -80,6 +82,14 @@ TEST_F(ResourceParserTest, ParseEscapedString) { EXPECT_EQ(std::u16string(u"?123"), *str->value); } TEST_F(ResourceParserTest, ParseFormattedString) { std::string input = "<string name=\"foo\">%d %s</string>"; ASSERT_FALSE(testParse(input)); input = "<string name=\"foo\">%1$d %2$s</string>"; ASSERT_TRUE(testParse(input)); } TEST_F(ResourceParserTest, IgnoreXliffTags) { std::string input = "<string name=\"foo\" \n" " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" Loading Loading @@ -322,6 +332,11 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { "</declare-styleable>"; ASSERT_TRUE(testParse(input)); Maybe<ResourceTable::SearchResult> result = mTable.findResource(test::parseNameOrDie(u"@styleable/foo")); AAPT_ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar"); ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); Loading tools/aapt2/compile/Compile.cpp +8 −1 Original line number Diff line number Diff line Loading @@ -132,8 +132,15 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, // Parse the values file from XML. XmlPullParser xmlParser(fin); ResourceParserOptions parserOptions; parserOptions.product = options.product; // If the filename includes donottranslate, then the default translatable is false. parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos; ResourceParser resParser(context->getDiagnostics(), &table, pathData.source, pathData.config, ResourceParserOptions{ options.product }); pathData.config, parserOptions); if (!resParser.parse(&xmlParser)) { return false; } Loading tools/aapt2/link/Linkers.h +41 −6 Original line number Diff line number Diff line Loading @@ -38,26 +38,61 @@ struct AutoVersioner : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; struct PrivateAttributeMover : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; struct XmlAutoVersioner : public IXmlResourceConsumer { bool consume(IAaptContext* context, XmlResource* resource) override; }; /** * If any attribute resource values are defined as public, this consumer will move all private * attribute resource values to a private ^private-attr type, avoiding backwards compatibility * issues with new apps running on old platforms. * * The Android platform ignores resource attributes it doesn't recognize, so an app developer can * use new attributes in their layout XML files without worrying about versioning. This assumption * actually breaks on older platforms. OEMs may add private attributes that are used internally. * AAPT originally assigned all private attributes IDs immediately proceeding the public attributes' * IDs. * * This means that on a newer Android platform, an ID previously assigned to a private attribute * may end up assigned to a public attribute. * * App developers assume using the newer attribute is safe on older platforms because it will * be ignored. Instead, the platform thinks the new attribute is an older, private attribute and * will interpret it as such. This leads to unintended styling and exceptions thrown due to * unexpected types. * * By moving the private attributes to a completely different type, this ID conflict will never * occur. */ struct PrivateAttributeMover : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; /** * Resolves all references to resources in the ResourceTable and assigns them IDs. * The ResourceTable must already have IDs assigned to each resource. * Once the ResourceTable is processed by this linker, it is ready to be flattened. */ struct ReferenceLinker : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; class XmlReferenceLinker : IXmlResourceConsumer { /** * Resolves attributes in the XmlResource and compiles string values to resource values. * Once an XmlResource is processed by this linker, it is ready to be flattened. */ class XmlReferenceLinker : public IXmlResourceConsumer { private: std::set<int> mSdkLevelsFound; public: bool consume(IAaptContext* context, XmlResource* resource) override; const std::set<int>& getSdkLevels() const { /** * Once the XmlResource has been consumed, this returns the various SDK levels in which * framework attributes used within the XML document were defined. */ inline const std::set<int>& getSdkLevels() const { return mSdkLevelsFound; } }; Loading Loading
tools/aapt2/ResourceParser.cpp +9 −6 Original line number Diff line number Diff line Loading @@ -446,11 +446,11 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou } } bool untranslateable = false; if (Maybe<StringPiece16> untranslateableAttr = findAttribute(parser, u"untranslateable")) { if (!ResourceUtils::tryParseBool(untranslateableAttr.value(), &untranslateable)) { bool translateable = mOptions.translatable; if (Maybe<StringPiece16> translateableAttr = findAttribute(parser, u"translatable")) { if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { mDiag->error(DiagMessage(source) << "invalid value for 'untranslateable'. Must be a boolean"); << "invalid value for 'translatable'. Must be a boolean"); return false; } } Loading @@ -461,10 +461,10 @@ bool ResourceParser::parseString(XmlPullParser* parser, ParsedResource* outResou return false; } if (formatted || untranslateable) { if (formatted && translateable) { if (String* stringValue = valueCast<String>(outResource->value.get())) { if (!util::verifyJavaStringFormat(*stringValue->value)) { mDiag->error(DiagMessage(mSource.withLine(parser->getLineNumber())) mDiag->error(DiagMessage(source) << "multiple substitutions specified in non-positional format; " "did you mean to add the formatted=\"false\" attribute?"); return false; Loading Loading @@ -973,6 +973,9 @@ bool ResourceParser::parseDeclareStyleable(XmlPullParser* parser, ParsedResource const Source source = mSource.withLine(parser->getLineNumber()); std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>(); // Declare-styleable is always public, because it technically only exists in R.java. outResource->symbolState = SymbolState::kPublic; std::u16string comment; bool error = false; const size_t depth = parser->getDepth(); Loading
tools/aapt2/ResourceParser.h +5 −0 Original line number Diff line number Diff line Loading @@ -40,6 +40,11 @@ struct ResourceParserOptions { * that don't match before we compile them. */ Maybe<std::u16string> product; /** * Whether the default setting for this parser is to allow translation. */ bool translatable = true; }; /* Loading
tools/aapt2/ResourceParser_test.cpp +16 −1 Original line number Diff line number Diff line Loading @@ -52,8 +52,10 @@ struct ResourceParserTest : public ::testing::Test { Maybe<std::u16string> product = {}) { std::stringstream input(kXmlPreamble); input << "<resources>\n" << str << "\n</resources>" << std::endl; ResourceParserOptions parserOptions; parserOptions.product = product; ResourceParser parser(mContext->getDiagnostics(), &mTable, Source{ "test" }, {}, ResourceParserOptions{ product }); parserOptions); XmlPullParser xmlParser(input); if (parser.parse(&xmlParser)) { return ::testing::AssertionSuccess(); Loading @@ -80,6 +82,14 @@ TEST_F(ResourceParserTest, ParseEscapedString) { EXPECT_EQ(std::u16string(u"?123"), *str->value); } TEST_F(ResourceParserTest, ParseFormattedString) { std::string input = "<string name=\"foo\">%d %s</string>"; ASSERT_FALSE(testParse(input)); input = "<string name=\"foo\">%1$d %2$s</string>"; ASSERT_TRUE(testParse(input)); } TEST_F(ResourceParserTest, IgnoreXliffTags) { std::string input = "<string name=\"foo\" \n" " xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" Loading Loading @@ -322,6 +332,11 @@ TEST_F(ResourceParserTest, ParseAttributesDeclareStyleable) { "</declare-styleable>"; ASSERT_TRUE(testParse(input)); Maybe<ResourceTable::SearchResult> result = mTable.findResource(test::parseNameOrDie(u"@styleable/foo")); AAPT_ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); Attribute* attr = test::getValue<Attribute>(&mTable, u"@attr/bar"); ASSERT_NE(attr, nullptr); EXPECT_TRUE(attr->isWeak()); Loading
tools/aapt2/compile/Compile.cpp +8 −1 Original line number Diff line number Diff line Loading @@ -132,8 +132,15 @@ static bool compileTable(IAaptContext* context, const CompileOptions& options, // Parse the values file from XML. XmlPullParser xmlParser(fin); ResourceParserOptions parserOptions; parserOptions.product = options.product; // If the filename includes donottranslate, then the default translatable is false. parserOptions.translatable = pathData.name.find(u"donottranslate") == std::string::npos; ResourceParser resParser(context->getDiagnostics(), &table, pathData.source, pathData.config, ResourceParserOptions{ options.product }); pathData.config, parserOptions); if (!resParser.parse(&xmlParser)) { return false; } Loading
tools/aapt2/link/Linkers.h +41 −6 Original line number Diff line number Diff line Loading @@ -38,26 +38,61 @@ struct AutoVersioner : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; struct PrivateAttributeMover : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; struct XmlAutoVersioner : public IXmlResourceConsumer { bool consume(IAaptContext* context, XmlResource* resource) override; }; /** * If any attribute resource values are defined as public, this consumer will move all private * attribute resource values to a private ^private-attr type, avoiding backwards compatibility * issues with new apps running on old platforms. * * The Android platform ignores resource attributes it doesn't recognize, so an app developer can * use new attributes in their layout XML files without worrying about versioning. This assumption * actually breaks on older platforms. OEMs may add private attributes that are used internally. * AAPT originally assigned all private attributes IDs immediately proceeding the public attributes' * IDs. * * This means that on a newer Android platform, an ID previously assigned to a private attribute * may end up assigned to a public attribute. * * App developers assume using the newer attribute is safe on older platforms because it will * be ignored. Instead, the platform thinks the new attribute is an older, private attribute and * will interpret it as such. This leads to unintended styling and exceptions thrown due to * unexpected types. * * By moving the private attributes to a completely different type, this ID conflict will never * occur. */ struct PrivateAttributeMover : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; /** * Resolves all references to resources in the ResourceTable and assigns them IDs. * The ResourceTable must already have IDs assigned to each resource. * Once the ResourceTable is processed by this linker, it is ready to be flattened. */ struct ReferenceLinker : public IResourceTableConsumer { bool consume(IAaptContext* context, ResourceTable* table) override; }; class XmlReferenceLinker : IXmlResourceConsumer { /** * Resolves attributes in the XmlResource and compiles string values to resource values. * Once an XmlResource is processed by this linker, it is ready to be flattened. */ class XmlReferenceLinker : public IXmlResourceConsumer { private: std::set<int> mSdkLevelsFound; public: bool consume(IAaptContext* context, XmlResource* resource) override; const std::set<int>& getSdkLevels() const { /** * Once the XmlResource has been consumed, this returns the various SDK levels in which * framework attributes used within the XML document were defined. */ inline const std::set<int>& getSdkLevels() const { return mSdkLevelsFound; } }; Loading