Actor signature overlayable policy

There are cases where an app can ship overlays for itself,
but the "signature" policy as described would open up
a vulnerability by allowing the system actor to create
and sign any arbitrary overlay that will apply to the target.

To prevent this, redefine "signature" as target package only,
and introduce "actor" for checking against the actor signature.
Any app that wishes to use both can include both policies.

Bug: 130563563

Test: m aapt2_tests idmapt2_tests and run from host test output
Test: atest libandroidfw_tests

Change-Id: I1c583a5b37f4abbeb18fc6a35c502377d8977a41
diff --git a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
index 85b5eff..02b27a8 100644
--- a/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
+++ b/cmds/idmap2/idmap2d/aidl/android/os/OverlayablePolicy.aidl
@@ -28,4 +28,5 @@
   const int SIGNATURE = 0x00000010;
   const int ODM_PARTITION = 0x00000020;
   const int OEM_PARTITION = 0x00000040;
+  const int ACTOR_SIGNATURE = 0x00000080;
 }
diff --git a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
index 9f9b836..4973b76 100644
--- a/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
+++ b/cmds/idmap2/libidmap2_policies/include/idmap2/Policies.h
@@ -29,6 +29,7 @@
 
 namespace android::idmap2::policy {
 
+constexpr const char* kPolicyActor = "actor";
 constexpr const char* kPolicyOdm = "odm";
 constexpr const char* kPolicyOem = "oem";
 constexpr const char* kPolicyProduct = "product";
@@ -37,8 +38,9 @@
 constexpr const char* kPolicySystem = "system";
 constexpr const char* kPolicyVendor = "vendor";
 
-inline static const std::array<std::pair<StringPiece, PolicyFlags>, 7> kPolicyStringToFlag = {
-    std::pair{kPolicyOdm, PolicyFlags::ODM_PARTITION},
+inline static const std::array<std::pair<StringPiece, PolicyFlags>, 8> kPolicyStringToFlag = {
+    std::pair{kPolicyActor, PolicyFlags::ACTOR_SIGNATURE},
+    {kPolicyOdm, PolicyFlags::ODM_PARTITION},
     {kPolicyOem, PolicyFlags::OEM_PARTITION},
     {kPolicyProduct, PolicyFlags::PRODUCT_PARTITION},
     {kPolicyPublic, PolicyFlags::PUBLIC},
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index 76c6eaf..87da36c 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -279,17 +279,25 @@
 
   const auto& target_entries = data->GetTargetEntries();
   ASSERT_EQ(target_entries.size(), 4U);
-  ASSERT_TARGET_ENTRY(target_entries[0], 0x7f010000, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00010000);
-  ASSERT_TARGET_ENTRY(target_entries[1], 0x7f02000c, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020000);
-  ASSERT_TARGET_ENTRY(target_entries[2], 0x7f02000e, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020001);
-  ASSERT_TARGET_ENTRY(target_entries[3], 0x7f02000f, Res_value::TYPE_DYNAMIC_REFERENCE, 0x00020002);
+  ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1,
+                      Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1);
+  ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE,
+                      R::overlay_shared::string::str1);
+  ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE,
+                      R::overlay_shared::string::str3);
+  ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE,
+                      R::overlay_shared::string::str4);
 
   const auto& overlay_entries = data->GetOverlayEntries();
   ASSERT_EQ(target_entries.size(), 4U);
-  ASSERT_OVERLAY_ENTRY(overlay_entries[0], 0x00010000, 0x7f010000);
-  ASSERT_OVERLAY_ENTRY(overlay_entries[1], 0x00020000, 0x7f02000c);
-  ASSERT_OVERLAY_ENTRY(overlay_entries[2], 0x00020001, 0x7f02000e);
-  ASSERT_OVERLAY_ENTRY(overlay_entries[3], 0x00020002, 0x7f02000f);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[0], R::overlay_shared::integer::int1,
+                       R::target::integer::int1);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[1], R::overlay_shared::string::str1,
+                       R::target::string::str1);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[2], R::overlay_shared::string::str3,
+                       R::target::string::str3);
+  ASSERT_OVERLAY_ENTRY(overlay_entries[3], R::overlay_shared::string::str4,
+                       R::target::string::str4);
 }
 
 TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) {
diff --git a/cmds/idmap2/tests/PoliciesTests.cpp b/cmds/idmap2/tests/PoliciesTests.cpp
index 4b395c7..1b27759 100644
--- a/cmds/idmap2/tests/PoliciesTests.cpp
+++ b/cmds/idmap2/tests/PoliciesTests.cpp
@@ -67,6 +67,14 @@
 
   const auto bitmask10 = PoliciesToBitmaskResult({"system "});
   ASSERT_FALSE(bitmask10);
+
+  const auto bitmask11 = PoliciesToBitmaskResult({"signature"});
+  ASSERT_TRUE(bitmask11);
+  ASSERT_EQ(*bitmask11, PolicyFlags::SIGNATURE);
+
+  const auto bitmask12 = PoliciesToBitmaskResult({"actor"});
+  ASSERT_TRUE(bitmask12);
+  ASSERT_EQ(*bitmask12, PolicyFlags::ACTOR_SIGNATURE);
 }
 
 TEST(PoliciesTests, BitmaskToPolicies) {
@@ -91,6 +99,14 @@
   ASSERT_EQ(policies3[3], "public");
   ASSERT_EQ(policies3[4], "system");
   ASSERT_EQ(policies3[5], "vendor");
+
+  const auto policies4 = BitmaskToPolicies(PolicyFlags::SIGNATURE);
+  ASSERT_EQ(1, policies4.size());
+  ASSERT_EQ(policies4[0], "signature");
+
+  const auto policies5 = BitmaskToPolicies(PolicyFlags::ACTOR_SIGNATURE);
+  ASSERT_EQ(1, policies5.size());
+  ASSERT_EQ(policies5[0], "actor");
 }
 
 }  // namespace android::idmap2
