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

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

Persist implicit overlay configurator actor policy

This change does three things:

1) If an overlay targets a package that does not declare an overlayable
   and the overlay fulfills the actor policy, the overlay can override
   any resource in the target,
2) The actor policy is now implicitly granted to overlays signed with
   the same signature as the configurator that target white-listed
   target packages regardless of whether or not the target defines an
   overlayable.
3) If an overlay was previously granted the actor policy through the
   mechanism specified in (2), the overlay will continue to fulfill
   the actor policy even if the configurator is removed, changes
   signature, changes to a different package, or the list of
   configurator targets changes. If the overlay target package or
   target overlayable name changes, the persisted setting will be
   reset.

Bug: 157244814
Bug: 157266239
Test: atest OverlayManagerServiceImplTests
Test: idmap2_tests
Change-Id: Iff3937849ad898b1b2d74c2c632a4cdf7690fe10
parent 57e977a5
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -61,7 +61,8 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package,
                              const ResourceId& target_resource) {
  static constexpr const PolicyBitmask sDefaultPolicies =
      PolicyFlags::ODM_PARTITION | PolicyFlags::OEM_PARTITION | PolicyFlags::SYSTEM_PARTITION |
      PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE;
      PolicyFlags::VENDOR_PARTITION | PolicyFlags::PRODUCT_PARTITION | PolicyFlags::SIGNATURE |
      PolicyFlags::ACTOR_SIGNATURE;

  // If the resource does not have an overlayable definition, allow the resource to be overlaid if
  // the overlay is preinstalled or signed with the same signature as the target.
+15 −55
Original line number Diff line number Diff line
@@ -287,66 +287,26 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget
                              R::overlay::string::str4, false /* rewrite */));
}

// Overlays that are neither pre-installed nor signed with the same signature as the target cannot
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) {
  auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
                                          "/overlay/overlay-no-name.apk", PolicyFlags::PUBLIC,
                                          /* enforce_overlayable */ true);

  ASSERT_TRUE(resources) << resources.GetErrorMessage();
  ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U);
}

// Overlays that are pre-installed or are signed with the same signature as the target can overlay
// packages that have not defined overlayable resources.
// Overlays that are pre-installed or are signed with the same signature as the target/actor can
// overlay packages that have not defined overlayable resources.
TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) {
  auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void {
  constexpr PolicyBitmask kDefaultPolicies =
      PolicyFlags::SIGNATURE | PolicyFlags::ACTOR_SIGNATURE | PolicyFlags::PRODUCT_PARTITION |
      PolicyFlags::SYSTEM_PARTITION | PolicyFlags::VENDOR_PARTITION | PolicyFlags::ODM_PARTITION |
      PolicyFlags::OEM_PARTITION;

  for (PolicyBitmask policy = 1U << (sizeof(PolicyBitmask) * 8 - 1); policy > 0;
       policy = policy >> 1U) {
    auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk",
                                            "/system-overlay-invalid/system-overlay-invalid.apk",
                                            fulfilled_policies,
                                            /* enforce_overlayable */ true);

                                            policy, /* enforce_overlayable */ true);
    ASSERT_TRUE(resources) << resources.GetErrorMessage();
    auto& res = *resources;
    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 */));
    ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE,
                                R::system_overlay_invalid::string::policy_oem,
                                false /* rewrite */));
    ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE,
                                R::system_overlay_invalid::string::policy_product,
                                false /* rewrite */));
    ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE,
                                R::system_overlay_invalid::string::policy_public,
                                false /* rewrite */));
    ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE,
                                R::system_overlay_invalid::string::policy_signature,
                                false /* rewrite */));
    ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE,
                                R::system_overlay_invalid::string::policy_system,
                                false /* rewrite */));
    ASSERT_RESULT(MappingExists(
        res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE,
        R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */));
  };

  CheckEntries(PolicyFlags::SIGNATURE);
  CheckEntries(PolicyFlags::PRODUCT_PARTITION);
  CheckEntries(PolicyFlags::SYSTEM_PARTITION);
  CheckEntries(PolicyFlags::VENDOR_PARTITION);
  CheckEntries(PolicyFlags::ODM_PARTITION);
  CheckEntries(PolicyFlags::OEM_PARTITION);

    const size_t expected_overlaid = (policy & kDefaultPolicies) != 0 ? 10U : 0U;
    ASSERT_EQ(expected_overlaid, resources->GetTargetToOverlayMap().size())
        << "Incorrect number of resources overlaid through policy " << policy;
  }
}

}  // namespace android::idmap2
+8 −22
Original line number Diff line number Diff line
@@ -29,18 +29,15 @@ import android.os.OverlayablePolicy;
import android.os.SystemProperties;
import android.util.Slog;

