PackageListObserver通知链
PackageListObserver 不是卸载请求的结果回调,也不是应用进程接收的 package broadcast。它是 system_server 内部消费者订阅包名集合变化的轻量接口。本文沿“初始列表 → observer 注册 → PMS 状态变化 → 锁外回调 → close”追踪完整生命周期,并结合旧版卸载回调材料区分三种通知面。
本文面向了解 PMS snapshot、Java synchronized 和 AutoCloseable 的读者。前置可阅读 PackageManagerInternal;本文不重复该文的全部内部 API,只深挖 observer 的并发和时序。
1. 三种通知面
| 通知 | 接收者 | 语义 |
|---|---|---|
IPackageDeleteObserver2 | 卸载发起者 | 一次卸载请求的结果 |
| package broadcast | 应用和系统组件 | 用户/包生命周期广播 |
PackageListObserver | system_server 内部服务 | 包名集合 added/changed/removed |
三者可能由同一次卸载触发,但 ID、线程、失败语义都不同。内部 observer 没有 return code,也不代表磁盘删除、广播和所有缓存清理全部完成。
2. 初始列表
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@Override
public PackageList getPackageList(@Nullable PackageListObserver observer) {
final ArrayList<String> list = new ArrayList<>();
PackageManagerService.this.forEachPackageState(snapshot(), packageState -> {
AndroidPackage pkg = packageState.getPkg();
if (pkg != null) {
list.add(pkg.getPackageName());
}
});
final PackageList packageList = new PackageList(list, observer);
if (observer != null) {
mPackageObserverHelper.addObserver(packageList);
}
return packageList;
}初始列表来自单个 Computer snapshot,只包含 getPkg() != null 的包。PackageSetting 存在但解析对象暂时缺失的状态不会进入列表。列表构造和 observer 注册是两个步骤,因此两者之间存在理论竞态:包变化可能发生在 snapshot 之后、注册之前。接口通过后续通知降低长期偏差,但没有事务性地把二者合并。
3. Wrapper所有权
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageList.java
public class PackageList implements PackageListObserver, AutoCloseable {
private final PackageListObserver mWrappedObserver;
private final List<String> mPackageNames;
public PackageList(List<String> packageNames, PackageListObserver observer) {
mPackageNames = packageNames;
mWrappedObserver = observer;
}
@Override
public void close() throws Exception {
LocalServices.getService(PackageManagerInternal.class)
.removePackageListObserver(this);
}
public List<String> getPackageNames() {
return mPackageNames;
}
}PackageList 同时拥有 copy-in-time 列表和 observer wrapper。close() 只取消未来订阅,不清空已经返回的列表,也不会等待正在执行的 callback。消费者若把 getPackageNames() 当作实时集合,会在安装或卸载后长期读到旧值。
4. 写时复制集合
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageObserverHelper.java
@GuardedBy("mLock")
private ArraySet<PackageListObserver> mActiveSnapshot = new ArraySet<>();
public void addObserver(PackageListObserver observer) {
synchronized (mLock) {
ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
set.add(observer);
mActiveSnapshot = set;
}
}
public void removeObserver(PackageListObserver observer) {
synchronized (mLock) {
ArraySet<PackageListObserver> set = new ArraySet<>(mActiveSnapshot);
set.remove(observer);
mActiveSnapshot = set;
}
}注册和移除都复制整个集合,换取通知路径不持锁执行 callback。这适合 observer 数量较少、通知不能阻塞 PMS 锁的场景。代价是 add/remove 为 O(n),并且 close 与正在进行的通知之间不是强同步屏障。
5. 锁外通知
public void notifyRemoved(String packageName, int uid) {
ArraySet<PackageListObserver> observers;
synchronized (mLock) {
observers = mActiveSnapshot;
}
for (int index = 0; index < observers.size(); index++) {
observers.valueAt(index).onPackageRemoved(packageName, uid);
}
}锁只保护 snapshot 引用的取得,实际回调在锁外执行。若线程 A 已取得旧集合,线程 B 随后 close() 并发布新集合,线程 A 仍可能向已关闭 observer 回调一次。消费者必须让回调幂等,并用自己的关闭标志拒绝迟到事件。
6. 生命周期触发
源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@Override
public void notifyPackageAdded(String packageName, int uid) {
mPackageObserverHelper.notifyAdded(packageName, uid);
}
@Override
public void notifyPackageChanged(String packageName, int uid) {
mPackageObserverHelper.notifyChanged(packageName, uid);
}
@Override
public void notifyPackageRemoved(String packageName, int uid) {
mPackageObserverHelper.notifyRemoved(packageName, uid);
UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName);
}removed 回调发生在 UserPackage cache remove 之前。因此 observer 被调用时,不应假设所有派生缓存已经清理。added/changed/removed 是集合变化信号,不是完整安装事务阶段枚举。
7. 弃用与替代
源码文件:frameworks/base/services/core/java/android/content/pm/PackageManagerInternal.java
/**
* @deprecated please use PackageMonitor instead. PackageMonitor covers more
* installation and uninstallation corner cases than PackageListObserver.
*/
@Deprecated
public interface PackageListObserver {
default void onPackageAdded(String packageName, int uid) {}
default void onPackageChanged(String packageName, int uid) {}
default void onPackageRemoved(String packageName, int uid) {}
}弃用原因写在源码里:PackageMonitor 覆盖更多安装/卸载边角情况。已有消费者仍可使用轻量 observer,但新代码若需要 user、replacing、data removed 等语义,应选择 PackageMonitor,不能在三个简单 callback 上推断这些状态。
8. 验证与排查
测试应覆盖四个条件:初始列表只含解析完成包;add/remove 后对应 callback 只调用一次;close 后新通知不再到达;并发 close 时允许已经取得旧 snapshot 的一次迟到 callback。最后一个条件验证写时复制语义,而不是把它误判为 listener 泄漏。
rg -n "getPackageList|PackageObserverHelper|notifyPackageRemoved" frameworks/base/services/core/java/com/android/server/pm
atest PackageManagerServiceInternalTest如果内部服务的包列表不更新,先检查是否保存了 PackageList owner、是否过早 close、callback 是否更新自己的集合,再区分迟到一次的合法竞态和持续收到通知的真实注销失败。
