Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e4e989cc authored by Ryan Mitchell's avatar Ryan Mitchell
Browse files

RRO: Added partition policies for overlays

<overlayable> tags can now have policy elements that indicate which
partition the overlay apk must reside on in order to be allowed to
overlay a resource. This change only adds parsing of <policy> and
encoding of policy in the proto ResourceTable. A later change will add
the encoding of policy and overlayable in the binary APK.

<overlayable>
  <policy type="system|vendor|product|product_services|public" >
    <item type="string" name="oof" />
  </policy>
</overlayable>

Bug: 110869880
Test: make aapt2_tests
Change-Id: I8d4ed7b0e01f981149c6e3190af1681073b79b03
parent cfc152af
Loading
Loading
Loading
Loading
+23 −2
Original line number Diff line number Diff line
@@ -306,8 +306,29 @@ void Debug::PrintTable(const ResourceTable& table, const DebugPrintTableOptions&
            break;
        }

        if (entry->overlayable) {
        for (size_t i = 0; i < entry->overlayable_declarations.size(); i++) {
          printer->Print((i == 0) ? " " : "|");
          printer->Print("OVERLAYABLE");

          if (entry->overlayable_declarations[i].policy) {
            switch (entry->overlayable_declarations[i].policy.value()) {
              case Overlayable::Policy::kProduct:
                printer->Print("_PRODUCT");
                break;
              case Overlayable::Policy::kProductServices:
                printer->Print("_PRODUCT_SERVICES");
                break;
              case Overlayable::Policy::kSystem:
                printer->Print("_SYSTEM");
                break;
              case Overlayable::Policy::kVendor:
                printer->Print("_VENDOR");
                break;
              case Overlayable::Policy::kPublic:
                printer->Print("_PUBLIC");
                break;
            }
          }
        }

        printer->Println();
+112 −52
Original line number Diff line number Diff line
@@ -99,7 +99,7 @@ struct ParsedResource {
  ResourceId id;
  Visibility::Level visibility_level = Visibility::Level::kUndefined;
  bool allow_new = false;
  bool overlayable = false;
  std::vector<Overlayable> overlayable_declarations;

  std::string comment;
  std::unique_ptr<Value> value;
@@ -133,11 +133,8 @@ static bool AddResourcesToTable(ResourceTable* table, IDiagnostics* diag, Parsed
    }
  }

  if (res->overlayable) {
    Overlayable overlayable;
    overlayable.source = res->source;
    overlayable.comment = res->comment;
    if (!table->SetOverlayable(res->name, overlayable, diag)) {
  for (auto& overlayable : res->overlayable_declarations) {
    if (!table->AddOverlayable(res->name, overlayable, diag)) {
      return false;
    }
  }
@@ -673,7 +670,7 @@ bool ResourceParser::ParseResource(xml::XmlPullParser* parser,
  if (can_be_bag) {
    const auto bag_iter = elToBagMap.find(resource_type);
    if (bag_iter != elToBagMap.end()) {
      // Ensure we have a name (unless this is a <public-group>).
      // Ensure we have a name (unless this is a <public-group> or <overlayable>).
      if (resource_type != "public-group" && resource_type != "overlayable") {
        if (!maybe_name) {
          diag_->Error(DiagMessage(out_resource->source)
@@ -1062,44 +1059,100 @@ bool ResourceParser::ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out
bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource) {
  if (out_resource->config != ConfigDescription::DefaultConfig()) {
    diag_->Warn(DiagMessage(out_resource->source)
                << "ignoring configuration '" << out_resource->config << "' for <overlayable> tag");
                    << "ignoring configuration '" << out_resource->config
                    << "' for <overlayable> tag");
  }

  if (Maybe<StringPiece> maybe_policy = xml::FindNonEmptyAttribute(parser, "policy")) {
    const StringPiece& policy = maybe_policy.value();
    if (policy != "system") {
      diag_->Error(DiagMessage(out_resource->source)
                   << "<overlayable> has invalid policy '" << policy << "'");
      return false;
    }
  }
  std::string comment;
  std::vector<Overlayable::Policy> policies;

  bool error = false;
  const size_t depth = parser->depth();
  while (xml::XmlPullParser::NextChildNode(parser, depth)) {
    if (parser->event() != xml::XmlPullParser::Event::kStartElement) {
      // Skip text/comments.
  const size_t start_depth = parser->depth();
  while (xml::XmlPullParser::IsGoodEvent(parser->Next())) {
    xml::XmlPullParser::Event event = parser->event();
    if (event == xml::XmlPullParser::Event::kEndElement && parser->depth() == start_depth) {
      // Break the loop when exiting the overyabale element
      break;
    } else if (event == xml::XmlPullParser::Event::kEndElement
               && parser->depth() == start_depth + 1) {
      // Clear the current policies when exiting the policy element
      policies.clear();
      continue;
    } else if (event == xml::XmlPullParser::Event::kComment) {
      // Get the comment of individual item elements
      comment = parser->comment();
      continue;
    } else if (event != xml::XmlPullParser::Event::kStartElement) {
      // Skip to the next element
      continue;
    }

    const Source item_source = source_.WithLine(parser->line_number());
    const std::string& element_namespace = parser->element_namespace();
    const std::string& element_name = parser->element_name();
    const std::string& element_namespace = parser->element_namespace();

    if (element_namespace.empty() && element_name == "item") {
      if (!ParseOverlayableItem(parser, policies, comment, out_resource)) {
        error = true;
      }
    } else if (element_namespace.empty() && element_name == "policy") {
      if (!policies.empty()) {
        // If the policy list is not empty, then we are currently inside a policy element
        diag_->Error(DiagMessage(item_source) << "<policy> blocks cannot be recursively nested");
        error = true;
        break;
      } else if (Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type")) {
        // Parse the polices separated by vertical bar characters to allow for specifying multiple
        // policies at once
        for (StringPiece part : util::Tokenize(maybe_type.value(), '|')) {
          StringPiece trimmed_part = util::TrimWhitespace(part);
          if (trimmed_part == "public") {
            policies.push_back(Overlayable::Policy::kPublic);
          } else if (trimmed_part == "product") {
            policies.push_back(Overlayable::Policy::kProduct);
          } else if (trimmed_part == "product_services") {
            policies.push_back(Overlayable::Policy::kProductServices);
          } else if (trimmed_part == "system") {
            policies.push_back(Overlayable::Policy::kSystem);
          } else if (trimmed_part == "vendor") {
            policies.push_back(Overlayable::Policy::kVendor);
          } else {
            diag_->Error(DiagMessage(out_resource->source)
                             << "<policy> has unsupported type '" << trimmed_part << "'");
            error = true;
            continue;
          }
        }
      }
    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
      diag_->Error(DiagMessage(item_source) << "invalid element <" << element_name << "> in "
                                            << " <overlayable>");
      error = true;
      break;
    }
  }

  return !error;
}

bool ResourceParser::ParseOverlayableItem(xml::XmlPullParser* parser,
                                          const std::vector<Overlayable::Policy>& policies,
                                          const std::string& comment,
                                          ParsedResource* out_resource) {
  const Source item_source = source_.WithLine(parser->line_number());

  Maybe<StringPiece> maybe_name = xml::FindNonEmptyAttribute(parser, "name");
  if (!maybe_name) {
    diag_->Error(DiagMessage(item_source)
                     << "<item> within an <overlayable> tag must have a 'name' attribute");
        error = true;
        continue;
    return false;
  }

  Maybe<StringPiece> maybe_type = xml::FindNonEmptyAttribute(parser, "type");
  if (!maybe_type) {
    diag_->Error(DiagMessage(item_source)
                     << "<item> within an <overlayable> tag must have a 'type' attribute");
        error = true;
        continue;
    return false;
  }

  const ResourceType* type = ParseResourceType(maybe_type.value());
@@ -1107,27 +1160,34 @@ bool ResourceParser::ParseOverlayable(xml::XmlPullParser* parser, ParsedResource
    diag_->Error(DiagMessage(out_resource->source)
                     << "invalid resource type '" << maybe_type.value()
                     << "' in <item> within an <overlayable>");
        error = true;
        continue;
    return false;
  }

  ParsedResource child_resource;
  child_resource.name.type = *type;
  child_resource.name.entry = maybe_name.value().to_string();
  child_resource.source = item_source;
      child_resource.overlayable = true;

  if (policies.empty()) {
    Overlayable overlayable;
    overlayable.source = item_source;
    overlayable.comment = comment;
    child_resource.overlayable_declarations.push_back(overlayable);
  } else {
    for (Overlayable::Policy policy : policies) {
      Overlayable overlayable;
      overlayable.policy = policy;
      overlayable.source = item_source;
      overlayable.comment = comment;
      child_resource.overlayable_declarations.push_back(overlayable);
    }
  }

  if (options_.visibility) {
    child_resource.visibility_level = options_.visibility.value();
  }
  out_resource->child_resources.push_back(std::move(child_resource));

      xml::XmlPullParser::SkipCurrentElement(parser);
    } else if (!ShouldIgnoreElement(element_namespace, element_name)) {
      diag_->Error(DiagMessage(item_source) << ":" << element_name << ">");
      error = true;
    }
  }
  return !error;
  return true;
}

bool ResourceParser::ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource) {
+4 −0
Original line number Diff line number Diff line
@@ -96,6 +96,10 @@ class ResourceParser {
  bool ParseSymbolImpl(xml::XmlPullParser* parser, ParsedResource* out_resource);
  bool ParseSymbol(xml::XmlPullParser* parser, ParsedResource* out_resource);
  bool ParseOverlayable(xml::XmlPullParser* parser, ParsedResource* out_resource);
  bool ParseOverlayableItem(xml::XmlPullParser* parser,
                            const std::vector<Overlayable::Policy>& policies,
                            const std::string& comment,
                            ParsedResource* out_resource);
  bool ParseAddResource(xml::XmlPullParser* parser, ParsedResource* out_resource);
  bool ParseAttr(xml::XmlPullParser* parser, ParsedResource* out_resource);
  bool ParseAttrImpl(xml::XmlPullParser* parser, ParsedResource* out_resource, bool weak);
+212 −27
Original line number Diff line number Diff line
@@ -891,63 +891,248 @@ TEST_F(ResourceParserTest, ParsePlatformIndependentNewline) {
  ASSERT_TRUE(TestParse(R"(<string name="foo">%1$s %n %2$s</string>)"));
}

TEST_F(ResourceParserTest, ParseOverlayableTagWithSystemPolicy) {
TEST_F(ResourceParserTest, ParseOverlayable) {
  std::string input = R"(<overlayable />)";
  EXPECT_TRUE(TestParse(input));

  input = R"(
      <overlayable>
        <item type="string" name="foo" />
        <item type="drawable" name="bar" />
      </overlayable>)";
  ASSERT_TRUE(TestParse(input));

  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);

  search_result = table_.FindResource(test::ParseNameOrDie("drawable/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);
}

TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
  std::string input = R"(<overlayable />)";
  EXPECT_TRUE(TestParse(input));

  input = R"(
      <overlayable>
        <item type="string" name="foo" />
        <policy type="product">
          <item type="string" name="bar" />
        </policy>
        <policy type="product_services">
          <item type="string" name="baz" />
        </policy>
        <policy type="system">
          <item type="string" name="fiz" />
        </policy>
        <policy type="vendor">
          <item type="string" name="fuz" />
        </policy>
        <policy type="public">
          <item type="string" name="faz" />
        </policy>
      </overlayable>)";
  ASSERT_TRUE(TestParse(input));

  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_FALSE(search_result.value().entry->overlayable_declarations[0].policy);

  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kProduct));

  search_result = table_.FindResource(test::ParseNameOrDie("string/baz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kProductServices));

  search_result = table_.FindResource(test::ParseNameOrDie("string/fiz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kSystem));

  search_result = table_.FindResource(test::ParseNameOrDie("string/fuz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kVendor));

  search_result = table_.FindResource(test::ParseNameOrDie("string/faz"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(1));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kPublic));
}

TEST_F(ResourceParserTest, ParseOverlayableBadPolicyError) {
  std::string input = R"(
      <overlayable policy="illegal_policy">
      <overlayable>
        <policy type="illegal_policy">
          <item type="string" name="foo" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable policy="system">
      <overlayable>
        <policy type="product">
          <item name="foo" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable policy="system">
        <item type="attr" />
      <overlayable>
        <policy type="vendor">
          <item type="string" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));
}

  input = R"(
      <overlayable policy="system">
        <item type="bad_type" name="foo" />
TEST_F(ResourceParserTest, ParseOverlayableMultiplePolicy) {
  std::string input = R"(
      <overlayable>
        <policy type="vendor|product_services">
          <item type="string" name="foo" />
        </policy>
        <policy type="product|system">
          <item type="string" name="bar" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));
  ASSERT_TRUE(TestParse(input));

  input = R"(<overlayable policy="system" />)";
  EXPECT_TRUE(TestParse(input));
  auto search_result = table_.FindResource(test::ParseNameOrDie("string/foo"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kVendor));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
              Eq(Overlayable::Policy::kProductServices));

  input = R"(<overlayable />)";
  EXPECT_TRUE(TestParse(input));
  search_result = table_.FindResource(test::ParseNameOrDie("string/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations.size(), Eq(2));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[0].policy,
              Eq(Overlayable::Policy::kProduct));
  EXPECT_THAT(search_result.value().entry->overlayable_declarations[1].policy,
              Eq(Overlayable::Policy::kSystem));
}

TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
  std::string input = R"(
      <overlayable>
        <item type="string" name="foo" />
        <item type="string" name="foo" />
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable policy="system">
      <overlayable>
        <item type="string" name="foo" />
      </overlayable>
      <overlayable>
        <item type="string" name="foo" />
        <item type="dimen" name="foo" />
      </overlayable>)";
  ASSERT_TRUE(TestParse(input));
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable">
        <policy type="product">
          <item type="string" name="foo" />
          <item type="string" name="foo" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
      <overlayable>
        <item type="string" name="bar" />
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
      </overlayable>

      <overlayable>
        <policy type="product">
          <item type="string" name="foo" />
        </policy>
      </overlayable>)";
  ASSERT_TRUE(TestParse(input));
  EXPECT_FALSE(TestParse(input));
}

  Maybe<ResourceTable::SearchResult> search_result =
      table_.FindResource(test::ParseNameOrDie("string/bar"));
  ASSERT_TRUE(search_result);
  ASSERT_THAT(search_result.value().entry, NotNull());
  EXPECT_THAT(search_result.value().entry->visibility.level, Eq(Visibility::Level::kUndefined));
  EXPECT_TRUE(search_result.value().entry->overlayable);
TEST_F(ResourceParserTest, PolicyAndNonPolicyOverlayableError) {
  std::string input = R"(
        <overlayable policy="product">
          <item type="string" name="foo" />
        </overlayable>
        <overlayable policy="">
          <item type="string" name="foo" />
        </overlayable>)";
  EXPECT_FALSE(TestParse(input));

  input = R"(
        <overlayable policy="">
          <item type="string" name="foo" />
        </overlayable>
        <overlayable policy="product">
          <item type="string" name="foo" />
        </overlayable>)";
  EXPECT_FALSE(TestParse(input));
}

TEST_F(ResourceParserTest, DuplicateOverlayableIsError) {
TEST_F(ResourceParserTest, DuplicateOverlayableMultiplePolicyError) {
  std::string input = R"(
      <overlayable>
        <policy type="vendor|product">
          <item type="string" name="foo" />
        </policy>
      </overlayable>
      <overlayable>
        <policy type="product_services|vendor">
          <item type="string" name="foo" />
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));
}