diff --git a/cmds/idmap2/tests/R.h b/cmds/idmap2/tests/R.h
index b2a5891..aed263a 100644
--- a/cmds/idmap2/tests/R.h
+++ b/cmds/idmap2/tests/R.h
@@ -40,16 +40,17 @@
   namespace string {
     constexpr ResourceId not_overlayable = 0x7f020003;
     constexpr ResourceId other = 0x7f020004;
-    constexpr ResourceId policy_odm = 0x7f020005;
-    constexpr ResourceId policy_oem = 0x7f020006;
-    constexpr ResourceId policy_product = 0x7f020007;
-    constexpr ResourceId policy_public = 0x7f020008;
-    constexpr ResourceId policy_signature = 0x7f020009;
-    constexpr ResourceId policy_system = 0x7f02000a;
-    constexpr ResourceId policy_system_vendor = 0x7f02000b;
-    constexpr ResourceId str1 = 0x7f02000c;
-    constexpr ResourceId str3 = 0x7f02000e;
-    constexpr ResourceId str4 = 0x7f02000f;
+    constexpr ResourceId policy_actor = 0x7f020005;
+    constexpr ResourceId policy_odm = 0x7f020006;
+    constexpr ResourceId policy_oem = 0x7f020007;
+    constexpr ResourceId policy_product = 0x7f020008;
+    constexpr ResourceId policy_public = 0x7f020009;
+    constexpr ResourceId policy_signature = 0x7f02000a;
+    constexpr ResourceId policy_system = 0x7f02000b;
+    constexpr ResourceId policy_system_vendor = 0x7f02000c;
+    constexpr ResourceId str1 = 0x7f02000d;
+    constexpr ResourceId str3 = 0x7f02000f;
+    constexpr ResourceId str4 = 0x7f020010;
 
     namespace literal {
       inline const std::string str1 = hexify(R::target::string::str1);
@@ -70,6 +71,17 @@
   }
 }
 
+namespace R::overlay_shared {
+  namespace integer {
+    constexpr ResourceId int1 = 0x00010000;
+  }
+  namespace string {
+    constexpr ResourceId str1 = 0x00020000;
+    constexpr ResourceId str3 = 0x00020001;
+    constexpr ResourceId str4 = 0x00020002;
+  }
+}
+
 namespace R::system_overlay::string {
   constexpr ResourceId policy_public = 0x7f010000;
   constexpr ResourceId policy_system = 0x7f010001;
@@ -79,13 +91,14 @@
 namespace R::system_overlay_invalid::string {
   constexpr ResourceId not_overlayable = 0x7f010000;
   constexpr ResourceId other = 0x7f010001;
-  constexpr ResourceId policy_odm = 0x7f010002;
-  constexpr ResourceId policy_oem = 0x7f010003;
-  constexpr ResourceId policy_product = 0x7f010004;
-  constexpr ResourceId policy_public = 0x7f010005;
-  constexpr ResourceId policy_signature = 0x7f010006;
-  constexpr ResourceId policy_system = 0x7f010007;
-  constexpr ResourceId policy_system_vendor = 0x7f010008;
+  constexpr ResourceId policy_actor = 0x7f010002;
+  constexpr ResourceId policy_odm = 0x7f010003;
+  constexpr ResourceId policy_oem = 0x7f010004;
+  constexpr ResourceId policy_product = 0x7f010005;
+  constexpr ResourceId policy_public = 0x7f010006;
+  constexpr ResourceId policy_signature = 0x7f010007;
+  constexpr ResourceId policy_system = 0x7f010008;
+  constexpr ResourceId policy_system_vendor = 0x7f010009;
 };
 // clang-format on
 
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index 8d0e660..de039f4 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -237,12 +237,15 @@
 
   ASSERT_TRUE(resources) << resources.GetErrorMessage();
   auto& res = *resources;
-  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 9U);
+  ASSERT_EQ(res.GetTargetToOverlayMap().size(), 10U);
   ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
                               R::system_overlay_invalid::string::not_overlayable,
                               false /* rewrite */));
   ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
                               R::system_overlay_invalid::string::other, false /* rewrite */));
