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

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

Make overlay config work with immutable non-android overlays

This change renames staticness to mutability and changes the OMS to
query OverlayConfig. Changing the enabled state of immutable overlays
and changing the mutability of overlays causes the overlays to be
reinitialized and the default-enabled states to be reapplied. The
default-enabled state is applied whenever the settings for the overlay
is initialized.

Bug: 135048762
Test: use package manager to dump overlay paths of package with
      static/immutable overlays
Test: verify "cmd overlay dump"
Test: atest OverlayManagerServiceImplTests
Test: atest OverlayManagerServiceImplRebootTests

Change-Id: I791befee7f4c7c6ab5ad69cd5d1f3d85e7424f96
parent 9b93942a
Loading
Loading
Loading
Loading
+18 −17
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ public final class OverlayInfo implements Parcelable {
            STATE_NO_IDMAP,
            STATE_DISABLED,
            STATE_ENABLED,
            STATE_ENABLED_STATIC,
            STATE_ENABLED_IMMUTABLE,
            // @Deprecated STATE_TARGET_IS_BEING_REPLACED,
            STATE_OVERLAY_IS_BEING_REPLACED,
    })
@@ -117,11 +117,12 @@ public final class OverlayInfo implements Parcelable {

    /**
     * The overlay package is currently enabled because it is marked as
     * 'static'. It cannot be disabled but will change state if for instance
     * 'immutable'. It cannot be disabled but will change state if for instance
     * its target is uninstalled.
     * @hide
     */
    public static final int STATE_ENABLED_STATIC = 6;
    @Deprecated
    public static final int STATE_ENABLED_IMMUTABLE = 6;

    /**
     * Overlay category: theme.
@@ -180,21 +181,21 @@ public final class OverlayInfo implements Parcelable {
    public final int userId;

    /**
     * Priority as read from the manifest. Used if isStatic is true. Not
     * intended to be exposed to 3rd party.
     * Priority as configured by {@link com.android.internal.content.om.OverlayConfig}.
     * Not intended to be exposed to 3rd party.
     *
     * @hide
     */
    public final int priority;

    /**
     * isStatic as read from the manifest. If true, the overlay is
     * unconditionally loaded and cannot be unloaded. Not intended to be
     * isMutable as configured by {@link com.android.internal.content.om.OverlayConfig}.
     * If false, the overlay is unconditionally loaded and cannot be unloaded. Not intended to be
     * exposed to 3rd party.
     *
     * @hide
     */
    public final boolean isStatic;
    public final boolean isMutable;

    /**
     * Create a new OverlayInfo based on source with an updated state.
@@ -207,14 +208,14 @@ public final class OverlayInfo implements Parcelable {
    public OverlayInfo(@NonNull OverlayInfo source, @State int state) {
        this(source.packageName, source.targetPackageName, source.targetOverlayableName,
                source.category, source.baseCodePath, state, source.userId, source.priority,
                source.isStatic);
                source.isMutable);
    }

    /** @hide */
    public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName,
            @Nullable String targetOverlayableName, @Nullable String category,
            @NonNull String baseCodePath, int state, int userId,
            int priority, boolean isStatic) {
            int priority, boolean isMutable) {
        this.packageName = packageName;
        this.targetPackageName = targetPackageName;
        this.targetOverlayableName = targetOverlayableName;
@@ -223,7 +224,7 @@ public final class OverlayInfo implements Parcelable {
        this.state = state;
        this.userId = userId;
        this.priority = priority;
        this.isStatic = isStatic;
        this.isMutable = isMutable;
        ensureValidState();
    }

@@ -237,7 +238,7 @@ public final class OverlayInfo implements Parcelable {
        state = source.readInt();
        userId = source.readInt();
        priority = source.readInt();
        isStatic = source.readBoolean();
        isMutable = source.readBoolean();
        ensureValidState();
    }

@@ -307,7 +308,7 @@ public final class OverlayInfo implements Parcelable {
            case STATE_NO_IDMAP:
            case STATE_DISABLED:
            case STATE_ENABLED:
            case STATE_ENABLED_STATIC:
            case STATE_ENABLED_IMMUTABLE:
            case STATE_TARGET_IS_BEING_REPLACED:
            case STATE_OVERLAY_IS_BEING_REPLACED:
                break;
@@ -331,7 +332,7 @@ public final class OverlayInfo implements Parcelable {
        dest.writeInt(state);
        dest.writeInt(userId);
        dest.writeInt(priority);
        dest.writeBoolean(isStatic);
        dest.writeBoolean(isMutable);
    }

    public static final @android.annotation.NonNull Parcelable.Creator<OverlayInfo> CREATOR =
@@ -360,7 +361,7 @@ public final class OverlayInfo implements Parcelable {
    public boolean isEnabled() {
        switch (state) {
            case STATE_ENABLED:
            case STATE_ENABLED_STATIC:
            case STATE_ENABLED_IMMUTABLE:
                return true;
            default:
                return false;
@@ -386,8 +387,8 @@ public final class OverlayInfo implements Parcelable {
                return "STATE_DISABLED";
            case STATE_ENABLED:
                return "STATE_ENABLED";
            case STATE_ENABLED_STATIC:
                return "STATE_ENABLED_STATIC";
            case STATE_ENABLED_IMMUTABLE:
                return "STATE_ENABLED_IMMUTABLE";
            case STATE_TARGET_IS_BEING_REPLACED:
                return "STATE_TARGET_IS_BEING_REPLACED";
            case STATE_OVERLAY_IS_BEING_REPLACED:
+3 −2
Original line number Diff line number Diff line
@@ -46,12 +46,13 @@ import java.util.function.Supplier;
 * @see OverlayConfigParser
 */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
final public class OverlayConfig {
public class OverlayConfig {
    static final String TAG = "OverlayConfig";

    // The default priority of an overlay that has not been configured. Overlays with default
    // priority have a higher precedence than configured overlays.
    private static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;
    @VisibleForTesting
    public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE;

    @VisibleForTesting
    public static final class Configuration {
+4 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import android.util.AtomicFile;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.content.om.OverlayConfig;
import com.android.server.FgThread;
import com.android.server.IoThread;
import com.android.server.LocalServices;
@@ -248,7 +249,8 @@ public final class OverlayManagerService extends SystemService {
            IdmapManager im = new IdmapManager(mPackageManager);
            mSettings = new OverlayManagerSettings();
            mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
                    getDefaultOverlayPackages(), new OverlayChangeListener());
                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
                    new OverlayChangeListener());
            mActorEnforcer = new OverlayActorEnforcer(mPackageManager);

            final IntentFilter packageFilter = new IntentFilter();
@@ -835,7 +837,7 @@ public final class OverlayManagerService extends SystemService {
                    case "basecodepath":
                    case "state":
                    case "isenabled":
                    case "isstatic":
                    case "ismutable":
                    case "priority":
                    case "category":
                        dumpState.setField(arg);
+52 −36
Original line number Diff line number Diff line
@@ -18,7 +18,7 @@ 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_STATIC;
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;
@@ -37,6 +37,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;

import com.android.internal.content.om.OverlayConfig;
import com.android.internal.util.ArrayUtils;

import java.io.PrintWriter;
@@ -69,6 +70,7 @@ final class OverlayManagerServiceImpl {
    private final PackageManagerHelper mPackageManager;
    private final IdmapManager mIdmapManager;
    private final OverlayManagerSettings mSettings;
    private final OverlayConfig mOverlayConfig;
    private final String[] mDefaultOverlays;
    private final OverlayChangeListener mListener;

@@ -83,7 +85,7 @@ final class OverlayManagerServiceImpl {
     * should either scrap the overlay manager's previous settings or merge the old
     * settings with the new.
     */
    private static boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
    private boolean mustReinitializeOverlay(@NonNull final PackageInfo theTruth,
            @Nullable final OverlayInfo oldSettings) {
        if (oldSettings == null) {
            return true;
@@ -94,27 +96,35 @@ final class OverlayManagerServiceImpl {
        if (!Objects.equals(theTruth.targetOverlayableName, oldSettings.targetOverlayableName)) {
            return true;
        }
        if (theTruth.isStaticOverlayPackage() != oldSettings.isStatic) {

        boolean isMutable = isPackageConfiguredMutable(theTruth.packageName);
        if (isMutable != oldSettings.isMutable) {
            return true;
        }
        // a change in priority is only relevant for static RROs: specifically,
        // a regular RRO should not have its state reset only because a change
        // in priority
        if (theTruth.isStaticOverlayPackage()
                && theTruth.overlayPriority != oldSettings.priority) {

        if (getPackageConfiguredPriority(theTruth.packageName) != oldSettings.priority) {
            return true;
        }

        // If an immutable overlay changes its configured enabled state, reinitialize the overlay.
        if (!isMutable && isPackageConfiguredEnabled(theTruth.packageName)
                != oldSettings.isEnabled()) {
            return true;
        }

        return false;
    }

    OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager,
            @NonNull final IdmapManager idmapManager,
            @NonNull final OverlayManagerSettings settings,
            @NonNull final OverlayConfig overlayConfig,
            @NonNull final String[] defaultOverlays,
            @NonNull final OverlayChangeListener listener) {
        mPackageManager = packageManager;
        mIdmapManager = idmapManager;
        mSettings = settings;
        mOverlayConfig = overlayConfig;
        mDefaultOverlays = defaultOverlays;
        mListener = listener;
    }
@@ -162,8 +172,9 @@ final class OverlayManagerServiceImpl {
                        overlayPackage.overlayTarget,
                        overlayPackage.targetOverlayableName,
                        overlayPackage.applicationInfo.getBaseCodePath(),
                        overlayPackage.isStaticOverlayPackage(),
                        overlayPackage.overlayPriority,
                        isPackageConfiguredMutable(overlayPackage.packageName),
                        isPackageConfiguredEnabled(overlayPackage.packageName),
                        getPackageConfiguredPriority(overlayPackage.packageName),
                        overlayPackage.overlayCategory);
            }

@@ -374,7 +385,9 @@ final class OverlayManagerServiceImpl {
        mSettings.init(packageName, userId, overlayPackage.overlayTarget,
                overlayPackage.targetOverlayableName,
                overlayPackage.applicationInfo.getBaseCodePath(),
                overlayPackage.isStaticOverlayPackage(), overlayPackage.overlayPriority,
                isPackageConfiguredMutable(overlayPackage.packageName),
                isPackageConfiguredEnabled(overlayPackage.packageName),
                getPackageConfiguredPriority(overlayPackage.packageName),
                overlayPackage.overlayCategory);
        try {
            if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
@@ -439,8 +452,10 @@ final class OverlayManagerServiceImpl {
                    mListener.onOverlaysChanged(pkg.overlayTarget, userId);
                }
                mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
                        pkg.applicationInfo.getBaseCodePath(), pkg.isStaticOverlayPackage(),
                        pkg.overlayPriority, pkg.overlayCategory);
                        pkg.applicationInfo.getBaseCodePath(),
                        isPackageConfiguredMutable(pkg.packageName),
                        isPackageConfiguredEnabled(pkg.packageName),
                        getPackageConfiguredPriority(pkg.packageName), pkg.overlayCategory);
            }

            if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
@@ -492,13 +507,13 @@ final class OverlayManagerServiceImpl {
            return false;
        }

        // Ignore static overlays.
        if (overlayPackage.isStaticOverlayPackage()) {
        try {
            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
            if (!oi.isMutable) {
                // Ignore immutable overlays.
                return false;
            }

        try {
            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
            boolean modified = mSettings.setEnabled(packageName, userId, enable);
            modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);

@@ -534,7 +549,8 @@ final class OverlayManagerServiceImpl {
            // Disable all other overlays.
            allOverlays.remove(oi);
            for (int i = 0; i < allOverlays.size(); i++) {
                final String disabledOverlayPackageName = allOverlays.get(i).packageName;
                final OverlayInfo disabledInfo = allOverlays.get(i);
                final String disabledOverlayPackageName = disabledInfo.packageName;
                final PackageInfo disabledOverlayPackageInfo = mPackageManager.getPackageInfo(
                        disabledOverlayPackageName, userId);
                if (disabledOverlayPackageInfo == null) {
@@ -542,8 +558,8 @@ final class OverlayManagerServiceImpl {
                    continue;
                }

                if (disabledOverlayPackageInfo.isStaticOverlayPackage()) {
                    // Don't touch static overlays.
                if (!disabledInfo.isMutable) {
                    // Don't touch immutable overlays.
                    continue;
                }
                if (withinCategory && !Objects.equals(disabledOverlayPackageInfo.overlayCategory,
@@ -570,12 +586,16 @@ final class OverlayManagerServiceImpl {
        }
    }

    private boolean isPackageUpdatableOverlay(@NonNull final String packageName, final int userId) {
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
        if (overlayPackage == null || overlayPackage.isStaticOverlayPackage()) {
            return false;
    private boolean isPackageConfiguredMutable(@NonNull final String packageName) {
        return mOverlayConfig.isMutable(packageName);
    }
        return true;

    private int getPackageConfiguredPriority(@NonNull final String packageName) {
        return mOverlayConfig.getPriority(packageName);
    }

    private boolean isPackageConfiguredEnabled(@NonNull final String packageName) {
        return mOverlayConfig.isEnabled(packageName);
    }

    boolean setPriority(@NonNull final String packageName,
@@ -585,7 +605,7 @@ final class OverlayManagerServiceImpl {
                    + newParentPackageName + " userId=" + userId);
        }

        if (!isPackageUpdatableOverlay(packageName, userId)) {
        if (!isPackageConfiguredMutable(packageName)) {
            return false;
        }

@@ -605,7 +625,7 @@ final class OverlayManagerServiceImpl {
            Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
        }

        if (!isPackageUpdatableOverlay(packageName, userId)) {
        if (!isPackageConfiguredMutable(packageName)) {
            return false;
        }

@@ -625,7 +645,7 @@ final class OverlayManagerServiceImpl {
            Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
        }

        if (!isPackageUpdatableOverlay(packageName, userId)) {
        if (!isPackageConfiguredMutable(packageName)) {
            return false;
        }

@@ -682,10 +702,10 @@ final class OverlayManagerServiceImpl {
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
                userId);

        // Static RROs targeting to "android", ie framework-res.apk, are handled by native layers.
        // Immutable RROs targeting to "android", ie framework-res.apk, are handled by native layers.
        if (targetPackage != null && overlayPackage != null
                && !("android".equals(targetPackageName)
                    && overlayPackage.isStaticOverlayPackage())) {
                    && !isPackageConfiguredMutable(overlayPackageName))) {
            mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
        }

@@ -737,10 +757,6 @@ final class OverlayManagerServiceImpl {
            return STATE_NO_IDMAP;
        }

        if (overlayPackage.isStaticOverlayPackage()) {
            return STATE_ENABLED_STATIC;
        }

        final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
        return enabled ? STATE_ENABLED : STATE_DISABLED;
    }
+33 −46
Original line number Diff line number Diff line
@@ -68,20 +68,18 @@ final class OverlayManagerSettings {

    void init(@NonNull final String packageName, final int userId,
            @NonNull final String targetPackageName, @Nullable final String targetOverlayableName,
            @NonNull final String baseCodePath, boolean isStatic, int priority,
            @NonNull final String baseCodePath, boolean isMutable, boolean isEnabled, int priority,
            @Nullable String overlayCategory) {
        remove(packageName, userId);
        final SettingsItem item =
                new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
                        baseCodePath, isStatic, priority, overlayCategory);
        if (isStatic) {
            // All static overlays are always enabled.
            item.setEnabled(true);
                        baseCodePath, OverlayInfo.STATE_UNKNOWN, isEnabled, isMutable, priority,
                        overlayCategory);

        int i;
        for (i = mItems.size() - 1; i >= 0; i--) {
            SettingsItem parentItem = mItems.get(i);
                if (parentItem.mIsStatic && parentItem.mPriority <= priority) {
            if (parentItem.mPriority <= priority) {
                break;
            }
        }
@@ -91,9 +89,6 @@ final class OverlayManagerSettings {
        } else {
            mItems.add(pos, item);
        }
        } else {
            mItems.add(item);
        }
    }

    /**
@@ -182,19 +177,19 @@ final class OverlayManagerSettings {

    List<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
            final int userId) {
        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
        // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
        // ignored in OverlayManagerService.
        return selectWhereTarget(targetPackageName, userId)
                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
                .map(SettingsItem::getOverlayInfo)
                .collect(Collectors.toList());
    }

    ArrayMap<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
        // Static RROs targeting "android" are loaded from AssetManager, and so they should be
        // Immutable RROs targeting "android" are loaded from AssetManager, and so they should be
        // ignored in OverlayManagerService.
        return selectWhereUser(userId)
                .filter((i) -> !(i.isStatic() && "android".equals(i.getTargetPackageName())))
                .filter((i) -> i.isMutable() || !"android".equals(i.getTargetPackageName()))
                .map(SettingsItem::getOverlayInfo)
                .collect(Collectors.groupingBy(info -> info.targetPackageName, ArrayMap::new,
                        Collectors.toList()));
@@ -320,7 +315,7 @@ final class OverlayManagerSettings {
        pw.println("mBaseCodePath..........: " + item.getBaseCodePath());
        pw.println("mState.................: " + OverlayInfo.stateToString(item.getState()));
        pw.println("mIsEnabled.............: " + item.isEnabled());
        pw.println("mIsStatic..............: " + item.isStatic());
        pw.println("mIsMutable.............: " + item.isMutable());
        pw.println("mPriority..............: " + item.mPriority);
        pw.println("mCategory..............: " + item.mCategory);

@@ -352,8 +347,8 @@ final class OverlayManagerSettings {
            case "isenabled":
                pw.println(item.mIsEnabled);
                break;
            case "isstatic":
                pw.println(item.mIsStatic);
            case "ismutable":
                pw.println(item.mIsMutable);
                break;
            case "priority":
                pw.println(item.mPriority);
@@ -446,7 +441,7 @@ final class OverlayManagerSettings {
            final String category = XmlUtils.readStringAttribute(parser, ATTR_CATEGORY);

            return new SettingsItem(packageName, userId, targetPackageName, targetOverlayableName,
                    baseCodePath, state, isEnabled, isStatic, priority, category);
                    baseCodePath, state, isEnabled, !isStatic, priority, category);
        }

        public static void persist(@NonNull final ArrayList<SettingsItem> table,
@@ -478,7 +473,7 @@ final class OverlayManagerSettings {
            XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.mBaseCodePath);
            XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.mState);
            XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.mIsEnabled);
            XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, item.mIsStatic);
            XmlUtils.writeBooleanAttribute(xml, ATTR_IS_STATIC, !item.mIsMutable);
            XmlUtils.writeIntAttribute(xml, ATTR_PRIORITY, item.mPriority);
            XmlUtils.writeStringAttribute(xml, ATTR_CATEGORY, item.mCategory);
            xml.endTag(null, TAG_ITEM);
@@ -494,36 +489,28 @@ final class OverlayManagerSettings {
        private @OverlayInfo.State int mState;
        private boolean mIsEnabled;
        private OverlayInfo mCache;
        private boolean mIsStatic;
        private boolean mIsMutable;
        private int mPriority;
        private String mCategory;

        SettingsItem(@NonNull final String packageName, final int userId,
                @NonNull final String targetPackageName,
                @Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
                final @OverlayInfo.State int state, final boolean isEnabled, final boolean isStatic,
                final int priority,  @Nullable String category) {
                final @OverlayInfo.State int state, final boolean isEnabled,
                final boolean isMutable, final int priority,  @Nullable String category) {
            mPackageName = packageName;
            mUserId = userId;
            mTargetPackageName = targetPackageName;
            mTargetOverlayableName = targetOverlayableName;
            mBaseCodePath = baseCodePath;
            mState = state;
            mIsEnabled = isEnabled || isStatic;
            mIsEnabled = isEnabled;
            mCategory = category;
            mCache = null;
            mIsStatic = isStatic;
            mIsMutable = isMutable;
            mPriority = priority;
        }

        SettingsItem(@NonNull final String packageName, final int userId,
                @NonNull final String targetPackageName,
                @Nullable final String targetOverlayableName, @NonNull final String baseCodePath,
                final boolean isStatic, final int priority, @Nullable String category) {
            this(packageName, userId, targetPackageName, targetOverlayableName, baseCodePath,
                    OverlayInfo.STATE_UNKNOWN, false, isStatic, priority, category);
        }

        private String getTargetPackageName() {
            return mTargetPackageName;
        }
@@ -567,7 +554,7 @@ final class OverlayManagerSettings {
        }

        private boolean setEnabled(boolean enable) {
            if (mIsStatic) {
            if (!mIsMutable) {
                return false;
            }

@@ -591,7 +578,7 @@ final class OverlayManagerSettings {
        private OverlayInfo getOverlayInfo() {
            if (mCache == null) {
                mCache = new OverlayInfo(mPackageName, mTargetPackageName, mTargetOverlayableName,
                        mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsStatic);
                        mCategory, mBaseCodePath, mState, mUserId, mPriority, mIsMutable);
            }
            return mCache;
        }
@@ -600,8 +587,8 @@ final class OverlayManagerSettings {
            mCache = null;
        }

        private boolean isStatic() {
            return mIsStatic;
        private boolean isMutable() {
            return mIsMutable;
        }

        private int getPriority() {
Loading