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
diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp
index a269ee9..b1ed42a 100644
--- a/cmds/idmap2/idmap2/Scan.cpp
+++ b/cmds/idmap2/idmap2/Scan.cpp
@@ -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 @@
 
   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;
+    if (!overlay_info->is_static) {
       continue;
     }
 
-    const auto tag = xml->FindTag("overlay");
-    if (!tag) {
+    if (overlay_info->target_package.empty() ||
+        overlay_info->target_package != target_package_name) {
       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) {
-      continue;
-    }
-
-    iter = tag->find("priority");
-    if (iter == tag->end()) {
-      continue;
-    }
-
-    const int priority = std::stoi(iter->second);
-    if (priority < 0) {
+    if (overlay_info->priority < 0) {
       continue;
     }
 
@@ -203,7 +180,8 @@
     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);
   }
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 323796b..22827ac 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -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
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 2890ae1..5d449e9 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -274,17 +274,24 @@
   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 @@
     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 @@
       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;
diff --git a/cmds/idmap2/libidmap2/ResourceUtils.cpp b/cmds/idmap2/libidmap2/ResourceUtils.cpp
index 04e0ec7..7a984f3 100644
--- a/cmds/idmap2/libidmap2/ResourceUtils.cpp
+++ b/cmds/idmap2/libidmap2/ResourceUtils.cpp
@@ -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 @@
   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
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index 35ec1ff..0e0e25f 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -111,43 +111,46 @@
   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);
 }
 
diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
index 0c8f164..4334fa6 100644
--- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp
+++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp
@@ -115,9 +115,9 @@
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
   ASSERT_NE(result->stdout.find("0x7f010000 -> 0x7f010000 integer/int1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f020008 -> 0x7f020000 string/str1"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f02000a -> 0x7f020001 string/str3"), std::string::npos);
-  ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020002 string/str4"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f020009 -> 0x7f020000 string/str1"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000b -> 0x7f020001 string/str3"), std::string::npos);
+  ASSERT_NE(result->stdout.find("0x7f02000c -> 0x7f020002 string/str4"), std::string::npos);
   ASSERT_EQ(result->stdout.find("00000210:     007f  target package id"), std::string::npos);
 
   // clang-format off
@@ -251,7 +251,7 @@
                           "lookup",
                           "--idmap-path", GetIdmapPath(),
                           "--config", "",
-                          "--resid", "0x7f020008"});  // string/str1
+                          "--resid", "0x7f020009"});  // string/str1
   // clang-format on
   ASSERT_THAT(result, NotNull());
   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index c6eb71c..df28918 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -191,7 +191,7 @@
   ASSERT_THAT(idmap->GetHeader(), NotNull());
   ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
   ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x01U);
-  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xca2093da);
+  ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0xab7cf70d);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0xd470336b);
   ASSERT_EQ(idmap->GetHeader()->GetTargetPath().to_string(), target_apk_path);
   ASSERT_EQ(idmap->GetHeader()->GetOverlayPath(), overlay_apk_path);
@@ -217,7 +217,7 @@
   ASSERT_EQ(types[1]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetOverlayTypeId(), 0x02U);
   ASSERT_EQ(types[1]->GetEntryCount(), 4U);
-  ASSERT_EQ(types[1]->GetEntryOffset(), 8U);
+  ASSERT_EQ(types[1]->GetEntryOffset(), 9U);
   ASSERT_EQ(types[1]->GetEntry(0), 0x0000U);
   ASSERT_EQ(types[1]->GetEntry(1), kNoEntry);
   ASSERT_EQ(types[1]->GetEntry(2), 0x0001U);
@@ -254,7 +254,7 @@
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
   ASSERT_EQ(types[0]->GetEntryCount(), 3U);
