PackageStateMutator 事务提交
本文承接 PackageSetting 数据结构 和 PMS 并发控制,只讨论“读到 snapshot 后如何安全写回包状态”。目标不是重复列出 setter,而是解释 recordInitialState()、generateResult()、commitPackageStateMutation() 和 onFinished() 组成的提交契约。读完后,读者应能判断一次 mutation 是否因包集合变化或状态变化而失效,知道 consumer 何时运行,以及为什么成功返回仍不等于磁盘或外部服务已经完成同步。
1. 两条序列
源码文件:
frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javaframeworks/base/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
PMS 用两条独立序列描述 optimistic mutation 的前提:mChangedPackagesTracker.getSequenceNumber() 表示包集合/版本层面的变化;PackageStateMutator.sStateChangeSequence 表示任意 PackageSetting 状态字段变化。调用方先记录二者,锁外完成查询和计算,再尝试提交。
public PackageStateMutator.InitialState recordInitialState() {
return mPackageStateMutator.initialState(
mChangedPackagesTracker.getSequenceNumber());
}
public static void onPackageStateChanged() {
sStateChangeSequence.incrementAndGet();
}两条序列不能合并成一个“版本号”:包可能被增删但目标 user state 没变,也可能包集合不变而 hidden/stopped 等字段改变。不同结果让调用方决定是重算、重试还是放弃。
2. 结果分类
源码文件:frameworks/base/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java;符号:generateResult、Result。
public Result generateResult(@Nullable InitialState state,
int changedPackagesSequenceNumber) {
if (state == null) {
return Result.SUCCESS;
}
boolean packagesChanged = changedPackagesSequenceNumber
!= state.mPackageSequence;
boolean stateChanged = sStateChangeSequence.get()
!= state.mStateSequence;
if (packagesChanged && stateChanged) {
return Result.PACKAGES_AND_STATE_CHANGED;
} else if (packagesChanged) {
return Result.PACKAGES_CHANGED;
} else if (stateChanged) {
return Result.STATE_CHANGED;
}
return Result.SUCCESS;
}Result 的 isCommitted() 只对 SUCCESS 为 true;三个 changed 结果表示 consumer 尚未运行。它们不是异常,也不会自动重试。调用方必须依据业务重新取得 snapshot,并在新前提下再次执行 mutation。
3. 写锁与 consumer
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java;符号:两个 commitPackageStateMutation 重载。
public PackageStateMutator.Result commitPackageStateMutation(
PackageStateMutator.InitialState initialState,
Consumer<PackageStateMutator> consumer) {
synchronized (mPackageStateWriteLock) {
Result result = mPackageStateMutator.generateResult(
initialState, mChangedPackagesTracker.getSequenceNumber());
if (result != Result.SUCCESS) {
return result;
}
consumer.accept(mPackageStateMutator);
mPackageStateMutator.onFinished();
}
return Result.SUCCESS;
}consumer 在 mPackageStateWriteLock 内执行;Android 17 生产 PMS 中该锁与 mLock 是同一对象。consumer 的职责应限制为轻量 setter:不能在其中做 Installer I/O、跨服务 Binder、网络或长时间计算,否则会把所有 PMS 查询和写路径堵在同一把锁上。
提交成功后 onFinished() 才通知本次触碰过的 PackageSetting。这一步触发 watchable/snapshot 变化;如果 consumer 修改了值但跳过 onFinished(),后续 Computer 可能继续复用旧快照。
4. 指定包 wrapper
源码文件:frameworks/base/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java;符号:forPackage、forPackageNullable、StateWriteWrapper。
@NonNull
public PackageStateWrite forPackage(String packageName) {
return setState(mActiveStateFunction.apply(packageName));
}
@Nullable
public PackageStateWrite forPackageNullable(String packageName) {
PackageSetting packageState = mActiveStateFunction.apply(packageName);
setState(packageState);
if (packageState == null) {
return null;
}
return setState(packageState);
}forPackage() 的返回类型非空,但 wrapper 内部 state 可以为 null;所有 setter 都会先检查 mState != null,因此目标包不存在时静默 no-op。需要明确检测不存在时,调用方使用 forPackageNullable() 或先从 Settings 取得 package state。
PMS 的按包重载会把 wrapper 交给 consumer:
PackageStateWrite state = mPackageStateMutator.forPackage(packageName);
if (state == null) {
return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL;
}
consumer.accept(state);
state.onChanged();阅读具体调用点时要以当前重载和 wrapper 实现为准,不能只凭方法名假定所有空包都会返回 SPECIFIC_PACKAGE_NULL。
5. user state 写入
StateWriteWrapper.userState(userId) 将 PackageSetting 的 PackageUserStateImpl 包装成 PackageUserStateWrite。它可以修改 installed、hidden、stopped、suspended、overlay paths、harmful warning 等 per-user 字段,但仍受同一个 mutation 锁保护。
public PackageUserStateWrite userState(int userId) {
var userState = mState == null ? null
: mState.getOrCreateUserState(userId);
if (userState != null) {
userState.setWatchable(mState);
}
return mUserStateWrite.setStates(userState);
}getOrCreateUserState 说明了一个重要副作用:即使此前没有 user 条目,写 wrapper 也可能创建 per-user 状态对象。调用方应在正确的 user 生命周期和写锁内使用它,不能把 wrapper 当作只读 view。
6. 重试调用方
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java;相关符号:安装/组件状态 mutation 调用点。
需要 optimistic 校验的调用方通常按以下模式组织:
PackageStateMutator.InitialState initialState = recordInitialState();
Computer snapshot = snapshotComputer();
// 锁外依据 snapshot 计算目标状态
PackageStateMutator.Result result = commitPackageStateMutation(
initialState, packageName, state ->
state.userState(userId).setHidden(hidden));
if (!result.isCommitted()) {
// 重新取得 snapshot,重新验证后重试或返回失败
}如果在拿到 initial state 后另一个线程更新了任意包状态,STATE_CHANGED 会阻止旧 consumer 写入;如果包集合变化,PACKAGES_CHANGED 会阻止基于旧包列表的写入。initialState == null 则明确表示调用方放弃 optimistic 检查,直接在写锁内执行。
7. 通知传播
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java、frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java。
状态 setter 通过 watchable parent 向 PackageSetting.onChanged() 传播;PackageSetting 再触发 PackageStateMutator.onPackageStateChanged() 和 Settings/PMS watcher,最终使 snapshot cache 失效。这个通知链是“内存状态变更已对读者可见”的必要条件,和 packages.xml 延迟写盘是两件事。
通知传播只说明新的 snapshot 不应继续复用旧状态;它不表示权限、Intent resolver、AppsFilter、Installer 或外部服务已经完成各自的后续工作。那些消费者由具体业务 helper 继续调度。
8. 失败与恢复
| 现象 | 检查点 | 解释 |
|---|---|---|
| consumer 没有执行 | Result 是否为 SUCCESS | changed result 表示 optimistic 前提失效 |
| 不存在的包被“成功”写入 | 使用了 forPackage() wrapper | 空 state setter 是 no-op;需要 nullable 入口 |
| 查询仍返回旧值 | onFinished()、PackageSetting watcher、snapshot 创建时点 | mutation 成功不等于旧 snapshot 自动更新 |
| 写入卡住 PMS | consumer 内是否做 I/O/Binder | consumer 持有 package state write lock |
| 重试后仍冲突 | initial state 记录过早或重试未重读 snapshot | 每次重试都要建立新的前提 |
| XML 没立即变化 | Settings 写盘调度 | state mutation 通知和持久化时机独立 |
9. 源码练习
- 构造“先记录 initial state,再修改另一个包的 stopped 状态,最后提交目标包 hidden”的时序,判断返回
STATE_CHANGED还是PACKAGES_CHANGED。 - 对比
forPackage()与forPackageNullable(),说明为什么 Android 17 的非空 wrapper 不能证明目标包存在。 - 从
PackageUserStateWrite.setSuspended追到PackageSetting.onChanged(),标出 setter、watcher、sequence 和 snapshot invalidation 的顺序。 - 检查一个真实 PMS 调用点,判断 consumer 是否只做轻量状态写入,以及 changed result 是否有重试或失败返回路径。