+  ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+                              R::system_overlay_invalid::string::policy_actor,
+                              false /* rewrite */));
   ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
                               R::system_overlay_invalid::string::policy_odm, false /* rewrite */));
   ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
@@ -306,12 +309,15 @@
 
     ASSERT_TRUE(resources) << resources.GetErrorMessage();
     auto& res = *resources;
-    ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 9U);
+    ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 10U);
     ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE,
                                 R::system_overlay_invalid::string::not_overlayable,
                                 false /* rewrite */));
     ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE,
                                 R::system_overlay_invalid::string::other, false /* rewrite */));
+    ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE,
+                                R::system_overlay_invalid::string::policy_actor,
+                                false /* rewrite */));
     ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE,
                                 R::system_overlay_invalid::string::policy_odm,
                                 false /* rewrite */));
diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h
index c874dc9..6bc924e 100644
--- a/cmds/idmap2/tests/TestConstants.h
+++ b/cmds/idmap2/tests/TestConstants.h
@@ -19,8 +19,8 @@
 
 namespace android::idmap2::TestConstants {
 
-constexpr const auto TARGET_CRC = 0x76a20829;
-constexpr const auto TARGET_CRC_STRING = "76a20829";
+constexpr const auto TARGET_CRC = 0x41c60c8c;
+constexpr const auto TARGET_CRC_STRING = "41c60c8c";
 
 constexpr const auto OVERLAY_CRC = 0xc054fb26;
 constexpr const auto OVERLAY_CRC_STRING = "c054fb26";
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 9ebfae4..7119d82 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
@@ -25,6 +25,7 @@
     <string name="policy_signature">policy_signature</string>
     <string name="policy_odm">policy_odm</string>
     <string name="policy_oem">policy_oem</string>
+    <string name="policy_actor">policy_actor</string>
 
     <!-- Requests to overlay a resource that is not declared as overlayable. -->
     <string name="not_overlayable">not_overlayable</string>
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 1456e74..bd99098 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 8389f56..ad4cd48 100644
--- a/cmds/idmap2/tests/data/target/res/values/overlayable.xml
+++ b/cmds/idmap2/tests/data/target/res/values/overlayable.xml
@@ -41,6 +41,10 @@
         <item type="string" name="policy_oem" />
     </policy>
 
+    <policy type="actor">
+        <item type="string" name="policy_actor" />
+    </policy>
+
     <!-- Resources publicly overlayable -->
     <policy type="public">
         <item type="string" name="policy_public" />
@@ -63,4 +67,4 @@
         <item type="string" name="other" />
     </policy>
 </overlayable>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/cmds/idmap2/tests/data/target/res/values/values.xml b/cmds/idmap2/tests/data/target/res/values/values.xml
index a892c98..5230e25 100644
--- a/cmds/idmap2/tests/data/target/res/values/values.xml
+++ b/cmds/idmap2/tests/data/target/res/values/values.xml
@@ -36,6 +36,7 @@
     <string name="policy_signature">policy_signature</string>
     <string name="policy_system">policy_system</string>
     <string name="policy_system_vendor">policy_system_vendor</string>
+    <string name="policy_actor">policy_actor</string>
 
     <string name="other">other</string>
 </resources>
diff --git a/cmds/idmap2/tests/data/target/target-no-overlayable.apk b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
index 2eb7c47..58504a7 100644
--- a/cmds/idmap2/tests/data/target/target-no-overlayable.apk
+++ b/cmds/idmap2/tests/data/target/target-no-overlayable.apk
Binary files differ
diff --git a/cmds/idmap2/tests/data/target/target.apk b/cmds/idmap2/tests/data/target/target.apk
index 251cf46..c80e5eb 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/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index 55a6cab..62815dd 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -138,6 +138,7 @@
      *
      * @hide
      */
+    @NonNull
     public final String packageName;
 
     /**
@@ -145,6 +146,7 @@
      *
      * @hide
      */
+    @NonNull
     public final String targetPackageName;
 
     /**
@@ -165,6 +167,7 @@
      * Full path to the base APK for this overlay package
      * @hide
      */
+    @NonNull
     public final String baseCodePath;
 
     /**
@@ -292,6 +295,7 @@
         return targetOverlayableName;
     }
 
+    @SuppressWarnings("ConstantConditions")
     private void ensureValidState() {
         if (packageName == null) {
             throw new IllegalArgumentException("packageName must not be null");
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index d15a3a2..2bfc7fc 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1682,7 +1682,6 @@
    * Flags for a bitmask for all possible overlayable policy options.
    *
    * Any changes to this set should also update aidl/android/os/OverlayablePolicy.aidl
-   * and proto/OverlayablePolicy.proto.
    */
   enum PolicyFlags : uint32_t {
     // Base
@@ -1703,8 +1702,8 @@
     // partition before an upgrade to overlay these resources.
     PRODUCT_PARTITION = 0x00000008,
 
-    // The overlay must be signed with the same signature as the actor of the target resource,
-    // which can be separate or the same as the target package with the resource.
+    // The overlay must be signed with the same signature as the package containing the target
+    // resource
     SIGNATURE = 0x00000010,
 
     // The overlay must reside of the odm partition or must have existed on the odm
@@ -1714,6 +1713,10 @@
     // The overlay must reside of the oem partition or must have existed on the oem
     // partition before an upgrade to overlay these resources.
     OEM_PARTITION = 0x00000040,
+
+    // The overlay must be signed with the same signature as the actor declared for the target
+    // resource
+    ACTOR_SIGNATURE = 0x00000080,
   };
 
   using PolicyBitmask = uint32_t;
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 48a1afd..7c9be2d 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
+import android.content.om.OverlayableInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.os.Build.VERSION_CODES;
@@ -29,9 +30,8 @@
 import android.os.UserHandle;
 import android.util.Slog;
 
-import com.android.server.om.OverlayManagerServiceImpl.PackageManagerHelper;
-
 import java.io.File;
+import java.io.IOException;
 
 /**
  * Handle the creation and deletion of idmap files.
@@ -55,11 +55,11 @@
         VENDOR_IS_Q_OR_LATER = isQOrLater;
     }
 
-    private final PackageManagerHelper mPackageManager;
+    private final OverlayableInfoCallback mOverlayableCallback;
     private final IdmapDaemon mIdmapDaemon;
 
-    IdmapManager(final PackageManagerHelper packageManager) {
-        mPackageManager = packageManager;
+    IdmapManager(final OverlayableInfoCallback verifyCallback) {
+        mOverlayableCallback = verifyCallback;
         mIdmapDaemon = IdmapDaemon.getInstance();
     }
 
@@ -151,11 +151,16 @@
         int fulfilledPolicies = OverlayablePolicy.PUBLIC;
 
         // Overlay matches target signature
-        if (mPackageManager.signaturesMatching(targetPackage.packageName,
+        if (mOverlayableCallback.signaturesMatching(targetPackage.packageName,
                 overlayPackage.packageName, userId)) {
             fulfilledPolicies |= OverlayablePolicy.SIGNATURE;
         }
 
+        // Overlay matches actor signature
+        if (matchesActorSignature(targetPackage, overlayPackage, userId)) {
+            fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
+        }
+
         // Vendor partition (/vendor)
         if (ai.isVendor()) {
             return fulfilledPolicies | OverlayablePolicy.VENDOR_PARTITION;
@@ -184,4 +189,26 @@
 
         return fulfilledPolicies;
     }
+
+    private boolean matchesActorSignature(@NonNull PackageInfo targetPackage,
+            @NonNull PackageInfo overlayPackage, int userId) {
+        String targetOverlayableName = overlayPackage.targetOverlayableName;
+        if (targetOverlayableName != null) {
+            try {
+                OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
+                        targetPackage.packageName, targetOverlayableName, userId);
+                if (overlayableInfo != null) {
+                    String actorPackageName = OverlayActorEnforcer.getPackageNameForActor(
+                            overlayableInfo.actor, mOverlayableCallback.getNamedActors()).first;
+                    if (mOverlayableCallback.signaturesMatching(actorPackageName,
+                            overlayPackage.packageName, userId)) {
+                        return true;
+                    }
+                }
+            } catch (IOException ignored) {
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 4c85603..40efb7c 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -17,7 +17,6 @@
 package com.android.server.om;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayableInfo;
 import android.content.pm.ApplicationInfo;
@@ -46,7 +45,7 @@
     // By default, the reason is not logged to prevent leaks of why it failed
     private static final boolean DEBUG_REASON = false;
 
-    private final VerifyCallback mVerifyCallback;
+    private final OverlayableInfoCallback mOverlayableCallback;
 
     /**
      * @return nullable actor result with {@link ActorState} failure status
@@ -80,8 +79,8 @@
         return Pair.create(packageName, ActorState.ALLOWED);
     }
 
-    public OverlayActorEnforcer(@NonNull VerifyCallback verifyCallback) {
-        mVerifyCallback = verifyCallback;
+    public OverlayActorEnforcer(@NonNull OverlayableInfoCallback overlayableCallback) {
+        mOverlayableCallback = overlayableCallback;
     }
 
     void enforceActor(@NonNull OverlayInfo overlayInfo, @NonNull String methodName,
@@ -117,7 +116,7 @@
                 return ActorState.ALLOWED;
         }
 
-        String[] callingPackageNames = mVerifyCallback.getPackagesForUid(callingUid);
+        String[] callingPackageNames = mOverlayableCallback.getPackagesForUid(callingUid);
         if (ArrayUtils.isEmpty(callingPackageNames)) {
             return ActorState.NO_PACKAGES_FOR_UID;
         }
@@ -132,12 +131,12 @@
 
         if (TextUtils.isEmpty(targetOverlayableName)) {
             try {
-                if (mVerifyCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
+                if (mOverlayableCallback.doesTargetDefineOverlayable(targetPackageName, userId)) {
                     return ActorState.MISSING_TARGET_OVERLAYABLE_NAME;
                 } else {
                     // If there's no overlayable defined, fallback to the legacy permission check
                     try {
-                        mVerifyCallback.enforcePermission(
+                        mOverlayableCallback.enforcePermission(
                                 android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
 
                         // If the previous method didn't throw, check passed
@@ -153,7 +152,7 @@
 
         OverlayableInfo targetOverlayable;
         try {
-            targetOverlayable = mVerifyCallback.getOverlayableForTarget(targetPackageName,
+            targetOverlayable = mOverlayableCallback.getOverlayableForTarget(targetPackageName,
                     targetOverlayableName, userId);
         } catch (IOException e) {
             return ActorState.UNABLE_TO_GET_TARGET;
@@ -167,7 +166,7 @@
         if (TextUtils.isEmpty(actor)) {
             // If there's no actor defined, fallback to the legacy permission check
             try {
-                mVerifyCallback.enforcePermission(
+                mOverlayableCallback.enforcePermission(
                         android.Manifest.permission.CHANGE_OVERLAY_PACKAGES, methodName);
 
                 // If the previous method didn't throw, check passed
@@ -177,7 +176,7 @@
             }
         }
 
-        Map<String, Map<String, String>> namedActors = mVerifyCallback.getNamedActors();
+        Map<String, Map<String, String>> namedActors = mOverlayableCallback.getNamedActors();
         Pair<String, ActorState> actorUriPair = getPackageNameForActor(actor, namedActors);
         ActorState actorUriState = actorUriPair.second;
         if (actorUriState != ActorState.ALLOWED) {
@@ -185,7 +184,7 @@
         }
 
         String packageName = actorUriPair.first;
-        PackageInfo packageInfo = mVerifyCallback.getPackageInfo(packageName, userId);
+        PackageInfo packageInfo = mOverlayableCallback.getPackageInfo(packageName, userId);
         if (packageInfo == null) {
             return ActorState.MISSING_APP_INFO;
         }
@@ -211,7 +210,7 @@
      * For easier logging/debugging, a set of all possible failure/success states when running
      * enforcement.
      */
-    enum ActorState {
+    public enum ActorState {
         ALLOWED,
         INVALID_ACTOR,
         MISSING_NAMESPACE,
@@ -228,53 +227,4 @@
         UNABLE_TO_GET_TARGET,
         MISSING_LEGACY_PERMISSION
     }
-
-    /**
-     * Delegate to the system for querying information about packages.
-     */
-    public interface VerifyCallback {
-
-        /**
-         * Read from the APK and AndroidManifest of a package to return the overlayable defined for
-         * a given name.
-         *
-         * @throws IOException if the target can't be read
-         */
-        @Nullable
-        OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
-                @Nullable String targetOverlayableName, int userId)
-                throws IOException;
-
-        /**
-         * @see android.content.pm.PackageManager#getPackagesForUid(int)
-         */
-        @Nullable
-        String[] getPackagesForUid(int uid);
-
-        /**
-         * @param userId user to filter package visibility by
-         * @see android.content.pm.PackageManager#getPackageInfo(String, int)
-         */
-        @Nullable
-        PackageInfo getPackageInfo(@NonNull String packageName, int userId);
-
-        /**
-         * @return map of system pre-defined, uniquely named actors; keys are namespace,
-         * value maps actor name to package name
-         */
-        @NonNull
-        Map<String, Map<String, String>> getNamedActors();
-
-        /**
-         * @return true if the target package has declared an overlayable
-         */
-        boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
-                throws RemoteException, IOException;
-
-        /**
-         * @throws SecurityException containing message if the caller doesn't have the given
-         *                           permission
-         */
-        void enforcePermission(String permission, String message) throws SecurityException;
-    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f221285..c81f7cd 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -226,7 +226,7 @@
 
     private final AtomicFile mSettingsFile;
 
-    private final PackageManagerHelper mPackageManager;
+    private final PackageManagerHelperImpl mPackageManager;
 
     private final UserManagerService mUserManager;
 
@@ -244,7 +244,7 @@
             traceBegin(TRACE_TAG_RRO, "OMS#OverlayManagerService");
             mSettingsFile = new AtomicFile(
                     new File(Environment.getDataSystemDirectory(), "overlays.xml"), "overlays");
-            mPackageManager = new PackageManagerHelper(context);
+            mPackageManager = new PackageManagerHelperImpl(context);
             mUserManager = UserManagerService.getInstance();
             IdmapManager im = new IdmapManager(mPackageManager);
             mSettings = new OverlayManagerSettings();
@@ -1053,14 +1053,8 @@
         }
     }
 