import com.android.internal.util.ArrayUtils;

import java.io.IOException;

/**
 * Handle the creation and deletion of idmap files.
 *
 * The actual work is performed by the idmap binary, launched through idmap2d.
 *
 * Note: this class is subclassed in the OMS unit tests, and hence not marked as final.
 * The actual work is performed by idmap2d.
 * @see IdmapDaemon
 */
class IdmapManager {
final class IdmapManager {
    private static final boolean VENDOR_IS_Q_OR_LATER;
    static {
        final String value = SystemProperties.get("ro.vndk.version", "29");
@@ -57,14 +54,10 @@ class IdmapManager {

    private final IdmapDaemon mIdmapDaemon;
    private final OverlayableInfoCallback mOverlayableCallback;
    private final String mOverlayableConfigurator;
    private final String[] mOverlayableConfiguratorTargets;

    IdmapManager(final IdmapDaemon idmapDaemon, final OverlayableInfoCallback verifyCallback) {
        mOverlayableCallback = verifyCallback;
        mIdmapDaemon = idmapDaemon;
        mOverlayableConfigurator = verifyCallback.getOverlayableConfigurator();
        mOverlayableConfiguratorTargets = verifyCallback.getOverlayableConfiguratorTargets() ;
    }

    /**
@@ -72,7 +65,7 @@ class IdmapManager {
     * modified.
     */
    boolean createIdmap(@NonNull final PackageInfo targetPackage,
            @NonNull final PackageInfo overlayPackage, int userId) {
            @NonNull final PackageInfo overlayPackage, int additionalPolicies, int userId) {
        if (DEBUG) {
            Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and "
                    + overlayPackage.packageName);
@@ -80,13 +73,14 @@ class IdmapManager {
        final String targetPath = targetPackage.applicationInfo.getBaseCodePath();
        final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath();
        try {
            int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId);
            boolean enforce = enforceOverlayable(overlayPackage);
            int policies = calculateFulfilledPolicies(targetPackage, overlayPackage, userId)
                    | additionalPolicies;
            if (mIdmapDaemon.verifyIdmap(targetPath, overlayPath, policies, enforce, userId)) {
                return false;
            }
            return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies,
                    enforce, userId) != null;
            return mIdmapDaemon.createIdmap(targetPath, overlayPath, policies, enforce, userId)
                    != null;
        } catch (Exception e) {
            Slog.w(TAG, "failed to generate idmap for " + targetPath + " and "
                    + overlayPath + ": " + e.getMessage());
@@ -190,14 +184,6 @@ class IdmapManager {
        String targetOverlayableName = overlayPackage.targetOverlayableName;
        if (targetOverlayableName != null) {
            try {
                if (!mOverlayableConfigurator.isEmpty()
                        && ArrayUtils.contains(mOverlayableConfiguratorTargets,
                                targetPackage.packageName)
                        && mOverlayableCallback.signaturesMatching(mOverlayableConfigurator,
                                overlayPackage.packageName, userId)) {
                    return true;
                }

                OverlayableInfo overlayableInfo = mOverlayableCallback.getOverlayableForTarget(
                        targetPackage.packageName, targetOverlayableName, userId);
                if (overlayableInfo != null && overlayableInfo.actor != null) {
+24 −12
Original line number Diff line number Diff line
@@ -252,7 +252,8 @@ public final class OverlayManagerService extends SystemService {
            mSettings = new OverlayManagerSettings();
            mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
                    new OverlayChangeListener());
                    new OverlayChangeListener(), getOverlayableConfigurator(),
                    getOverlayableConfiguratorTargets());
            mActorEnforcer = new OverlayActorEnforcer(mPackageManager);

            final IntentFilter packageFilter = new IntentFilter();
@@ -335,6 +336,28 @@ public final class OverlayManagerService extends SystemService {
        return defaultPackages.toArray(new String[defaultPackages.size()]);
    }


    /**
     * Retrieves the package name that is recognized as an actor for the packages specified by
     * {@link #getOverlayableConfiguratorTargets()}.
     */
    @Nullable
    private String getOverlayableConfigurator() {
        return TextUtils.nullIfEmpty(Resources.getSystem()
                .getString(R.string.config_overlayableConfigurator));
    }

    /**
     * Retrieves the target packages that recognize the {@link #getOverlayableConfigurator} as an
     * actor for itself. Overlays targeting one of the specified targets that are signed with the
     * same signature as the overlayable configurator will be granted the "actor" policy.
     */
    @Nullable
    private String[] getOverlayableConfiguratorTargets() {
        return Resources.getSystem().getStringArray(
                R.array.config_overlayableConfiguratorTargets);
    }

    private final class PackageReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
@@ -1120,17 +1143,6 @@ public final class OverlayManagerService extends SystemService {
            return false;
        }

        @Override
        public String getOverlayableConfigurator() {
            return Resources.getSystem().getString(R.string.config_overlayableConfigurator);
        }

        @Override
        public String[] getOverlayableConfiguratorTargets() {
            return Resources.getSystem().getStringArray(
                    R.array.config_overlayableConfiguratorTargets);
        }

        @Override
        public List<PackageInfo> getOverlayPackages(final int userId) {
            final List<PackageInfo> overlays = mPackageManagerInternal.getOverlayPackages(userId);
+28 −2
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import android.annotation.Nullable;
import android.content.om.OverlayInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.os.OverlayablePolicy;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -73,6 +74,9 @@ final class OverlayManagerServiceImpl {
    private final String[] mDefaultOverlays;
    private final OverlayChangeListener mListener;

    private final String mOverlayableConfigurator;
    private final String[] mOverlayableConfiguratorTargets;

    /**
     * Helper method to merge the overlay manager's (as read from overlays.xml)
     * and package manager's (as parsed from AndroidManifest.xml files) views
@@ -115,13 +119,17 @@ final class OverlayManagerServiceImpl {
            @NonNull final OverlayManagerSettings settings,
            @NonNull final OverlayConfig overlayConfig,
            @NonNull final String[] defaultOverlays,
            @NonNull final OverlayChangeListener listener) {
            @NonNull final OverlayChangeListener listener,
            @Nullable final String overlayableConfigurator,
            @Nullable final String[] overlayableConfiguratorTargets) {
        mPackageManager = packageManager;
        mIdmapManager = idmapManager;
        mSettings = settings;
        mOverlayConfig = overlayConfig;
        mDefaultOverlays = defaultOverlays;
        mListener = listener;
        mOverlayableConfigurator = overlayableConfigurator;
        mOverlayableConfiguratorTargets = overlayableConfiguratorTargets;
    }

    /**
@@ -706,7 +714,25 @@ final class OverlayManagerServiceImpl {
        if (targetPackage != null && overlayPackage != null
                && !("android".equals(targetPackageName)
                    && !isPackageConfiguredMutable(overlayPackageName))) {
            modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);

            int additionalPolicies = 0;
            if (TextUtils.nullIfEmpty(mOverlayableConfigurator) != null
                    && ArrayUtils.contains(mOverlayableConfiguratorTargets, targetPackageName)
                    && isPackageConfiguredMutable(overlayPackageName)
                    && mPackageManager.signaturesMatching(mOverlayableConfigurator,
                            overlayPackageName, userId)) {
                // The overlay targets a package that has the overlayable configurator configured as
                // its actor. The overlay and this actor are signed with the same signature, so
                // the overlay fulfills the actor policy.
                modified |= mSettings.setHasConfiguratorActorPolicy(overlayPackageName, userId,
                        true);
                additionalPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
            } else if (mSettings.hasConfiguratorActorPolicy(overlayPackageName, userId)) {
                additionalPolicies |= OverlayablePolicy.ACTOR_SIGNATURE;
            }

            modified |= mIdmapManager.createIdmap(targetPackage, overlayPackage, additionalPolicies,
                    userId);
        }

        if (overlayPackage != null) {
Loading