SharedLibrary 管理
本文承接 PackageManager 实践,聚焦 PMS 如何管理共享库依赖。重点不是罗列 <uses-library> 语法,而是解释 SharedLibraryInfo 的四种类型、版本选择、依赖路径如何写回 PackageStateUnserialized,以及删除/更新共享库时哪些依赖包会被影响。
1. 共享库的四种类型
源码文件:frameworks/base/core/java/android/content/pm/SharedLibraryInfo.java,符号:类型常量
public static final int TYPE_BUILTIN = 0;
public static final int TYPE_DYNAMIC = 1;
public static final int TYPE_STATIC = 2;
public static final int TYPE_SDK_PACKAGE = 3;
public static final int VERSION_UNDEFINED = -1;Builtin 是系统不可更新库;dynamic 是向后兼容并使用最新版本的动态库;static 是按指定版本链接的库;SDK package 是不向后兼容、按版本依赖的 SDK 包库。类型决定版本查找、删除策略和客户端依赖是否能继续运行。
2. LibraryInfo 数据
源码文件:frameworks/base/core/java/android/content/pm/SharedLibraryInfo.java,符号:字段与依赖方法
private final String mPath;
private final String mPackageName;
private final String mName;
private List<String> mCodePaths;
private final long mVersion;
private final @Type int mType;
private final boolean mIsNative;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
private List<SharedLibraryInfo> mDependencies;
public void addDependency(
@Nullable SharedLibraryInfo info) {
if (info == null) return;
if (mDependencies == null) {
mDependencies = new ArrayList<>();
}
mDependencies.add(info);
}库记录同时保存代码路径、声明包、版本、native 标志、依赖包和自身依赖。mDependencies 是库依赖图的一条边;它在重新计算时会先清空再建立,避免沿用旧版本关系。
3. 版本索引
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:容器字段
@Watched
private final WatchedArrayMap<String,
WatchedLongSparseArray<SharedLibraryInfo>>
mSharedLibraries;
@Watched
private final WatchedArrayMap<String,
WatchedLongSparseArray<SharedLibraryInfo>>
mStaticLibsByDeclaringPackage;第一张表按 library name 索引所有版本;第二张表按声明包名索引 static library 版本,解决多个 static library 共享同一个 declaring package name 时的冲突。两张表都可观察并支持 snapshot。
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:getSharedLibraryInfo
@GuardedBy("mPm.mLock")
public SharedLibraryInfo getSharedLibraryInfo(
@NonNull String libName, long version) {
WatchedLongSparseArray<SharedLibraryInfo> versions =
mSharedLibraries.get(libName);
if (versions == null) {
return null;
}
return versions.get(version);
}调用方必须在 PMS lock 下访问 live shared library map;并发查询使用 SharedLibrariesRead snapshot。库名存在但版本不存在时返回 null,后续安装解析会把它当作未满足依赖。
4. 依赖收集
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:updateSharedLibraries
void updateSharedLibraries(
@NonNull AndroidPackage pkg,
@NonNull PackageSetting pkgSetting,
@Nullable AndroidPackage changingLib,
@Nullable PackageSetting changingLibSetting,
@NonNull Map<String, AndroidPackage> availablePackages)
throws PackageManagerException {
ArrayList<SharedLibraryInfo> infos =
collectSharedLibraryInfos(
pkg, availablePackages,
null /* newLibraries */);
synchronized (mPm.mLock) {
executeSharedLibrariesUpdateLPw(
pkg, pkgSetting, changingLib,
changingLibSetting, infos,
mPm.mUserManager.getUserIds());
}
}依赖收集可以看到正在更新的 library package(changingLib),因此新库尚未提交到 PMS map 时也能计算依赖。收集完成后才在 mPm.mLock 下更新 package state。
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:executeSharedLibrariesUpdateLPw
applyDefiningSharedLibraryUpdateLPr(
pkg, null, (definingLibrary, dependency) ->
definingLibrary.clearDependencies());
pkgSetting.getPkgState().setUsesLibraryInfos(infos);
Set<String> usesLibraryFiles =
new LinkedHashSet<>();
for (SharedLibraryInfo info : infos) {
addSharedLibraryLPr(pkg, usesLibraryFiles,
info, changingLib, changingLibSetting);
}
pkgSetting.setPkgStateLibraryFiles(
usesLibraryFiles);更新时先清空由目标包提供的旧依赖,再写入新 SharedLibraryInfo 列表和去重后的 code paths。LinkedHashSet 保留添加顺序并消除重复路径,供 classloader/代码路径消费者使用。
5. Static 用户状态
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:static library 安装状态
int[] installedUsers = new int[allUsers.length];
int count = 0;
for (int userId : allUsers) {
if (pkgSetting.getInstalled(userId)) {
installedUsers[count++] = userId;
}
}
for (SharedLibraryInfo info : infos) {
if (!info.isStatic()) continue;
PackageSetting libSetting =
mPm.getPackageSettingForMutation(
info.getPackageName());
if (libSetting == null) continue;
for (int i = 0; i < count; i++) {
libSetting.setInstalled(
true, installedUsers[i]);
}
}静态共享库会被标记为与依赖包相同的 installed users。这样依赖包在某用户已安装时,static library 的 per-user state 也能满足解析;它不是把 static library 代码复制到依赖包目录。
6. builtin 库注册
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:addBuiltInSharedLibraryLPw
if (getSharedLibraryInfo(entry.name,
SharedLibraryInfo.VERSION_UNDEFINED) != null) {
return;
}
SharedLibraryInfo info = new SharedLibraryInfo(
entry.filename, null, null, entry.name,
SharedLibraryInfo.VERSION_UNDEFINED,
SharedLibraryInfo.TYPE_BUILTIN,
new VersionedPackage(
PLATFORM_PACKAGE_NAME, 0L),
null, null, entry.isNative);
commitSharedLibraryInfoLPw(info);builtin 库由 SystemConfig 注册,声明包是平台包,版本使用 VERSION_UNDEFINED。重复名称直接跳过,避免启动时覆盖已有 builtin 定义。
7. 删除影响
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:removeSharedLibrary
boolean removeSharedLibrary(
@NonNull String libName, long version) {
synchronized (mPm.mLock) {
WatchedLongSparseArray<SharedLibraryInfo> versions =
mSharedLibraries.get(libName);
if (versions == null
|| versions.indexOfKey(version) < 0) {
return false;
}
SharedLibraryInfo info =
versions.valueAt(versions.indexOfKey(version));
Computer snapshot = mPm.snapshotComputer();
for (int userId : mPm.mUserManager.getUserIds()) {
Pair<List<VersionedPackage>, ?> pair =
snapshot.getPackagesUsingSharedLibrary(
info, 0, Process.SYSTEM_UID,
userId);
if (pair.first == null) continue;
for (VersionedPackage dependent : pair.first) {
PackageSetting ps = mPm.mSettings
.getPackageLPr(
dependent.getPackageName());
if (ps != null) {
ps.setOverlayPathsForLibrary(
info.getName(), null, userId);
}
}
}删除库前,PMS 按 user 查询依赖包并清理 shared-library overlay paths。真正能否删除还取决于库类型、版本和后续 package deletion/reconcile;删除一个仍被强依赖的库不能简单当作文件删除。
8. 版本与依赖选择
动态库客户端使用 VERSION_UNDEFINED,由 PMS 指向可用动态版本;static/SDK package 依赖在 manifest 中携带明确版本。版本不兼容或 required dependency 缺失时,安装流程拒绝包;optional dependency 缺失则允许继续,但 SharedLibraryInfo 的 optional dependent 语义会保留。
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java,符号:库包定位
if (libInfo.isStatic()) {
String internalName = computer
.resolveInternalPackageName(
declaringPackage.getPackageName(),
declaringPackage.getLongVersionCode());
return computer.getPackageStateInternal(
internalName);
}
if (libInfo.isSdk()) {
return computer.getPackageStateInternal(
declaringPackage.getPackageName());
}static library 可能使用 synthetic internal package name,SDK package 则直接按 declaring package 查询。两种类型的版本/包定位不能用同一个字符串规则替代。
9. Snapshot 一致性
SharedLibrariesImpl 实现 Watchable/Snappable,其 map 和 SharedLibraryInfo 变化会使 PMS Computer snapshot 失效。安装线程在 mPm.mLock 下提交依赖状态,查询线程读取旧 snapshot 时仍可完成一致的旧视图;下一次查询再看到新依赖。
10. 失败定位
| 现象 | 检查点 |
|---|---|
| required library 缺失 | collectSharedLibraryInfos、name/version 索引 |
| 依赖包加载旧路径 | pkgState.usesLibraryFiles、changingLib 分支 |
| static library 用户不可见 | 依赖包 installed users 与 static library user state |
| 删除库后应用异常 | dependent packages、overlay paths、reconcile |
| 同名库版本冲突 | mSharedLibraries 与 mStaticLibsByDeclaringPackage |
| 查询读到旧依赖 | snapshot version/invalidation 是否完成 |
11. 阅读检查
复述这条链:解析包声明 → collectSharedLibraryInfos 选择版本 → executeSharedLibrariesUpdateLPw 重建依赖和 code paths → 写入 package state → 查询/类加载器消费;删除时反向找依赖包并清理关联状态。然后回答:static library 为什么需要第二张索引表?依赖库更新时为什么要传 changingLib?删除 shared library 是否只需删除一个 APK?