-    /**
-     * Delegate for {@link android.content.pm.PackageManager} and {@link PackageManagerInternal}
-     * functionality, separated for easy testing.
-     *
-     * @hide
-     */
-    public static final class PackageManagerHelper implements
-            OverlayManagerServiceImpl.PackageManagerHelper, OverlayActorEnforcer.VerifyCallback {
+    private static final class PackageManagerHelperImpl implements PackageManagerHelper,
+            OverlayableInfoCallback {
 
         private final Context mContext;
         private final IPackageManager mPackageManager;
@@ -1073,7 +1067,7 @@
         // behind until all pending intents have been processed.
         private final SparseArray<HashMap<String, PackageInfo>> mCache = new SparseArray<>();
 
-        PackageManagerHelper(Context context) {
+        PackageManagerHelperImpl(Context context) {
             mContext = context;
             mPackageManager = getPackageManager();
             mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
@@ -1132,7 +1126,7 @@
         @Nullable
         @Override
         public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
-                @Nullable String targetOverlayableName, int userId)
+                @NonNull String targetOverlayableName, int userId)
                 throws IOException {
             PackageInfo packageInfo = getPackageInfo(packageName, userId);
             if (packageInfo == null) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 2493057..5734271 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -18,7 +18,6 @@
 
 import static android.content.om.OverlayInfo.STATE_DISABLED;
 import static android.content.om.OverlayInfo.STATE_ENABLED;
-import static android.content.om.OverlayInfo.STATE_ENABLED_IMMUTABLE;
 import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
 import static android.content.om.OverlayInfo.STATE_NO_IDMAP;
 import static android.content.om.OverlayInfo.STATE_OVERLAY_IS_BEING_REPLACED;
@@ -806,11 +805,4 @@
          **/
         void onOverlaysChanged(@NonNull String targetPackage, int userId);
     }
-
-    interface PackageManagerHelper {
-        PackageInfo getPackageInfo(@NonNull String packageName, int userId);
-        boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2,
-                                   int userId);
-        List<PackageInfo> getOverlayPackages(int userId);
-    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayableInfoCallback.java b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
new file mode 100644
index 0000000..6b81884
--- /dev/null
+++ b/services/core/java/com/android/server/om/OverlayableInfoCallback.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.om.OverlayableInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+
+import com.android.server.pm.PackageManagerServiceUtils;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Delegate to the system for querying information about overlayables and packages.
+ */
+public interface OverlayableInfoCallback {
+
+    /**
+     * Read from the APK and AndroidManifest of a package to return the overlayable defined for
+     * a given name.
+     *
+     * @throws IOException if the target can't be read
+     */
+    @Nullable
+    OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+            @NonNull String targetOverlayableName, int userId)
+            throws IOException;
+
+    /**
+     * @see PackageManager#getPackagesForUid(int)
+     */
+    @Nullable
+    String[] getPackagesForUid(int uid);
+
+    /**
+     * @param userId user to filter package visibility by
+     * @see PackageManager#getPackageInfo(String, int)
+     */
+    @Nullable
+    PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+
+    /**
+     * @return map of system pre-defined, uniquely named actors; keys are namespace,
+     * value maps actor name to package name
+     */
+    @NonNull
+    Map<String, Map<String, String>> getNamedActors();
+
+    /**
+     * @return true if the target package has declared an overlayable
+     */
+    boolean doesTargetDefineOverlayable(String targetPackageName, int userId)
+            throws RemoteException, IOException;
+
+    /**
+     * @throws SecurityException containing message if the caller doesn't have the given
+     *                           permission
+     */
+    void enforcePermission(String permission, String message) throws SecurityException;
+
+    /**
+     * @return true if {@link PackageManagerServiceUtils#compareSignatures} run on both packages
+     *     in the system returns {@link PackageManager#SIGNATURE_MATCH}
+     */
+    boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+}
diff --git a/services/core/java/com/android/server/om/PackageManagerHelper.java b/services/core/java/com/android/server/om/PackageManagerHelper.java
new file mode 100644
index 0000000..ec9c5e6
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageManagerHelper.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.om;
+
+import android.annotation.NonNull;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+
+import java.util.List;
+
+/**
+ * Delegate for {@link PackageManager} and {@link PackageManagerInternal} functionality,
+ * separated for easy testing.
+ *
+ * @hide
+ */
+interface PackageManagerHelper {
+    PackageInfo getPackageInfo(@NonNull String packageName, int userId);
+    boolean signaturesMatching(@NonNull String pkgName1, @NonNull String pkgName2, int userId);
+    List<PackageInfo> getOverlayPackages(int userId);
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 233e16c..e08eea2 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -24,6 +24,7 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.rules.ExpectedException
+import java.lang.UnsupportedOperationException
 
 class OverlayActorEnforcerTests {
     companion object {
@@ -159,7 +160,7 @@
         private val hasPermission: Boolean = false,
         private val overlayableInfo: OverlayableInfo? = null,
         private vararg val packageNames: String = arrayOf("com.test.actor.one")
-    ) : OverlayActorEnforcer.VerifyCallback {
+    ) : OverlayableInfoCallback {
 
         override fun getNamedActors() = if (isActor) {
             mapOf(NAMESPACE to mapOf(ACTOR_NAME to ACTOR_PKG_NAME))
@@ -169,7 +170,7 @@
 
         override fun getOverlayableForTarget(
             packageName: String,
-            targetOverlayableName: String?,
+            targetOverlayableName: String,
             userId: Int
         ) = overlayableInfo
 
@@ -193,5 +194,9 @@
                 throw SecurityException()
             }
         }
+
+        override fun signaturesMatching(pkgName1: String, pkgName2: String, userId: Int): Boolean {
+            throw UnsupportedOperationException()
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index a428a97..cd73432 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -24,30 +24,16 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
 
-import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
-import android.util.ArraySet;
 
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.content.om.OverlayConfig;
-
-import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index a753aac..820e61c 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -23,10 +23,13 @@
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayInfo.State;
+import android.content.om.OverlayableInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.util.ArraySet;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.content.om.OverlayConfig;
 
 import org.junit.Before;
@@ -35,6 +38,7 @@
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -281,8 +285,8 @@
         }
     }
 
-    static final class DummyPackageManagerHelper implements
-            OverlayManagerServiceImpl.PackageManagerHelper {
+    static final class DummyPackageManagerHelper implements PackageManagerHelper,
+            OverlayableInfoCallback {
         private final DummyDeviceState mState;
 
         private DummyPackageManagerHelper(DummyDeviceState state) {
@@ -320,6 +324,35 @@
                     .map(p -> getPackageInfo(p.packageName, p.userId))
                     .collect(Collectors.toList());
         }
+
+        @Nullable
+        @Override
+        public OverlayableInfo getOverlayableForTarget(@NonNull String packageName,
+                @NonNull String targetOverlayableName, int userId) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Nullable
+        @Override
+        public String[] getPackagesForUid(int uid) {
+            throw new UnsupportedOperationException();
+        }
+
+        @NonNull
+        @Override
+        public Map<String, Map<String, String>> getNamedActors() {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public void enforcePermission(String permission, String message) throws SecurityException {
+            throw new UnsupportedOperationException();
+        }
     }
 
     static class DummyIdmapManager extends IdmapManager {
diff --git a/tools/aapt2/ResourceParser_test.cpp b/tools/aapt2/ResourceParser_test.cpp
index 77e7fc5..9b70079 100644
--- a/tools/aapt2/ResourceParser_test.cpp
+++ b/tools/aapt2/ResourceParser_test.cpp
@@ -1007,6 +1007,9 @@
         <policy type="oem">
           <item type="string" name="buz" />
         </policy>
+        <policy type="actor">
+          <item type="string" name="actor" />
+        </policy>
       </overlayable>)";
   ASSERT_TRUE(TestParse(input));
 
@@ -1065,6 +1068,14 @@
   result_overlayable_item = search_result.value().entry->overlayable_item.value();
   EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
   EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::OEM_PARTITION));
+
+  search_result = table_.FindResource(test::ParseNameOrDie("string/actor"));
+  ASSERT_TRUE(search_result);
+  ASSERT_THAT(search_result.value().entry, NotNull());
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  result_overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(result_overlayable_item.overlayable->name, Eq("Name"));
+  EXPECT_THAT(result_overlayable_item.policies, Eq(PolicyFlags::ACTOR_SIGNATURE));
 }
 
 TEST_F(ResourceParserTest, ParseOverlayableNoPolicyError) {
diff --git a/tools/aapt2/Resources.proto b/tools/aapt2/Resources.proto
index 8a2f5af..ab9ce66 100644
--- a/tools/aapt2/Resources.proto
+++ b/tools/aapt2/Resources.proto
@@ -167,6 +167,7 @@
     SIGNATURE = 5;
     ODM = 6;
     OEM = 7;
+    ACTOR = 8;
   }
 
   // The location of the <item> declaration in source.
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index e1e0f17..59627ce 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -775,6 +775,7 @@
   std::string name_three = "com.app.test:integer/overlayable_three";
   OverlayableItem overlayable_item_three(group_one);
   overlayable_item_three.policies |= PolicyFlags::SIGNATURE;
+  overlayable_item_three.policies |= PolicyFlags::ACTOR_SIGNATURE;
 
   std::unique_ptr<ResourceTable> table =
       test::ResourceTableBuilder()
@@ -828,7 +829,8 @@
   result_overlayable = search_result.value().entry->overlayable_item.value();
   EXPECT_EQ(result_overlayable.overlayable->name, "OtherName");
   EXPECT_EQ(result_overlayable.overlayable->actor, "overlay://customization");
-  EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE);
+  EXPECT_EQ(result_overlayable.policies, PolicyFlags::SIGNATURE
+                                           | PolicyFlags::ACTOR_SIGNATURE);
 }
 
 TEST_F(TableFlattenerTest, FlattenOverlayableNoPolicyFails) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index db8e093..2fd01d7 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -401,6 +401,9 @@
       case pb::OverlayableItem::OEM:
         out_overlayable->policies |= PolicyFlags::OEM_PARTITION;
         break;
