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

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

Add enforcement of overlayable targetName

Adds android:targetName to the overlay manifest attributes and
PackageParser reads the name into PackageInfo. Specifying
android:targetName on an overlay allows the overlay to be associated
with a particular set of overlayable resources. The overlay can only
override the values of the resources defined within the target
overlayable element.

Test: idmap2_tests
Bug: 119390855
Bug: 110869880

Change-Id: I1128274af4cae983f61ae15cdfcbface63233ff2
parent a117526b
Loading
Loading
Loading
Loading
+13 −35
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "idmap2/CommandLineOptions.h"
#include "idmap2/FileUtils.h"
#include "idmap2/Idmap.h"
#include "idmap2/ResourceUtils.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"

@@ -36,14 +37,13 @@

using android::idmap2::CommandLineOptions;
using android::idmap2::Idmap;
using android::idmap2::MemoryChunk;
using android::idmap2::PoliciesToBitmask;
using android::idmap2::PolicyBitmask;
using android::idmap2::PolicyFlags;
using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::idmap2::utils::ExtractOverlayManifestInfo;
using android::idmap2::utils::FindFiles;
using android::idmap2::utils::OverlayManifestInfo;

namespace {

@@ -138,46 +138,23 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {

  std::vector<InputOverlay> interesting_apks;
  for (const std::string& path : *apk_paths) {
    std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
    if (!zip) {
      out_error << "error: failed to open " << path << " as a zip file" << std::endl;
    Result<OverlayManifestInfo> overlay_info =
        ExtractOverlayManifestInfo(path, out_error,
                                   /* assert_overlay */ false);
    if (!overlay_info) {
      return false;
    }

    std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
    if (!entry) {
      out_error << "error: failed to uncompress AndroidManifest.xml from " << path << std::endl;
      return false;
    }

    std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
    if (!xml) {
      out_error << "error: failed to parse AndroidManifest.xml from " << path << std::endl;
      continue;
    }

    const auto tag = xml->FindTag("overlay");
    if (!tag) {
      continue;
    }

    auto iter = tag->find("isStatic");
    if (iter == tag->end() || std::stoul(iter->second) == 0U) {
      continue;
    }

    iter = tag->find("targetPackage");
    if (iter == tag->end() || iter->second != target_package_name) {
    if (!overlay_info->is_static) {
      continue;
    }

    iter = tag->find("priority");
    if (iter == tag->end()) {
    if (overlay_info->target_package.empty() ||
        overlay_info->target_package != target_package_name) {
      continue;
    }

    const int priority = std::stoi(iter->second);
    if (priority < 0) {
    if (overlay_info->priority < 0) {
      continue;
    }

@@ -203,7 +180,8 @@ bool Scan(const std::vector<std::string>& args, std::ostream& out_error) {
    std::string idmap_path = Idmap::CanonicalIdmapPathFor(output_directory, path);

    // Sort the static overlays in ascending priority order
    InputOverlay input{path, idmap_path, priority, override_policies, ignore_overlayable};
    InputOverlay input{path, idmap_path, overlay_info->priority, override_policies,
                       ignore_overlayable};
    interesting_apks.insert(
        std::lower_bound(interesting_apks.begin(), interesting_apks.end(), input), input);
  }
+14 −0
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_
#define IDMAP2_INCLUDE_IDMAP2_RESOURCEUTILS_H_

#include <optional>
#include <ostream>
#include <string>

#include "android-base/macros.h"
@@ -24,9 +26,21 @@

#include "idmap2/Idmap.h"
#include "idmap2/Result.h"
#include "idmap2/ZipFile.h"

namespace android::idmap2::utils {

struct OverlayManifestInfo {
  std::string target_package;  // NOLINT(misc-non-private-member-variables-in-classes)
  std::string target_name;     // NOLINT(misc-non-private-member-variables-in-classes)
  bool is_static;              // NOLINT(misc-non-private-member-variables-in-classes)
  int priority = -1;           // NOLINT(misc-non-private-member-variables-in-classes)
};

Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
                                                       std::ostream& out_error,
                                                       bool assert_overlay = true);

Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, ResourceId resid);

}  // namespace android::idmap2::utils
+20 −8
Original line number Diff line number Diff line
@@ -274,17 +274,24 @@ std::unique_ptr<const Idmap> Idmap::FromBinaryStream(std::istream& stream,
  return std::move(idmap);
}

bool CheckOverlayable(const LoadedPackage& target_package, PolicyBitmask fulfilled_polices,
                      ResourceId resid) {
  const OverlayableInfo* info = target_package.GetOverlayableInfo(resid);
  if (info == nullptr) {
bool CheckOverlayable(const LoadedPackage& target_package,
                      const utils::OverlayManifestInfo& overlay_info,
                      const PolicyBitmask& fulfilled_polices, const ResourceId& resid) {
  const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(resid);
  if (overlayable_info == nullptr) {
    // If the resource does not have an overlayable definition, allow the resource to be overlaid.
    // Once overlayable enforcement is turned on, this check will return false.
    return true;
  }

  if (!overlay_info.target_name.empty() && overlay_info.target_name != overlayable_info->name) {
    // If the overlay supplies a target overlayable name, the resource must belong to the
    // overlayable defined with the specified name to be overlaid.
    return false;
  }

  // Enforce policy restrictions if the resource is declared as overlayable.
  return (info->policy_flags & fulfilled_polices) != 0;
  return (overlayable_info->policy_flags & fulfilled_polices) != 0;
}

std::unique_ptr<const Idmap> Idmap::FromApkAssets(
@@ -339,6 +346,12 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(
    return nullptr;
  }

  Result<utils::OverlayManifestInfo> overlay_info =
      utils::ExtractOverlayManifestInfo(overlay_apk_path, out_error);
  if (!overlay_info) {
    return nullptr;
  }

  std::unique_ptr<IdmapHeader> header(new IdmapHeader());
  header->magic_ = kIdmapMagic;
  header->version_ = kIdmapCurrentVersion;
@@ -393,9 +406,8 @@ std::unique_ptr<const Idmap> Idmap::FromApkAssets(
      continue;
    }

    if (enforce_overlayable && !CheckOverlayable(*target_pkg, fulfilled_policies, target_resid)) {
      // The resources must be defined as overlayable and the overlay must fulfill at least one
      // policy enforced on the overlayable resource
    if (enforce_overlayable &&
        !CheckOverlayable(*target_pkg, *overlay_info, fulfilled_policies, target_resid)) {
      LOG(WARNING) << "overlay \"" << overlay_apk_path << "\" is not allowed to overlay resource \""
                   << full_name << "\"" << std::endl;
      continue;
+65 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <memory>
#include <string>

#include "androidfw/StringPiece.h"
@@ -21,8 +22,13 @@

#include "idmap2/ResourceUtils.h"
#include "idmap2/Result.h"
#include "idmap2/Xml.h"
#include "idmap2/ZipFile.h"

using android::StringPiece16;
using android::idmap2::Result;
using android::idmap2::Xml;
using android::idmap2::ZipFile;
using android::util::Utf16ToUtf8;

namespace android::idmap2::utils {
@@ -47,4 +53,63 @@ Result<std::string> WARN_UNUSED ResToTypeEntryName(const AssetManager2& am, Reso
  return {out};
}

Result<OverlayManifestInfo> ExtractOverlayManifestInfo(const std::string& path,
                                                       std::ostream& out_error,
                                                       bool assert_overlay) {
  std::unique_ptr<const ZipFile> zip = ZipFile::Open(path);
  if (!zip) {
    out_error << "error: failed to open " << path << " as a zip file" << std::endl;
    return kResultError;
  }

  std::unique_ptr<const MemoryChunk> entry = zip->Uncompress("AndroidManifest.xml");
  if (!entry) {
    out_error << "error: failed to uncompress AndroidManifest.xml from " << path << std::endl;
    return kResultError;
  }

  std::unique_ptr<const Xml> xml = Xml::Create(entry->buf, entry->size);
  if (!xml) {
    out_error << "error: failed to parse AndroidManifest.xml from " << path << std::endl;
    return kResultError;
  }

  OverlayManifestInfo info{};
  const auto tag = xml->FindTag("overlay");
  if (!tag) {
    if (assert_overlay) {
      out_error << "error: <overlay> missing from AndroidManifest.xml of " << path << std::endl;
      return kResultError;
    }
    return info;
  }

  auto iter = tag->find("targetPackage");
  if (iter == tag->end()) {
    if (assert_overlay) {
      out_error << "error: android:targetPackage missing from <overlay> of " << path << std::endl;
      return kResultError;
    }
  } else {
    info.target_package = iter->second;
  }

  iter = tag->find("targetName");
  if (iter != tag->end()) {
    info.target_name = iter->second;
  }

  iter = tag->find("isStatic");
  if (iter != tag->end()) {
    info.is_static = std::stoul(iter->second) != 0U;
  }

  iter = tag->find("priority");
  if (iter != tag->end()) {
    info.priority = std::stoi(iter->second);
  }

  return info;
}

}  // namespace android::idmap2::utils
+15 −12
Original line number Diff line number Diff line
@@ -111,43 +111,46 @@ TEST(BinaryStreamVisitorTests, CreateIdmapFromApkAssetsInteropWithLoadedIdmap) {
  success = LoadedIdmap::Lookup(header, 0x0002, &entry);  // string/c
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0003, &entry);  // string/not_overlayable
  success = LoadedIdmap::Lookup(header, 0x0003, &entry);  // string/other
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0004, &entry);  // string/policy_product
  success = LoadedIdmap::Lookup(header, 0x0004, &entry);  // string/not_overlayable
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0005, &entry);  // string/policy_public
  success = LoadedIdmap::Lookup(header, 0x0005, &entry);  // string/policy_product
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0006, &entry);  // string/policy_system
  success = LoadedIdmap::Lookup(header, 0x0006, &entry);  // string/policy_public
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0007, &entry);  // string/policy_system_vendor
  success = LoadedIdmap::Lookup(header, 0x0007, &entry);  // string/policy_system
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0008, &entry);  // string/str1
  success = LoadedIdmap::Lookup(header, 0x0008, &entry);  // string/policy_system_vendor
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x0009, &entry);  // string/str1
  ASSERT_TRUE(success);
  ASSERT_EQ(entry, 0x0000);

  success = LoadedIdmap::Lookup(header, 0x0009, &entry);  // string/str2
  success = LoadedIdmap::Lookup(header, 0x000a, &entry);  // string/str2
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x000a, &entry);  // string/str3
  success = LoadedIdmap::Lookup(header, 0x000b, &entry);  // string/str3
  ASSERT_TRUE(success);
  ASSERT_EQ(entry, 0x0001);

  success = LoadedIdmap::Lookup(header, 0x000b, &entry);  // string/str4
  success = LoadedIdmap::Lookup(header, 0x000c, &entry);  // string/str4
  ASSERT_TRUE(success);
  ASSERT_EQ(entry, 0x0002);

  success = LoadedIdmap::Lookup(header, 0x000c, &entry);  // string/x
  success = LoadedIdmap::Lookup(header, 0x000d, &entry);  // string/x
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x000d, &entry);  // string/y
  success = LoadedIdmap::Lookup(header, 0x000e, &entry);  // string/y
  ASSERT_FALSE(success);

  success = LoadedIdmap::Lookup(header, 0x000e, &entry);  // string/z
  success = LoadedIdmap::Lookup(header, 0x000f, &entry);  // string/z
  ASSERT_FALSE(success);
}

Loading