Skip to content

Overlay 资源覆盖

追踪 Android 17 OverlayManagerService、idmap、PMS user state 和资源路径更新的真实调用链。

基于android-17.0.0_r1
AndroidPMSOverlayRRO

Overlay 资源覆盖 ​

本文承接 PackageManager 实践,聚焦 Runtime Resource Overlay(RRO)如何从 overlay 服务操作变成目标包的资源路径。文章不把 overlay 简化为“同名资源替换”,而是区分 OverlayManagerService(OMS)的策略状态、idmap 生成、PMS 的 per-user OverlayPaths 以及 AssetManager 的最终消费。

1. 资源覆盖链 ​

层owner结果
overlay 策略OverlayManagerServiceImplenabled/disabled、priority、target
映射文件IdmapManager/idmap daemonoverlay 到 target 的资源 ID 映射
PMS 包状态PackageUserStateImpluser-specific OverlayPaths
资源消费framework/resource loader目标包加载 overlay 资源

2. OverlayInfo 状态 ​

源码文件:frameworks/base/core/java/android/content/om/OverlayInfo.java,符号:状态常量

java
public static final int STATE_MISSING_TARGET = 0;
public static final int STATE_NO_IDMAP = 1;
public static final int STATE_DISABLED = 2;
public static final int STATE_ENABLED = 3;
@Deprecated
public static final int STATE_TARGET_IS_BEING_REPLACED = 4;
public static final int STATE_OVERLAY_IS_BEING_REPLACED = 5;
@Deprecated
public static final int STATE_ENABLED_IMMUTABLE = 6;
public static final int STATE_SYSTEM_UPDATE_UNINSTALL = 7;

STATE_MISSING_TARGET、STATE_NO_IDMAP 和 STATE_DISABLED 都表示当前资源覆盖未生效,但原因不同:目标不存在、idmap 创建失败、或策略未启用。调试时不能把所有非 enabled 状态归为“overlay 被禁用”。

3. OMS Binder 入口 ​

源码文件:frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java,符号:setEnabled

