Use R field directly for shared lib styleable attrs
If a shared library exposes an attribute and a client uses the
attribute in its own styleable, the value of the lib attribute
resource id in the client styleable must be fixed with the correct
package id at runtime. Since the client will not have an
onResourcesLoaded to call, the client should directly reference the
attribute resource field in its styleable.
Bug: 147674078
Test: aapt2_tests
Change-Id: I8e64bb2d3165a7072e2604fe1730b248545978f4
diff --git a/tools/aapt2/java/ClassDefinition.h b/tools/aapt2/java/ClassDefinition.h
index 1e4b681..995495a 100644
--- a/tools/aapt2/java/ClassDefinition.h
+++ b/tools/aapt2/java/ClassDefinition.h
@@ -70,8 +70,8 @@
return name_;
}
- void Print(bool final, text::Printer* printer, bool strip_api_annotations = false)
- const override {
+ void Print(bool final, text::Printer* printer,
+ bool strip_api_annotations = false) const override {
using std::to_string;
ClassMember::Print(final, printer, strip_api_annotations);
@@ -127,13 +127,13 @@
using ResourceMember = PrimitiveMember<ResourceId>;
using StringMember = PrimitiveMember<std::string>;
-template <typename T>
+template <typename T, typename StringConverter>
class PrimitiveArrayMember : public ClassMember {
public:
explicit PrimitiveArrayMember(const android::StringPiece& name) : name_(name.to_string()) {}
void AddElement(const T& val) {
- elements_.push_back(val);
+ elements_.emplace_back(val);
}
bool empty() const override {
@@ -158,7 +158,7 @@
printer->Println();
}
- printer->Print(to_string(*current));
+ printer->Print(StringConverter::ToString(*current));
if (std::distance(current, end) > 1) {
printer->Print(", ");
}
@@ -175,7 +175,24 @@
std::vector<T> elements_;
};
-using ResourceArrayMember = PrimitiveArrayMember<ResourceId>;
+struct FieldReference {
+ explicit FieldReference(std::string reference) : ref(std::move(reference)) {
+ }
+ std::string ref;
+};
+
+struct ResourceArrayMemberStringConverter {
+ static std::string ToString(const std::variant<ResourceId, FieldReference>& ref) {
+ if (auto id = std::get_if<ResourceId>(&ref)) {
+ return to_string(*id);
+ } else {
+ return std::get<FieldReference>(ref).ref;
+ }
+ }
+};
+
+using ResourceArrayMember = PrimitiveArrayMember<std::variant<ResourceId, FieldReference>,
+ ResourceArrayMemberStringConverter>;
// Represents a method in a class.
class MethodDefinition : public ClassMember {
diff --git a/tools/aapt2/java/JavaClassGenerator.cpp b/tools/aapt2/java/JavaClassGenerator.cpp
index f0f839d..59dd481 100644
--- a/tools/aapt2/java/JavaClassGenerator.cpp
+++ b/tools/aapt2/java/JavaClassGenerator.cpp
@@ -224,7 +224,16 @@
return cmp_ids_dynamic_after_framework(lhs_id, rhs_id);
}
-void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
+static FieldReference GetRFieldReference(const ResourceName& name,
+ StringPiece fallback_package_name) {
+ const std::string package_name =
+ name.package.empty() ? fallback_package_name.to_string() : name.package;
+ const std::string entry = JavaClassGenerator::TransformToFieldName(name.entry);
+ return FieldReference(
+ StringPrintf("%s.R.%s.%s", package_name.c_str(), to_string(name.type).data(), entry.c_str()));
+}
+
+bool JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
const Styleable& styleable,
const StringPiece& package_name_to_generate,
ClassDefinition* out_class_def,
@@ -340,14 +349,29 @@
// Add the ResourceIds to the array member.
for (size_t i = 0; i < attr_count; i++) {
- const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
- array_def->AddElement(id);
+ const StyleableAttr& attr = sorted_attributes[i];
+ std::string r_txt_contents;
+ if (attr.symbol && attr.symbol.value().is_dynamic) {
+ if (!attr.attr_ref->name) {
+ error_ = "unable to determine R.java field name of dynamic resource";
+ return false;
+ }
+
+ const FieldReference field_name =
+ GetRFieldReference(attr.attr_ref->name.value(), package_name_to_generate);
+ array_def->AddElement(field_name);
+ r_txt_contents = field_name.ref;
+ } else {
+ const ResourceId attr_id = attr.attr_ref->id.value_or_default(ResourceId(0));
+ array_def->AddElement(attr_id);
+ r_txt_contents = to_string(attr_id);
+ }
if (r_txt_printer != nullptr) {
if (i != 0) {
r_txt_printer->Print(",");
}
- r_txt_printer->Print(" ").Print(id.to_string());
+ r_txt_printer->Print(" ").Print(r_txt_contents);
}
}
@@ -419,19 +443,7 @@
}
}
- // If there is a rewrite method to generate, add the statements that rewrite package IDs
- // for this styleable.
- if (out_rewrite_method != nullptr) {
- out_rewrite_method->AppendStatement(
- StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
- out_rewrite_method->AppendStatement(
- StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
- out_rewrite_method->AppendStatement(
- StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | packageIdBits;",
- array_field_name.data(), array_field_name.data()));
- out_rewrite_method->AppendStatement(" }");
- out_rewrite_method->AppendStatement("}");
- }
+ return true;
}
void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
@@ -448,8 +460,7 @@
const std::string field_name = TransformToFieldName(name.entry);
if (out_class_def != nullptr) {
- std::unique_ptr<ResourceMember> resource_member =
- util::make_unique<ResourceMember>(field_name, real_id);
+ auto resource_member = util::make_unique<ResourceMember>(field_name, real_id);
// Build the comments and annotations for this entry.
AnnotationProcessor* processor = resource_member->GetCommentBuilder();
@@ -551,12 +562,11 @@
if (resource_name.type == ResourceType::kStyleable) {
CHECK(!entry->values.empty());
-
- const Styleable* styleable =
- static_cast<const Styleable*>(entry->values.front()->value.get());
-
- ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
- out_rewrite_method_def, r_txt_printer);
+ const auto styleable = reinterpret_cast<const Styleable*>(entry->values.front()->value.get());
+ if (!ProcessStyleable(resource_name, id, *styleable, package_name_to_generate,
+ out_type_class_def, out_rewrite_method_def, r_txt_printer)) {
+ return false;
+ }
} else {
ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
r_txt_printer);
@@ -626,8 +636,7 @@
if (type->type == ResourceType::kAttr) {
// Also include private attributes in this same class.
- const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
- if (priv_type) {
+ if (const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate)) {
if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
rewrite_method.get(), r_txt_printer.get())) {
return false;
diff --git a/tools/aapt2/java/JavaClassGenerator.h b/tools/aapt2/java/JavaClassGenerator.h
index 853120b..d9d1b39 100644
--- a/tools/aapt2/java/JavaClassGenerator.h
+++ b/tools/aapt2/java/JavaClassGenerator.h
@@ -105,7 +105,7 @@
// Writes a styleable resource to the R.java file, optionally writing out a rewrite rule for
// its package ID if `out_rewrite_method` is not nullptr.
// `package_name_to_generate` is the package
- void ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
+ bool ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
const Styleable& styleable,
const android::StringPiece& package_name_to_generate,
ClassDefinition* out_class_def, MethodDefinition* out_rewrite_method,
diff --git a/tools/aapt2/java/JavaClassGenerator_test.cpp b/tools/aapt2/java/JavaClassGenerator_test.cpp
index 04e2010..ec5b415 100644
--- a/tools/aapt2/java/JavaClassGenerator_test.cpp
+++ b/tools/aapt2/java/JavaClassGenerator_test.cpp
@@ -581,7 +581,7 @@
out.Flush();
EXPECT_THAT(output, HasSubstr("public static final int[] MyStyleable={"));
- EXPECT_THAT(output, HasSubstr("0x01010000, 0x00010000"));
+ EXPECT_THAT(output, HasSubstr("0x01010000, lib.R.attr.dynamic_attr"));
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_android_framework_attr=0;"));
EXPECT_THAT(output, HasSubstr("public static final int MyStyleable_dynamic_attr=1;"));
}