TEST_F(ResourceParserTest, NestPolicyInOverlayableError) {
  std::string input = R"(
      <overlayable>
        <policy type="vendor|product">
          <policy type="product_services">
            <item type="string" name="foo" />
          </policy>
        </policy>
      </overlayable>)";
  EXPECT_FALSE(TestParse(input));
}
+27 −12
Original line number Diff line number Diff line
@@ -625,17 +625,17 @@ bool ResourceTable::SetAllowNewImpl(const ResourceNameRef& name, const AllowNew&
  return true;
}

bool ResourceTable::SetOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
bool ResourceTable::AddOverlayable(const ResourceNameRef& name, const Overlayable& overlayable,
                                   IDiagnostics* diag) {
  return SetOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
  return AddOverlayableImpl(name, overlayable, ResourceNameValidator, diag);
}

bool ResourceTable::SetOverlayableMangled(const ResourceNameRef& name,
bool ResourceTable::AddOverlayableMangled(const ResourceNameRef& name,
                                          const Overlayable& overlayable, IDiagnostics* diag) {
  return SetOverlayableImpl(name, overlayable, SkipNameValidator, diag);
  return AddOverlayableImpl(name, overlayable, SkipNameValidator, diag);
}

bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
bool ResourceTable::AddOverlayableImpl(const ResourceNameRef& name, const Overlayable& overlayable,
                                       NameValidator name_validator, IDiagnostics* diag) {
  CHECK(diag != nullptr);

@@ -646,13 +646,28 @@ bool ResourceTable::SetOverlayableImpl(const ResourceNameRef& name, const Overla
  ResourceTablePackage* package = FindOrCreatePackage(name.package);
  ResourceTableType* type = package->FindOrCreateType(name.type);
  ResourceEntry* entry = type->FindOrCreateEntry(name.entry);
  if (entry->overlayable) {

  for (auto& overlayable_declaration : entry->overlayable_declarations) {
    // An overlayable resource cannot be declared twice with the same policy
    if (overlayable.policy == overlayable_declaration.policy) {
      diag->Error(DiagMessage(overlayable.source)
                    << "duplicate overlayable declaration for resource '" << name << "'");
    diag->Error(DiagMessage(entry->overlayable.value().source) << "previous declaration here");
      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
      return false;
    }
  entry->overlayable = overlayable;

    // An overlayable resource cannot be declared once with a policy and without a policy because
    // the policy becomes unused
    if (!overlayable.policy || !overlayable_declaration.policy) {
      diag->Error(DiagMessage(overlayable.source)
                    << "overlayable resource '" << name << "'"
                    << " declared once with a policy and once with no policy");
      diag->Error(DiagMessage(overlayable_declaration.source) << "previous declaration here");
      return false;
    }
  }

  entry->overlayable_declarations.push_back(overlayable);
  return true;
}

@@ -688,7 +703,7 @@ std::unique_ptr<ResourceTable> ResourceTable::Clone() const {
        new_entry->id = entry->id;
        new_entry->visibility = entry->visibility;
        new_entry->allow_new = entry->allow_new;
        new_entry->overlayable = entry->overlayable;
        new_entry->overlayable_declarations = entry->overlayable_declarations;

        for (const auto& config_value : entry->values) {
          ResourceConfigValue* new_value =
Loading