java
public boolean setEnabled(String packageName,
        boolean enable, int userIdArg) {
    if (packageName == null) {
        return false;
    }
    OverlayIdentifier overlay =
            new OverlayIdentifier(packageName);
    int userId = handleIncomingUser(
            userIdArg, "setEnabled");
    enforceActor(overlay, "setEnabled", userId);

    final long ident = Binder.clearCallingIdentity();
    try {
        synchronized (mLock) {
            updateTargetPackagesLocked(
                    mImpl.setEnabled(
                            overlay, enable, userId,
                            constraints));
            return true;
        }
    } catch (OperationFailedException e) {
        return false;
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

OMS 先解析目标 user,再做 actor 权限校验,最后在 OMS lock 内调用实现。setEnabled 的返回值只表示策略操作是否接受,不等于 PMS 已经完成资源路径更新。

4. idmap 与状态计算 ​

源码文件:frameworks/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java,符号:setEnabled、updateOverlaysForUser

java
Set<UserPackage> setEnabled(
        OverlayIdentifier overlay, boolean enable,
        int userId, @Nullable OverlayConstraints constraints)
        throws OperationFailedException {
    OverlayInfo oldInfo = mSettings.getOverlayInfo(
            overlay, userId);
    boolean modified = mSettings.setEnabled(
            overlay, userId, enable);
    if (!modified) {
        return Collections.emptySet();
    }
    return updateOverlaysForUser(userId);
}

OMS 修改自己的持久化 settings,再重新计算受影响 target。实现返回的是需要更新的 UserPackage 集合,供上层通知 PMS 哪些目标包的 overlay paths 需要改变。

源码文件:frameworks/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java,符号:createIdmap

java
int idmapStatus = mIdmapManager.createIdmap(
        targetPackageState, overlayPackageState,
        overlayName, userId);
if (idmapStatus != IdmapManager.IDMAP_SUCCESS) {
    return STATE_NO_IDMAP;
}
return enabled ? STATE_ENABLED : STATE_DISABLED;

资源映射失败会进入 STATE_NO_IDMAP,即使 overlay 的 enabled setting 是 true,也不能进入有效覆盖路径。idmap 是状态计算的一部分,而不是启用后的异步装饰。

5. PMS 接收 paths ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:setEnabledOverlayPackages

java
private void setEnabledOverlayPackages(
        int userId,
        ArrayMap<String, OverlayPaths> pendingChanges,
        Set<String> outUpdatedPackageNames,
        Set<String> outInvalidPackageNames) {
    synchronized (mOverlayPathsLock) {
        Computer computer = snapshotComputer();
        for (int i = 0; i < pendingChanges.size(); i++) {
            String targetPackageName =
                    pendingChanges.keyAt(i);
            OverlayPaths newPaths =
                    pendingChanges.valueAt(i);
            PackageStateInternal state =
                    computer.getPackageStateInternal(
                            targetPackageName);
            AndroidPackage target =
                    state == null ? null : state.getPkg();
            if (target == null) {
                outInvalidPackageNames.add(
                        targetPackageName);
                continue;
            }
            if (canSetOverlayPaths(
                    state.getUserStateOrDefault(userId)
                            .getOverlayPaths(), newPaths)) {
                outUpdatedPackageNames.add(
                        targetPackageName);
            }
        }

PMS 使用当前 snapshot 验证 target package,并在 overlay paths 确实变化时加入更新集合。不存在 target 的 pending change 不会写入 user state。

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:commit mutation

java
        commitPackageStateMutation(null, mutator -> {
            for (int i = 0; i < pendingChanges.size(); i++) {
                String target = pendingChanges.keyAt(i);
                if (!outUpdatedPackageNames
                        .contains(target)) {
                    continue;
                }
                mutator.forPackage(target)
                        .userState(userId)
                        .setOverlayPaths(
                                pendingChanges.valueAt(i));
            }
        });
    }
    invalidatePackageInfoCache(
            PackageMetrics
                    .INVALIDATION_REASON_ENABLE_OVERLAY_PACKAGES);
}

PMS 把 overlay paths 写入目标包指定 user 的 PackageUserState,然后使 package/application info cache 失效。资源消费者下一次取得新的 ApplicationInfo 时才看到更新后的 overlay paths。

6. 共享库依赖传播 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:setEnabledOverlayPackages 的 dependent 更新

java
for (String libName : targetPkg.getLibraryNames()) {
    SharedLibraryInfo info = computer
            .getSharedLibraryInfo(
                    libName,
                    SharedLibraryInfo.VERSION_UNDEFINED);
    Pair<List<VersionedPackage>, ?> dependents =
            computer.getPackagesUsingSharedLibrary(
                    info, 0, Process.SYSTEM_UID, userId);
    for (VersionedPackage dependent :
            dependents.first) {
        mutator.forPackage(
                dependent.getPackageName())
                .userState(userId)
                .setOverlayPathsForLibrary(
                        libName, newOverlayPaths);
    }
}

overlay 目标如果是 shared library,PMS 还要把 paths 传播给依赖该库的包。只更新目标包而不更新 dependents,会让同一 user 的资源解析上下文不一致。

7. System UI 路径 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java,符号:maybeUpdateSystemOverlays

java
if (targetPackageName.equals("android")) {
    if (newOverlayPaths == null) {
        mPlatformPackageOverlayPaths = null;
        mPlatformPackageOverlayResourceDirs = null;
    } else {
        mPlatformPackageOverlayPaths =
                newOverlayPaths.getOverlayPaths()
                        .toArray(new String[0]);
        mPlatformPackageOverlayResourceDirs =
                newOverlayPaths.getResourceDirs()
                        .toArray(new String[0]);
    }
    applyUpdatedSystemOverlayPaths();
}

针对 android 平台包的 overlay,PMS 还同步更新系统 ApplicationInfo 的 overlay/resource dirs,确保 System UI 和 framework 资源使用最新路径。这个特殊缓存只在 system user 的 overlay 更新中维护。

8. 包事件与重算 ​

源码文件:frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java,符号:package monitor

java
private void onPackageAdded(
        @NonNull String packageName, int userId) {
    traceBegin(TRACE_TAG_RRO,
            "OMS#onPackageAdded " + packageName);
    try {
        var packageState =
                mPackageManager.onPackageAdded(
                        packageName, userId);
        updateTargetPackagesLocked(
                mImpl.onPackageAdded(
                        packageName, userId));
    } finally {
        traceEnd(TRACE_TAG_RRO);
    }
}

overlay 包、target 包安装/更新/删除都会触发 OMS package monitor,重新计算 target overlays。RRO 的生效不仅由 setEnabled 触发,也由包生命周期事件触发。

9. 用户维度与持久化 ​

Overlay 的 enabled state、priority 和 idmap 状态按 user 计算;PMS 接收的 OverlayPaths 也写入指定 user 的 PackageUserState。因此 user 0 启用 overlay 不代表 user 10 自动看到同一资源覆盖。

10. 失败定位 ​

现象应检查
setEnabled 返回 true 但资源未变idmap 状态、PMS paths mutation、cache invalidation
STATE_MISSING_TARGETtarget package 是否安装/可见
STATE_NO_IDMAPtarget/overlay resources、idmap daemon
只影响一个用户userId 参数、OMS settings、PackageUserState
shared library 使用旧资源dependent overlay paths 是否传播
System UI 未更新主题target android 特殊路径、applyUpdatedSystemOverlayPaths

11. 阅读检查 ​

复述这条链:OverlayManager client → OMS 权限/user/actor → OMSImpl 更新 enabled/priority → idmap → PMS setEnabledOverlayPackages → per-user OverlayPaths → cache invalidation → resource consumer。然后回答:启用设置成功是否等于 idmap 成功?PMS 是否保存 overlay 的全局唯一状态?更新 shared library overlay 时只改 library package 是否足够?答案分别是“不一定”“不是,路径按 user 写入”“不够,还要传播给依赖包”。