+      case pb::OverlayableItem::ACTOR:
+        out_overlayable->policies |= PolicyFlags::ACTOR_SIGNATURE;
+        break;
       default:
         *out_error = "unknown overlayable policy";
         return false;
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index 6bc4524d..ba6df22 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -322,6 +322,9 @@
   if (overlayable_item.policies & PolicyFlags::OEM_PARTITION) {
     pb_overlayable_item->add_policy(pb::OverlayableItem::OEM);
   }
+  if (overlayable_item.policies & PolicyFlags::ACTOR_SIGNATURE) {
+    pb_overlayable_item->add_policy(pb::OverlayableItem::ACTOR);
+  }
 
   if (source_pool != nullptr) {
     SerializeSourceToPb(overlayable_item.source, source_pool,
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index cdc8659..1a7de6d 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -536,6 +536,11 @@
   overlayable_item_boz.policies |= PolicyFlags::ODM_PARTITION;
   overlayable_item_boz.policies |= PolicyFlags::OEM_PARTITION;
 
+  OverlayableItem overlayable_item_actor_config(std::make_shared<Overlayable>(
+      "ActorConfig", "overlay://theme"));
+  overlayable_item_actor_config.policies |= PolicyFlags::SIGNATURE;
+  overlayable_item_actor_config.policies |= PolicyFlags::ACTOR_SIGNATURE;
+
   OverlayableItem overlayable_item_biz(std::make_shared<Overlayable>(
       "Other", "overlay://customization"));
   overlayable_item_biz.comment ="comment";
@@ -548,6 +553,7 @@
           .SetOverlayable("com.app.a:bool/baz", overlayable_item_baz)
           .SetOverlayable("com.app.a:bool/boz", overlayable_item_boz)
           .SetOverlayable("com.app.a:bool/biz", overlayable_item_biz)
+          .SetOverlayable("com.app.a:bool/actor_config", overlayable_item_actor_config)
           .AddValue("com.app.a:bool/fiz", ResourceUtils::TryParseBool("true"))
           .Build();
 
@@ -597,6 +603,15 @@
                                             | PolicyFlags::ODM_PARTITION
                                             | PolicyFlags::OEM_PARTITION));
 
+  search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/actor_config"));
+  ASSERT_TRUE(search_result);
+  ASSERT_TRUE(search_result.value().entry->overlayable_item);
+  overlayable_item = search_result.value().entry->overlayable_item.value();
+  EXPECT_THAT(overlayable_item.overlayable->name, Eq("ActorConfig"));
+  EXPECT_THAT(overlayable_item.overlayable->actor, Eq("overlay://theme"));
+  EXPECT_THAT(overlayable_item.policies, Eq(PolicyFlags::SIGNATURE
+                                            | PolicyFlags::ACTOR_SIGNATURE));
+
   search_result = new_table.FindResource(test::ParseNameOrDie("com.app.a:bool/biz"));
   ASSERT_TRUE(search_result);
   ASSERT_TRUE(search_result.value().entry->overlayable_item);