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
This commit is contained in:
@@ -28,4 +28,5 @@ interface OverlayablePolicy {
|
||||
const int SIGNATURE = 0x00000010;
|
||||
const int ODM_PARTITION = 0x00000020;
|
||||
const int OEM_PARTITION = 0x00000040;
|
||||
const int ACTOR_SIGNATURE = 0x00000080;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags;
|
||||
|
||||
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* kPolicySignature = "signature";
|
||||
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},
|
||||
|
||||
@@ -279,17 +279,25 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -67,6 +67,14 @@ TEST(PoliciesTests, PoliciesToBitmaskResults) {
|
||||
|
||||
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 @@ TEST(PoliciesTests, BitmaskToPolicies) {
|
||||
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
|
||||
|
||||
@@ -40,16 +40,17 @@ namespace R::target {
|
||||
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 {
|
||||
}
|
||||
}
|
||||
|
||||
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::string {
|
||||
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
|
||||
|
||||
|
||||
@@ -237,12 +237,15 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore
|
||||
|
||||
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 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
|
||||
|
||||
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 */));
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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>
|
||||
|
||||
Binary file not shown.
@@ -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>
|
||||
</resources>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -138,6 +138,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final String packageName;
|
||||
|
||||
/**
|
||||
@@ -145,6 +146,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final String targetPackageName;
|
||||
|
||||
/**
|
||||
@@ -165,6 +167,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
* Full path to the base APK for this overlay package
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public final String baseCodePath;
|
||||
|
||||
/**
|
||||
@@ -292,6 +295,7 @@ public final class OverlayInfo implements Parcelable {
|
||||
return targetOverlayableName;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
private void ensureValidState() {
|
||||
if (packageName == null) {
|
||||
throw new IllegalArgumentException("packageName must not be null");
|
||||
|
||||
@@ -1682,7 +1682,6 @@ struct ResTable_overlayable_policy_header
|
||||
* 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 @@ struct ResTable_overlayable_policy_header
|
||||
// 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 @@ struct ResTable_overlayable_policy_header
|
||||
// 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;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static com.android.server.om.OverlayManagerService.TAG;
|
||||
|
||||
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.SystemProperties;
|
||||
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 @@ class IdmapManager {
|
||||
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 @@ class IdmapManager {
|
||||
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 @@ class IdmapManager {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 @@ public class OverlayActorEnforcer {
|
||||
// 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 @@ public class OverlayActorEnforcer {
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
}
|
||||
|
||||
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 @@ public class OverlayActorEnforcer {
|
||||
* 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 @@ public class OverlayActorEnforcer {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
|
||||
private final AtomicFile mSettingsFile;
|
||||
|
||||
private final PackageManagerHelper mPackageManager;
|
||||
private final PackageManagerHelperImpl mPackageManager;
|
||||
|
||||
private final UserManagerService mUserManager;
|
||||
|
||||
@@ -244,7 +244,7 @@ public final class OverlayManagerService extends SystemService {
|
||||
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 @@ public final class OverlayManagerService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ public final class OverlayManagerService extends SystemService {
|
||||
// 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 @@ public final class OverlayManagerService extends SystemService {
|
||||
@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) {
|
||||
|
||||
@@ -18,7 +18,6 @@ package com.android.server.om;
|
||||
|
||||
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 @@ final class OverlayManagerServiceImpl {
|
||||
**/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import android.os.Process
|
||||
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 @@ class OverlayActorEnforcerTests {
|
||||
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 @@ class OverlayActorEnforcerTests {
|
||||
|
||||
override fun getOverlayableForTarget(
|
||||
packageName: String,
|
||||
targetOverlayableName: String?,
|
||||
targetOverlayableName: String,
|
||||
userId: Int
|
||||
) = overlayableInfo
|
||||
|
||||
@@ -193,5 +194,9 @@ class OverlayActorEnforcerTests {
|
||||
throw SecurityException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun signaturesMatching(pkgName1: String, pkgName2: String, userId: Int): Boolean {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,30 +24,16 @@ import static org.junit.Assert.assertEquals;
|
||||
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 {
|
||||
|
||||
@@ -23,10 +23,13 @@ import static org.mockito.Mockito.when;
|
||||
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.ArrayList;
|
||||
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 @@ class OverlayManagerServiceImplTestsBase {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ class OverlayManagerServiceImplTestsBase {
|
||||
.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 {
|
||||
|
||||
@@ -1007,6 +1007,9 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
|
||||
<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 @@ TEST_F(ResourceParserTest, ParseOverlayablePolicy) {
|
||||
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) {
|
||||
|
||||
@@ -167,6 +167,7 @@ message OverlayableItem {
|
||||
SIGNATURE = 5;
|
||||
ODM = 6;
|
||||
OEM = 7;
|
||||
ACTOR = 8;
|
||||
}
|
||||
|
||||
// The location of the <item> declaration in source.
|
||||
|
||||
@@ -775,6 +775,7 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
|
||||
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 @@ TEST_F(TableFlattenerTest, FlattenMultipleOverlayable) {
|
||||
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) {
|
||||
|
||||
@@ -401,6 +401,9 @@ bool DeserializeOverlayableItemFromPb(const pb::OverlayableItem& pb_overlayable,
|
||||
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;
|
||||
|
||||
@@ -322,6 +322,9 @@ static void SerializeOverlayableItemToPb(const OverlayableItem& overlayable_item
|
||||
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,
|
||||
|
||||
@@ -536,6 +536,11 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
|
||||
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 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
|
||||
.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 @@ TEST(ProtoSerializeTest, SerializeAndDeserializeOverlayable) {
|
||||
| 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);
|
||||
|
||||
Reference in New Issue
Block a user