-  ASSERT_EQ(types[0]->GetEntryOffset(), 5U);
+  ASSERT_EQ(types[0]->GetEntryOffset(), 6U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/policy_public
   ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/policy_system
   ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_system_vendor
@@ -290,13 +290,14 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 5U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 6U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);   // string/not_overlayable
-  ASSERT_EQ(types[0]->GetEntry(1), kNoEntry);  // string/policy_product
-  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);   // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);   // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);   // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(1), kNoEntry);  // string/other
+  ASSERT_EQ(types[0]->GetEntry(2), kNoEntry);  // string/policy_product
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);   // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);   // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);   // string/policy_system_vendor
 }
 
 TEST(IdmapTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalidIgnoreOverlayable) {
@@ -329,13 +330,14 @@
 
   ASSERT_EQ(types[0]->GetTargetTypeId(), 0x02U);
   ASSERT_EQ(types[0]->GetOverlayTypeId(), 0x01U);
-  ASSERT_EQ(types[0]->GetEntryCount(), 5U);
+  ASSERT_EQ(types[0]->GetEntryCount(), 6U);
   ASSERT_EQ(types[0]->GetEntryOffset(), 3U);
   ASSERT_EQ(types[0]->GetEntry(0), 0x0000U);  // string/not_overlayable
-  ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/policy_product
-  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_public
-  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_system
-  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_system_vendor
+  ASSERT_EQ(types[0]->GetEntry(1), 0x0001U);  // string/other
+  ASSERT_EQ(types[0]->GetEntry(2), 0x0002U);  // string/policy_product
+  ASSERT_EQ(types[0]->GetEntry(3), 0x0003U);  // string/policy_public
+  ASSERT_EQ(types[0]->GetEntry(4), 0x0004U);  // string/policy_system
+  ASSERT_EQ(types[0]->GetEntry(5), 0x0005U);  // string/policy_system_vendor
 }
 
 TEST(IdmapTests, FailToCreateIdmapFromApkAssetsIfPathTooLong) {
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index b58c61a..b1ca125 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -52,7 +52,7 @@
 
   ASSERT_NE(stream.str().find("00000000: 504d4449  magic\n"), std::string::npos);
   ASSERT_NE(stream.str().find("00000004: 00000001  version\n"), std::string::npos);
-  ASSERT_NE(stream.str().find("00000008: ca2093da  target crc\n"), std::string::npos);
+  ASSERT_NE(stream.str().find("00000008: ab7cf70d  target crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000000c: d470336b  overlay crc\n"), std::string::npos);
   ASSERT_NE(stream.str().find("0000021c: 00000000  0x7f010000 -> 0x7f010000 integer/int1\n"),
             std::string::npos);
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
index 977cd97..ae687d3 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/AndroidManifest.xml
@@ -17,5 +17,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="test.overlay.system.invalid">
     <overlay
-        android:targetPackage="test.target" />
+        android:targetPackage="test.target"
+        android:targetName="TestResources"/>
 </manifest>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
index 5127707..af1bea1 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/res/values/values.xml
@@ -20,10 +20,13 @@
     <string name="policy_system_vendor">policy_system_vendor</string>
     <string name="policy_public">policy_public</string>
 
-    <!-- It also requests to overlay a resource that belongs to a policy the overlay does not
-         fulfill.-->
+    <!-- Requests to overlay a resource that belongs to a policy the overlay does not fulfill. -->
     <string name="policy_product">policy_product</string>
 
-    <!-- It also requests to overlay a resource that is not declared as overlayable.-->
+    <!-- Requests to overlay a resource that is not declared as overlayable. -->
     <string name="not_overlayable">not_overlayable</string>
+
+    <!-- Requests to overlay a resource that is defined in an overlayable with a name other than
+         the targetName in the manifest. -->
+    <string name="other">other</string>
 </resources>
diff --git a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
index c367f82..710ed90 100644
--- a/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
+++ b/cmds/idmap2/tests/data/system-overlay-invalid/system-overlay-invalid.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/res/values/overlayable.xml b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
index de19e6f..02d2563 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -45,4 +45,8 @@
         <item type="string" name="policy_public" />
     </policy>
 </overlayable>
+
+<overlayable name="OtherResources">
+    <item type="string" name="other" />
+</overlayable>
 </resources>
\ No newline at end of file
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index ef9012e..0d337f3 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -33,4 +33,6 @@
     <string name="policy_system_vendor">policy_system_vendor</string>
     <string name="policy_product">policy_product</string>
     <string name="policy_public">policy_public</string>
+
+    <item type="string" name="other" />
 </resources>
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 9a6220d..ecbe875 100644
--- a/cmds/idmap2/tests/data/target/target.apk
+++ b/cmds/idmap2/tests/data/target/target.apk
Binary files differ
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index b8d7889..3a77a6a 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -23,9 +23,6 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Overall information about the contents of a package.  This corresponds
  * to all of the information collected from AndroidManifest.xml.
@@ -374,6 +371,14 @@
     public String overlayTarget;
 
     /**
+     * What overlayable set of elements package, if any, this package will overlay.
+     *
+     * Overlayable name defined within the target package, or null.
+     * @hide
+     */
+    public String overlayTargetName;
+
+    /**
      * The overlay category, if any, of this package
      *
      * @hide
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index dbf3574..5e15b00 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -689,6 +689,7 @@
         pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.requiredAccountType = p.mRequiredAccountType;
         pi.overlayTarget = p.mOverlayTarget;
+        pi.overlayTargetName = p.mOverlayTargetName;
         pi.overlayCategory = p.mOverlayCategory;
         pi.overlayPriority = p.mOverlayPriority;
         pi.mOverlayIsStatic = p.mOverlayIsStatic;
@@ -2122,6 +2123,8 @@
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay);
                 pkg.mOverlayTarget = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
+                pkg.mOverlayTargetName = sa.getString(
+                        com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetName);
                 pkg.mOverlayCategory = sa.getString(
                         com.android.internal.R.styleable.AndroidManifestResourceOverlay_category);
                 pkg.mOverlayPriority = sa.getInt(
@@ -6730,6 +6733,7 @@
         public String mRequiredAccountType;
 
         public String mOverlayTarget;
+        public String mOverlayTargetName;
         public String mOverlayCategory;
         public int mOverlayPriority;
         public boolean mOverlayIsStatic;
@@ -7253,6 +7257,7 @@
             mRestrictedAccountType = dest.readString();
             mRequiredAccountType = dest.readString();
             mOverlayTarget = dest.readString();
+            mOverlayTargetName = dest.readString();
             mOverlayCategory = dest.readString();
             mOverlayPriority = dest.readInt();
             mOverlayIsStatic = (dest.readInt() == 1);
@@ -7380,6 +7385,7 @@
             dest.writeString(mRestrictedAccountType);
             dest.writeString(mRequiredAccountType);
             dest.writeString(mOverlayTarget);
+            dest.writeString(mOverlayTargetName);
             dest.writeString(mOverlayCategory);
             dest.writeInt(mOverlayPriority);
             dest.writeInt(mOverlayIsStatic ? 1 : 0);
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index b15f72e..8ef264a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2797,6 +2797,9 @@
         <attr name="requiredSystemPropertyName" format="string" />
         <!-- @hide This shouldn't be public. -->
         <attr name="requiredSystemPropertyValue" format="string" />
+
+        <!-- The name of the overlayable whose resources will be overlaid. -->
+        <attr name="targetName" />
     </declare-styleable>
 
     <!-- Declaration of an {@link android.content.Intent} object in XML.  May
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index ce59e6e..85909d5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -276,6 +276,7 @@
         assertEquals(a.mRestrictedAccountType, b.mRestrictedAccountType);
         assertEquals(a.mRequiredAccountType, b.mRequiredAccountType);
         assertEquals(a.mOverlayTarget, b.mOverlayTarget);
+        assertEquals(a.mOverlayTargetName, b.mOverlayTargetName);
         assertEquals(a.mOverlayCategory, b.mOverlayCategory);
         assertEquals(a.mOverlayPriority, b.mOverlayPriority);
         assertEquals(a.mOverlayIsStatic, b.mOverlayIsStatic);
@@ -545,6 +546,7 @@
 
         pkg.mOverlayCategory = "foo24";
         pkg.mOverlayIsStatic = true;
+        pkg.mOverlayTargetName = "foo26";
 
         pkg.baseHardwareAccelerated = true;
         pkg.coreApp = true;