共享库依赖解析
本文承接 Manifest 解析入口、Split APK 解析 和 meta-data 解析。主题是 Manifest 中的库依赖声明如何被解析和消费,重点覆盖 <uses-library>、<uses-native-library>、<uses-static-library> 和 <uses-sdk-library>。
要先划清一个边界:解析阶段只把声明、版本、证书摘要和 required/optional 状态写入 ParsingPackage;真正查找提供者、构建 SharedLibraryInfo、更新 PackageSetting 和形成运行时库文件列表发生在 SharedLibrariesImpl。因此“Manifest 解析成功”并不等于“依赖库已经找到并加入 class path”。
1. 总体主线
1.1 声明到运行时
四类标签共享“声明被保存、安装时再解析”的两阶段模型,但保存的数据形状不同:动态库是名称列表,静态库和 SDK 库还要维护版本与证书数组,native 库则有独立的 required/optional 列表。
1.2 四种声明
| 标签 | 保存字段 | 解析阶段必须提供 | 安装期用途 |
|---|---|---|---|
uses-library | usesLibraries / usesOptionalLibraries | name,required 默认 true | 查找动态共享库 |
uses-native-library | native required/optional 列表 | name,required 默认 true | 查找 native shared library |
uses-static-library | name、version、cert digests | 三者都必须存在 | 精确版本/证书匹配 |
uses-sdk-library | name、versionMajor、cert digests、optional | name/version/cert | SDK library 独立性策略 |
2. 包模型
2.1 PackageImpl 字段
源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/parsing/pkg/PackageImpl.java
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringList.class)
protected List<String> usesLibraries = emptyList();
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringList.class)
protected List<String> usesOptionalLibraries = emptyList();
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringList.class)
protected List<String> usesNativeLibraries = emptyList();
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringList.class)
protected List<String> usesOptionalNativeLibraries = emptyList();
private List<String> usesStaticLibraries = emptyList();
private long[] usesStaticLibrariesVersions;
private String[][] usesStaticLibrariesCertDigests;
private List<String> usesSdkLibraries = emptyList();
private long[] usesSdkLibrariesVersionsMajor;
private String[][] usesSdkLibrariesCertDigests;
private boolean[] usesSdkLibrariesOptional;动态/native 库的 required 和 optional 依赖用两套列表表达;静态/SDK 库则用并行数组保存每项的版本和证书摘要。数组索引与对应名称列表的索引必须一致,后续安装期匹配会按同一个 index 读取三组数据。
2.2 去重与升级约束
public PackageImpl addUsesLibrary(String libraryName) {
libraryName = TextUtils.safeIntern(libraryName);
if (!ArrayUtils.contains(this.usesLibraries, libraryName)) {
this.usesLibraries = CollectionUtils.add(this.usesLibraries, libraryName);
}
return this;
}
public PackageImpl addUsesOptionalLibrary(String libraryName) {
libraryName = TextUtils.safeIntern(libraryName);
if (!ArrayUtils.contains(this.usesOptionalLibraries, libraryName)) {
this.usesOptionalLibraries = CollectionUtils.add(this.usesOptionalLibraries,
libraryName);
}
return this;
}PackageImpl 的 add 方法本身只负责列表去重;“同名库先 optional 后 required 时 required 优先”由解析函数在调用 add 前通过 removeUsesOptionalLibrary() 实现。不要把所有语义都归到数据模型方法里。
2.3 静态与 SDK 追加
public PackageImpl addUsesSdkLibrary(String libraryName, long versionMajor,
String[] certSha256Digests, boolean usesSdkLibrariesOptional) {
this.usesSdkLibraries = CollectionUtils.add(this.usesSdkLibraries,
TextUtils.safeIntern(libraryName));
this.usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
this.usesSdkLibrariesVersionsMajor, versionMajor, true);
this.usesSdkLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
this.usesSdkLibrariesCertDigests, certSha256Digests, true);
this.usesSdkLibrariesOptional = ArrayUtils.appendBooleanDuplicatesAllowed(
this.usesSdkLibrariesOptional, usesSdkLibrariesOptional);
return this;
}
public PackageImpl addUsesStaticLibrary(String libraryName, long version,
String[] certSha256Digests) {
this.usesStaticLibraries = CollectionUtils.add(this.usesStaticLibraries,
TextUtils.safeIntern(libraryName));
this.usesStaticLibrariesVersions = ArrayUtils.appendLong(
this.usesStaticLibrariesVersions, version, true);
this.usesStaticLibrariesCertDigests = ArrayUtils.appendElement(String[].class,
this.usesStaticLibrariesCertDigests, certSha256Digests, true);
return this;
}静态/SDK 库的证书摘要不是一个单字符串字段,而是 String[][]:外层对应依赖项,内层允许主证书加 additional certificates。版本和证书数组必须与名称列表同步追加。
3. 动态库解析
3.1 uses-library
源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
private static ParseResult<ParsingPackage> parseUsesLibrary(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser) {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesLibrary);
try {
// Do not allow a resource reference that may change after parsing.
String lname = sa.getNonResourceString(
R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
R.styleable.AndroidManifestUsesLibrary_required, true);
if (lname != null) {
lname = lname.intern();
if (req) {
pkg.addUsesLibrary(lname).removeUsesOptionalLibrary(lname);
} else if (!ArrayUtils.contains(pkg.getUsesLibraries(), lname)) {
pkg.addUsesOptionalLibrary(lname);
}
}
return input.success(pkg);
} finally {
sa.recycle();
}
}required 默认 true;名称为空时源码不会在这个函数中抛错,而是保持成功且不加入列表。required 声明会把同名 optional 声明升级为 required;反过来 optional 不会降级已有 required。
3.2 native library
private static ParseResult<ParsingPackage> parseUsesNativeLibrary(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser) {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestUsesNativeLibrary);
try {
String lname = sa.getNonResourceString(
R.styleable.AndroidManifestUsesNativeLibrary_name);
boolean req = sa.getBoolean(
R.styleable.AndroidManifestUsesNativeLibrary_required, true);
if (lname != null) {
if (req) {
pkg.addUsesNativeLibrary(lname)
.removeUsesOptionalNativeLibrary(lname);
} else if (!ArrayUtils.contains(pkg.getUsesNativeLibraries(), lname)) {
pkg.addUsesOptionalNativeLibrary(lname);
}
}
return input.success(pkg);
} finally {
sa.recycle();
}
}native 库与 Java 动态库的 required/optional 规则相同,但存储列表独立。它是否在安装期被强制检查还受 ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES compatibility change 影响,解析阶段只记录声明。
4. 静态库解析
4.1 必需字段
源码文件:frameworks/base/core/java/com/android/internal/pm/pkg/parsing/ParsingPackageUtils.java
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestUsesStaticLibrary);
try {
String lname = sa.getNonResourceString(
R.styleable.AndroidManifestUsesLibrary_name);
final int version = sa.getInt(
R.styleable.AndroidManifestUsesStaticLibrary_version, -1);
String certSha256Digest = sa.getNonResourceString(
R.styleable.AndroidManifestUsesStaticLibrary_certDigest);
if (lname == null || version < 0 || certSha256Digest == null) {
return input.error("Bad uses-static-library declaration name: " + lname
+ " version: " + version + " certDigest" + certSha256Digest);
}
if (pkg.getUsesStaticLibraries().contains(lname)) {
return input.error("Depending on multiple versions of static library " + lname);
}静态库名称、非负 version 和 certDigest 都是解析必需项;同一包依赖同一静态库多个版本会立即失败。这里的 version 是静态库自己的 android:version,不是 package version code。
4.2 证书数组
lname = lname.intern();
certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
String[] additionalCertSha256Digests = EmptyArray.STRING;
if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O_MR1) {
ParseResult<String[]> certResult =
parseAdditionalCertificates(input, pkg, res, parser);
if (certResult.isError()) {
return input.error(certResult);
}
additionalCertSha256Digests = certResult.getResult();
}
String[] certSha256Digests = new String[additionalCertSha256Digests.length + 1];
certSha256Digests[0] = certSha256Digest;
System.arraycopy(additionalCertSha256Digests, 0, certSha256Digests, 1,
additionalCertSha256Digests.length);
return input.success(pkg.addUsesStaticLibrary(lname, version, certSha256Digests));证书摘要会移除冒号并转小写;面向 O-MR1 及以上 target SDK 的包还会读取 additional certificates。解析阶段不验证库提供者是否存在,只把规范化后的摘要交给安装期匹配。
5. SDK 库解析
5.1 版本与可选性
private static ParseResult<ParsingPackage> parseUsesSdkLibrary(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser,
R.styleable.AndroidManifestUsesSdkLibrary);
try {
String lname = sa.getNonResourceString(
R.styleable.AndroidManifestUsesSdkLibrary_name);
final int versionMajor = sa.getInt(
R.styleable.AndroidManifestUsesSdkLibrary_versionMajor, -1);
String certSha256Digest = sa.getNonResourceString(
R.styleable.AndroidManifestUsesSdkLibrary_certDigest);
boolean optional = sa.getBoolean(
R.styleable.AndroidManifestUsesSdkLibrary_optional, false);
if (lname == null || versionMajor < 0 || certSha256Digest == null) {
return input.error("Bad uses-sdk-library declaration name: " + lname
+ " version: " + versionMajor + " certDigest" + certSha256Digest);
}
if (pkg.getUsesSdkLibraries().contains(lname)) {
return input.error("Depending on multiple versions of SDK library " + lname);
}SDK 库用 versionMajor 而不是普通 version;optional 默认 false。源码明确拒绝同一包依赖同名 SDK 库的多个版本。
5.2 摘要与追加证书
lname = lname.intern();
certSha256Digest = certSha256Digest.replace(":", "").toLowerCase();
if ("".equals(certSha256Digest)) {
// Test-only default digest override.
certSha256Digest = SystemProperties.get(
"debug.pm.uses_sdk_library_default_cert_digest", "");
try {
HexEncoding.decode(certSha256Digest, false);
} catch (IllegalArgumentException e) {
certSha256Digest = "";
}
}
ParseResult<String[]> certResult = parseAdditionalCertificates(input, pkg, res, parser);
if (certResult.isError()) {
return input.error(certResult);
}
String[] additional = certResult.getResult();
String[] certs = new String[additional.length + 1];
certs[0] = certSha256Digest;
System.arraycopy(additional, 0, certs, 1, additional.length);
return input.success(pkg.addUsesSdkLibrary(lname, versionMajor, certs, optional));空 certDigest 的系统属性覆盖是测试专用兼容路径,不应理解为生产环境可以省略证书。SDK 库额外证书始终走 parseAdditionalCertificates,最后以名称、versionMajor、certs、optional 四组并行数据保存。
6. 轻量解析同步
6.1 ApkLite 字段
源码文件:frameworks/base/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
List<String> usesSdkLibraries = new ArrayList<>();
long[] usesSdkLibrariesVersionsMajor = new long[0];
String[][] usesSdkLibrariesCertDigests = new String[0][0];
List<String> usesStaticLibraries = new ArrayList<>();
long[] usesStaticLibrariesVersions = new long[0];
String[][] usesStaticLibrariesCertDigests = new String[0][0];轻量解析也必须记录静态/SDK 库,因为安装阶段可能在尚未完整解析所有 APK 时先检查缺失依赖。它与完整解析使用相同的并行数组原则,最终由 PackageLite 携带。
6.2 SDK 轻量分支
if (usesSdkLibName == null || usesSdkLibName.isBlank()
|| usesSdkLibVersionMajor < 0) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Bad uses-sdk-library declaration name: " + usesSdkLibName
+ " version: " + usesSdkLibVersionMajor);
}
if (usesSdkLibraries.contains(usesSdkLibName)) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Depending on multiple versions of SDK library: " + usesSdkLibName);
}
usesSdkLibraries.add(usesSdkLibName);
usesSdkLibrariesVersionsMajor = ArrayUtils.appendLong(
usesSdkLibrariesVersionsMajor, usesSdkLibVersionMajor, true);轻量分支直接读取 Manifest 属性并做结构校验,保留了 bundletool 将 versionMajor 写成字符串的兼容转换。完整解析和轻量解析的错误边界必须分别阅读,不能只看其中一条路径。
7. 安装期解析
7.1 收集四类库
源码文件:frameworks/base/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
ArrayList<SharedLibraryInfo> usesLibraryInfos = null;
if (!pkg.getUsesLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesLibraries(), null, null, null,
pkg.getPackageName(), "shared", true, pkg.getTargetSdkVersion(), null,
availablePackages, newLibraries, null);
}
if (!pkg.getUsesStaticLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesStaticLibraries(),
pkg.getUsesStaticLibrariesVersions(), pkg.getUsesStaticLibrariesCertDigests(),
null, pkg.getPackageName(), LIBRARY_TYPE_STATIC, true,
pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages, newLibraries, null);
}
if (!pkg.getUsesOptionalLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesOptionalLibraries(), null, null, null,
pkg.getPackageName(), "shared", false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, newLibraries, null);
}收集顺序是 required dynamic、required static、optional dynamic;collectSharedLibraryInfos 负责在 available packages 中查找、验证并形成 SharedLibraryInfo。顺序会影响后续搜索顺序,因此不是任意的集合遍历。
7.2 Native/SDK 策略
if (platformCompat.isChangeEnabledInternal(ENFORCE_NATIVE_SHARED_LIBRARY_DEPENDENCIES,
pkg.getPackageName(), pkg.getTargetSdkVersion())) {
if (!pkg.getUsesNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesNativeLibraries(), null, null, null,
pkg.getPackageName(), "native shared", true, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, newLibraries, null);
}
if (!pkg.getUsesOptionalNativeLibraries().isEmpty()) {
usesLibraryInfos = collectSharedLibraryInfos(
pkg.getUsesOptionalNativeLibraries(), null, null, null,
pkg.getPackageName(), "native shared", false, pkg.getTargetSdkVersion(),
usesLibraryInfos, availablePackages, newLibraries, null);
}
}
if (!pkg.getUsesSdkLibraries().isEmpty()) {
boolean required = !Flags.sdkLibIndependence();
usesLibraryInfos = collectSharedLibraryInfos(pkg.getUsesSdkLibraries(),
pkg.getUsesSdkLibrariesVersionsMajor(), pkg.getUsesSdkLibrariesCertDigests(),
pkg.getUsesSdkLibrariesOptional(), pkg.getPackageName(), LIBRARY_TYPE_SDK,
required, pkg.getTargetSdkVersion(), usesLibraryInfos, availablePackages,
newLibraries, null);
}native 依赖是否强制检查由 compatibility change 控制;SDK 库整体 required 与 sdkLibIndependence flag 有关,同时每项仍保留 optional 数组。也就是说,Manifest 的 optional 值不是安装期唯一决定因素。
7.3 缺失依赖检查
ArrayList<SharedLibraryInfo> missingSharedLibrary = new ArrayList<>();
collectSharedLibraryInfos(pkgLite.getUsesSdkLibraries(),
pkgLite.getUsesSdkLibrariesVersionsMajor(),
pkgLite.getUsesSdkLibrariesCertDigests(),
/*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_SDK,
/*required=*/ true, pkgLite.getTargetSdk(),
/*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
missingSharedLibrary);
collectSharedLibraryInfos(pkgLite.getUsesStaticLibraries(),
pkgLite.getUsesStaticLibrariesVersions(),
pkgLite.getUsesStaticLibrariesCertDigests(),
/*libsOptional=*/ null, pkgLite.getPackageName(), LIBRARY_TYPE_STATIC,
/*required=*/ true, pkgLite.getTargetSdk(),
/*outUsedLibraries=*/ null, mPm.mPackages, /*newLibraries=*/ null,
missingSharedLibrary);PackageLite 可在完整包对象之外参与 SDK/static 缺失库检查。这里把两类库按 required 检查,得到的 missing 列表供安装流程决定是否继续;它不是对完整解析对象的替代。
8. 对外与运行时
8.1 Info 输出
源码文件:frameworks/base/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
var usesLibraries = pkgState.getUsesLibraryInfos();
var usesLibraryInfos = new ArrayList<SharedLibraryInfo>();
for (int index = 0; index < usesLibraries.size(); index++) {
usesLibraryInfos.add(usesLibraries.get(index).getInfo());
}
info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
? null : usesLibraryFiles.toArray(new String[0]);
info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;ApplicationInfo 使用的是 PackageSetting transient state,而不是直接读取 pkg.getUsesLibraries()。这说明安装期解析已经把声明解析成实际库信息和文件路径;PackageInfo 只是复制最终状态。
8.2 sorted accessor
public String[] getUsesLibrariesSorted() {
if (mUsesLibrariesSorted == null) {
// Lazy sorting preserves immutable content.
mUsesLibrariesSorted = sortLibraries(usesLibraries);
}
return mUsesLibrariesSorted;
}包模型保留 Manifest 声明顺序,同时提供懒排序数组给需要稳定顺序的消费者。排序副本不是依赖解析本身,也不会改变原始 required/optional 列表。
9. 状态与失败
9.1 状态机
9.2 失败边界
| 阶段 | 条件 | 结果 |
|---|---|---|
| Manifest 解析 | static/SDK 缺 name、version 或 cert | 立即 parse error |
| Manifest 解析 | 同名 static/SDK 多版本 | 立即 parse error |
| Manifest 解析 | 动态/native name 缺失 | 成功但不加入列表 |
| 安装期查找 | required provider 不存在 | 安装失败 |
| 安装期查找 | 版本或证书不匹配 | 安装失败 |
| 安装期查找 | optional provider 不存在 | 继续,省略该库 |
| compatibility | native enforcement 未启用 | native 依赖可能不强制检查 |
解析失败不会产生可提交的完整包;安装期依赖失败则发生在包对象已经构建之后,由 shared-library 更新流程决定是否回滚安装。
9.3 清理路径
包替换或卸载时,shared-library 更新逻辑会重新计算依赖关系并更新 PackageSetting transient state;对外 ApplicationInfo 下次生成时读取新状态。旧的 sharedLibraryFiles 不应被理解为永久绑定在 Manifest 对象上,它们属于安装期状态。
10. 测试与诊断
10.1 输入与断言
| 场景 | 断言 | 覆盖范围 |
|---|---|---|
| required dynamic library | 进入 usesLibraries | 默认 required |
| optional 后 required | optional 列表移除,required 保留 | 强约束升级 |
| static library | name/version/certs 三组索引对齐 | 并行数组 |
| SDK library optional | optional 数组对应名称索引为 true | SDK 可选性 |
| 同名 static 两版本 | ParseResult error | Manifest 约束 |
| 非法 cert digest | 安装期匹配失败 | 证书路径 |
| optional provider 缺失 | 继续安装且不出现在 transient state | 可选恢复 |
| native enforcement 开关 | 同一声明在不同 target/change 下结果不同 | compatibility |
ApplicationInfo 输出 | files/infos 来自 PackageSetting | 对外消费 |
测试必须分开验证三种对象:解析后的 ParsingPackage 声明、安装期 SharedLibraryInfo 结果、最终 ApplicationInfo 文件/信息列表。只断言其中一个层次,无法定位失败发生在声明、查找还是输出。
10.2 现场排查顺序
- 先确认标签类型和 required/optional 属性是否进入正确列表。
- 对 static/SDK 库打印名称、版本数组和证书摘要数组的同一 index。
- 检查 provider 包是否已进入
availablePackages,版本和签名是否满足匹配。 - 查看 native enforcement 与
sdkLibIndependence的当前策略。 - 检查
PackageSetting.getTransientState()是否有实际SharedLibraryInfo和文件路径。 - 最后查看
ApplicationInfoflags 是否请求了 shared library files,以及运行时 class loader 使用的路径是否来自最新 transient state。
11. 源码路线
建议按以下顺序阅读:
ParsingPackageUtils.parseUsesLibrary()/parseUsesNativeLibrary():动态和 native 列表。parseUsesStaticLibrary()/parseUsesSdkLibrary():版本、证书和并行数组。ApkLiteParseUtils中对应分支:轻量解析为何也保存 static/SDK 依赖。PackageImpl的 add/get/makeImmutable:声明如何去重、排序和冻结。SharedLibrariesImpl.collectSharedLibraryInfos():安装期提供者查找和 required 语义。PackageInfoUtils.generateApplicationInfo():最终 files/infos 的来源。- compatibility change 与 SDK independence 分支:为什么同一 Manifest 在不同策略下结果不同。
12. 设计收束
共享库依赖的真实状态链是:
Manifest
-> 名称列表、版本数组、证书数组、可选性数组
-> PackageLite/PackageImpl 保存声明
-> SharedLibrariesImpl 查找并校验 provider
-> PackageSetting 保存实际 SharedLibraryInfo 与文件路径
-> ApplicationInfo 和运行时 ClassLoader 消费最终状态解析器负责“声明是否合法、如何保存”;shared-library 子系统负责“声明指向谁、是否满足版本/证书/策略”;PackageInfo 负责“把已解析的安装期结果暴露出去”。把三层分开,才能正确解释 optional 库为何可以缺失、static/SDK 库为何必须携带版本和证书,以及为什么仅看到 uses-library 字符串并不能证明类路径已经建立。
