PMS核心机制:应用安装与包管理深度解析

引言

在上一篇文章中,我们深入了解了AMS如何管理Activity的生命周期和进程优先级。但有一个更基础的问题:这些应用是如何被"装进"Android系统的?

想象你点击一个APK文件准备安装应用,系统如何验证这个APK是否安全?如何解析AndroidManifest.xml?如何分配UID和权限?如何处理应用更新和版本兼容?

这一切的答案都指向PackageManagerService(PMS)------Android系统中负责应用"户籍管理"的核心服务。

markdown 复制代码
用户点击安装APK
    ↓
PackageInstaller UI启动
    ↓
PMS验证签名
    ↓
PMS解析AndroidManifest
    ↓
PMS分配UID和权限
    ↓
PMS复制APK到系统目录
    ↓
PMS更新packages.xml
    ↓
应用安装完成,可以启动了!

如果把Android系统比作一个国家,那么:

  • AMS是"劳动局",管理进程的工作和生命周期
  • PMS是"户籍管理局",管理应用的"身份证"和"准入资格"
  • WMS是"城市规划局",管理窗口的位置和层级

📖 系列前置阅读:建议先阅读第14篇(AMS核心机制),理解应用进程的管理方式。


PMS是什么?

PMS的核心职责

PackageManagerService是Android系统中最早启动的系统服务之一,在SystemServer中随着第一批"引导服务(Bootstrap Services)"一起启动。它的职责可以概括为"应用全生命周期管理":

阶段 核心功能 关键操作
安装前 验证与检查 签名校验、版本检查、空间检查
安装中 解析与注册 AndroidManifest解析、权限授予、UID分配
运行中 查询与管理 包信息查询、权限检查、组件查询
更新时 版本迁移 数据保留、权限更新、代码替换
卸载时 清理与回收 数据删除、权限回收、UID释放

PMS管理的核心数据

PMS维护着Android系统中所有应用的"户籍档案",这些数据持久化在/data/system/packages.xml文件中:

xml 复制代码
<!-- packages.xml 核心结构 -->
<packages>
    <!-- 权限定义 -->
    <permissions>
        <item name="android.permission.CAMERA" ... />
    </permissions>

    <!-- 共享UID定义 -->
    <shared-user name="android.uid.system" userId="1000">
        <sigs count="1">
            <cert index="0" key="..." />
        </sigs>
        <perms>
            <item name="android.permission.MODIFY_PHONE_STATE" />
        </perms>
    </shared-user>

    <!-- 已安装应用包 -->
    <package name="com.android.chrome"
             codePath="/data/app/~~xYz123==/chrome-ABC/base.apk"
             userId="10086"
             version="115"
             targetSdkVersion="34"
             installer="com.android.vending">
        <sigs count="1">
            <cert index="15" />
        </sigs>
        <perms>
            <item name="android.permission.INTERNET" granted="true" />
            <item name="android.permission.CAMERA" granted="false" />
        </perms>
    </package>
</packages>

核心字段解析:

  • codePath: APK在系统中的存储路径
  • userId: 应用的Linux UID(用于进程隔离)
  • installer: 安装来源(Google Play = com.android.vending)
  • sigs: 应用签名证书(用于更新时验证)
  • perms: 已授予的权限列表

PMS的架构演进

Android 15的重大优化:

优化项 Android 14及之前 Android 15
启动速度 packages.xml顺序解析 并行解析 + 懒加载
内存占用 全量加载到内存 按需加载 + LRU缓存
查询性能 线性搜索 HashMap索引
Apex模块 基础支持 深度集成,支持动态更新
签名校验 SHA-1 + SHA-256 强制SHA-256,弃用SHA-1

PMS整体架构如下图所示:

图1: PackageManagerService整体架构,展示了从SystemServer启动到Binder IPC接口,再到数据持久化的完整层次结构


PMS启动流程

SystemServer中的启动顺序

PMS作为"Bootstrap Services"(引导服务)在SystemServer启动的第一阶段被创建:

java 复制代码
// SystemServer.java (Android 15)
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
    t.traceBegin("StartInstaller");
    Installer installer = mSystemServiceManager.startService(Installer.class);
    t.traceEnd();

    // PMS依赖Installer服务,用于实际的文件操作
    t.traceBegin("StartPackageManagerService");
    try {
        // 注意:这里只是"创建"PMS,真正的初始化在后面
        mPackageManagerService = PackageManagerService.main(
            mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
            mOnlyCore);
    } finally {
        t.traceEnd();
    }

    // PMS初始化完成后,其他服务才能查询应用信息
    mFirstBoot = mPackageManagerService.isFirstBoot();
    mPackageManager = mSystemContext.getPackageManager();
}

启动时序:

  1. Installer服务启动 (~50ms) - 负责底层文件操作
  2. PMS创建 (~100ms) - 创建PMS实例
  3. 扫描系统应用 (~2000ms) - 扫描/system/app/system/priv-app
  4. 扫描用户应用 (~1000ms) - 扫描/data/app
  5. 权限初始化 (~500ms) - 加载权限定义和授权状态
  6. packages.xml写入 (~200ms) - 持久化应用信息

性能提示 : Android 15通过并行扫描将启动时间从4秒优化到2.5秒。

PMS初始化核心流程

java 复制代码
// PackageManagerService.java
public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // 1. 创建PackageManagerServiceInjector(依赖注入器)
    PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
            context, installer, ...);

    // 2. 创建PMS实例(构造函数会执行大量初始化工作)
    PackageManagerService m = new PackageManagerService(injector, ...);

    // 3. 注册Binder服务,供其他进程调用
    ServiceManager.addService("package", m);

    // 4. 注册PackageManagerLocal(供SystemServer内部调用,性能更好)
    LocalManagerRegistry.addManager(PackageManagerLocal.class,
            m.new PackageManagerLocalImpl());

    return m;
}

构造函数中的关键步骤:

java 复制代码
// PackageManagerService构造函数(简化版)
public PackageManagerService(PackageManagerServiceInjector injector, ...) {
    // 1. 初始化Settings(负责packages.xml的读写)
    mSettings = injector.getSettings();

    // 2. 扫描系统分区的APK(只读,预装应用)
    scanDirTracedLI(new File(Environment.getRootDirectory(), "app"), ...);
    scanDirTracedLI(new File(Environment.getRootDirectory(), "priv-app"), ...);

    // 3. 扫描vendor分区(厂商定制应用)
    scanDirTracedLI(new File("/vendor/app"), ...);

    // 4. 扫描data分区(用户安装应用)
    scanDirTracedLI(new File("/data/app"), ...);

    // 5. 读取packages.xml,恢复已安装应用状态
    mSettings.readLPw(mUserManager.getUsersLPr());

    // 6. 初始化权限管理器
    mPermissionManager = injector.getPermissionManagerServiceInternal();

    // 7. 启用"组件解析器"(用于Intent匹配)
    mComponentResolver = injector.getComponentResolver();
}

APK安装完整流程

安装流程总览

一个APK从点击安装到成功运行,要经历7个核心阶段:

完整的APK安装流程如下图所示:

图2: APK安装完整流程,展示了从预检查到注册生效的7个核心阶段,每个阶段的关键操作和耗时

阶段1-2: PackageInstaller与Session创建

当用户点击APK文件或通过adb install安装应用时,会启动PackageInstaller应用:

java 复制代码
// PackageInstaller应用的核心代码
public class InstallStart extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // 1. 从Intent获取APK的URI
        Uri packageUri = getIntent().getData();

        // 2. 检查是否有INSTALL_PACKAGES权限
        if (getPackageManager().canRequestPackageInstalls()) {
            startInstall();
        } else {
            // 跳转到"允许安装未知应用"设置页
            requestPermission();
        }
    }

    private void startInstall() {
        // 3. 创建PackageInstaller.Session
        PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
        PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
            PackageInstaller.SessionParams.MODE_FULL_INSTALL);

        int sessionId = packageInstaller.createSession(params);
        PackageInstaller.Session session = packageInstaller.openSession(sessionId);

        // 4. 将APK数据写入Session
        OutputStream out = session.openWrite("base.apk", 0, -1);
        // ... 复制APK数据到out ...
        session.fsync(out);

        // 5. 提交Session,触发安装
        session.commit(createIntentSender(sessionId));
    }
}

Session机制的优势:

  • ✅ 支持增量更新(只传输变更的部分)
  • ✅ 支持分包安装(Split APK)
  • ✅ 支持安装回滚(失败时自动恢复)

阶段3: 签名校验

为什么需要签名校验?

  • 防止APK被篡改(确保代码完整性)
  • 验证应用来源(防止伪造开发者)
  • 支持应用更新(只有相同签名的APK才能覆盖安装)

Android签名方案演进:

版本 引入时间 签名位置 校验方式 优缺点
V1 Android 1.0 META-INF/目录 逐文件校验JAR签名 ❌ 校验慢,易被绕过
V2 Android 7.0 APK Signing Block 全APK哈希校验 ✅ 快速,安全
V3 Android 9.0 APK Signing Block 支持证书轮转 ✅ 密钥升级
V4 Android 11.0 .apk.idsig文件 流式校验(边下边装) ✅ 超大APK优化

签名校验完整流程如下图所示:

图3: APK签名校验完整流程,展示了V3→V2→V1的优先级选择、签名块提取、证书验证的完整决策链路

签名校验源码:

java 复制代码
// ApkSignatureVerifier.java (Android 15)
public static PackageParser.SigningDetails verifySignatures(
        ParseTypeImpl input, ParseInput parseInput,
        String apkPath, int minSignatureSchemeVersion) {

    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "verifySignatures");

    try {
        // 1. 优先尝试V3签名(Android 9+)
        if (minSignatureSchemeVersion <= SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3) {
            try {
                return verifyV3Signature(apkPath);
            } catch (SignatureNotFoundException e) {
                // V3签名不存在,尝试V2
            }
        }

        // 2. 尝试V2签名(Android 7+)
        if (minSignatureSchemeVersion <= SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2) {
            try {
                return verifyV2Signature(apkPath);
            } catch (SignatureNotFoundException e) {
                // V2签名不存在,尝试V1
            }
        }

        // 3. 兜底:V1签名(JAR签名)
        return verifyV1Signature(apkPath, true);

    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

// V2签名校验(核心逻辑)
private static PackageParser.SigningDetails verifyV2Signature(String apkPath)
        throws SignatureNotFoundException, SecurityException {

    // 1. 打开APK文件
    try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) {

        // 2. 定位APK Signing Block(在ZIP Central Directory之前)
        SignatureInfo signatureInfo = ApkSigningBlockUtils.findSignature(apk);

        // 3. 解析签名块,提取证书
        ApkSignatureSchemeV2Verifier.VerifiedSigner signer =
            ApkSignatureSchemeV2Verifier.verify(apk, signatureInfo);

        // 4. 计算APK内容的哈希值
        byte[] contentDigest = signer.contentDigests.get(
            ContentDigestAlgorithm.CHUNKED_SHA256);

        // 5. 验证签名(使用证书公钥验证哈希签名)
        Certificate[] certs = signer.certs.toArray(new Certificate[0]);
        Signature[] signatures = convertToSignatures(certs);

        // 6. 返回签名详情
        return new PackageParser.SigningDetails(
            signatures,
            SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2,
            null, // publicKeys
            null  // pastSigningCertificates (V3才有)
        );
    }
}

签名校验失败的常见原因:

  1. 签名不匹配: 更新应用时使用了不同的签名密钥
  2. APK被篡改: 下载过程中APK文件损坏或被修改
  3. 最低API要求: APK使用V3签名,但系统不支持(Android < 9)

阶段4: AndroidManifest解析

AndroidManifest.xml的二进制格式

为了减小APK体积和提升解析速度,Android将Manifest编译为二进制格式(AXML):

scss 复制代码
原始XML(文本格式,~10KB)
    ↓ aapt编译
二进制AXML(~4KB,解析速度提升5倍)

解析核心代码:

java 复制代码
// PackageParser2.java (Android 15)
public ParsedPackage parsePackage(File packageFile, int flags) throws PackageParserException {

    // 1. 打开APK文件
    final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
    final ParseInput parseInput = input.reset();

    try {
        // 2. 提取AndroidManifest.xml
        AssetManager assets = new AssetManager();
        assets.addAssetPath(packageFile.getAbsolutePath());
        XmlResourceParser parser = assets.openXmlResourceParser("AndroidManifest.xml");

        // 3. 解析<manifest>根元素
        ParsedPackage pkg = parseBaseApk(parseInput, parser, flags);
        pkg.setCodePath(packageFile.getAbsolutePath());

        // 4. 解析Split APK(如果有)
        String[] splitCodePaths = pkg.getSplitCodePaths();
        if (splitCodePaths != null) {
            for (String splitPath : splitCodePaths) {
                parseSplitApk(parseInput, pkg, splitPath, assets, flags);
            }
        }

        return pkg;

    } catch (Exception e) {
        throw new PackageParserException(
            INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
            "Failed to parse " + packageFile, e);
    }
}

// 解析核心元素
private ParsedPackage parseBaseApk(ParseInput parseInput,
        XmlResourceParser parser, int flags) throws PackageParserException {

    final ParsedPackage pkg = new ParsedPackage();

    // 1. 解析package属性
    pkg.setPackageName(parser.getAttributeValue(null, "package"));
    pkg.setVersionCode(parser.getAttributeIntValue(null, "versionCode", 0));
    pkg.setVersionName(parser.getAttributeValue(null, "versionName"));

    // 2. 遍历子元素
    int type;
    while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
        if (type != XmlPullParser.START_TAG) continue;

        String tagName = parser.getName();
        switch (tagName) {
            case "application":
                parseApplication(pkg, parser, flags);
                break;
            case "uses-permission":
                parseUsesPermission(pkg, parser);
                break;
            case "uses-sdk":
                parseUsesSdk(pkg, parser);
                break;
            case "uses-feature":
                parseUsesFeature(pkg, parser);
                break;
            // ... 其他标签 ...
        }
    }

    return pkg;
}

// 解析<application>标签
private void parseApplication(ParsedPackage pkg, XmlResourceParser parser, int flags) {
    // 提取Application属性
    pkg.setApplicationInfo(new ApplicationInfo());
    pkg.setLabel(parser.getAttributeResourceValue(null, "label", 0));
    pkg.setIcon(parser.getAttributeResourceValue(null, "icon", 0));

    // 解析四大组件
    int type;
    while ((type = parser.next()) != XmlPullParser.END_TAG) {
        if (type != XmlPullParser.START_TAG) continue;

        String tagName = parser.getName();
        switch (tagName) {
            case "activity":
                ParsedActivity activity = parseActivity(parser, flags);
                pkg.addActivity(activity);
                break;
            case "service":
                ParsedService service = parseService(parser, flags);
                pkg.addService(service);
                break;
            case "receiver":
                ParsedReceiver receiver = parseReceiver(parser, flags);
                pkg.addReceiver(receiver);
                break;
            case "provider":
                ParsedProvider provider = parseProvider(parser, flags);
                pkg.addProvider(provider);
                break;
        }
    }
}

解析结果示例:

java 复制代码
ParsedPackage {
    packageName = "com.example.myapp"
    versionCode = 42
    versionName = "2.1.5"
    targetSdkVersion = 34  // Android 14
    minSdkVersion = 24      // Android 7.0

    activities = [
        ParsedActivity {
            name = "com.example.myapp.MainActivity"
            exported = true
            intentFilters = [
                IntentFilter {
                    actions = ["android.intent.action.MAIN"]
                    categories = ["android.intent.category.LAUNCHER"]
                }
            ]
        }
    ]

    services = [
        ParsedService {
            name = "com.example.myapp.MyService"
            exported = false
            permission = "com.example.myapp.permission.MY_SERVICE"
        }
    ]

    usesPermissions = [
        "android.permission.INTERNET",
        "android.permission.CAMERA",
        "android.permission.ACCESS_FINE_LOCATION"
    ]
}

阶段5: UID分配与权限授予

UID分配规则

Android使用Linux的UID机制实现应用沙箱隔离:

UID范围 用途 示例
0 root用户 init进程
1000-1999 系统服务 system_server(1000)
2000-2999 Shell用户 adb shell(2000)
10000-19999 第一用户的应用 com.android.chrome(10086)
20000-29999 第二用户的应用(多用户) -
1000000+ 隔离进程 WebView渲染进程

UID分配源码:

java 复制代码
// Settings.java
private int newUserIdLPw(Object obj) {
    // 1. 从10000开始查找可用UID
    final int N = mUserIds.size();
    for (int i = mFirstAvailableUid; i < N; i++) {
        if (mUserIds.get(i) == null) {
            // 找到空闲UID
            mUserIds.set(i, obj);
            return Process.FIRST_APPLICATION_UID + i; // 10000 + i
        }
    }

    // 2. 如果现有范围已满,扩展数组
    if (N >= (Process.LAST_APPLICATION_UID - Process.FIRST_APPLICATION_UID)) {
        // UID耗尽(理论上支持9999个应用)
        return -1;
    }

    mUserIds.add(obj);
    return Process.FIRST_APPLICATION_UID + N;
}

SharedUserId机制(已废弃,Android 14+不推荐):

xml 复制代码
<!-- 两个应用共享UID,可以访问彼此的数据 -->
<manifest package="com.example.app1"
          android:sharedUserId="com.example.shared">
</manifest>

<manifest package="com.example.app2"
          android:sharedUserId="com.example.shared">
</manifest>

⚠️ 安全提示: Android 14+强烈建议使用ContentProvider或AIDL进行数据共享,而非SharedUserId。

权限授予流程:

java 复制代码
// PermissionManagerServiceImpl.java
private void grantPermissionsLPw(AndroidPackage pkg, ...) {

    final PackageStateInternal ps = mPackageManagerInt.getPackageStateInternal(pkg.getPackageName());
    final int[] userIds = mUserManagerInt.getUserIds();

    // 遍历所有用户
    for (int userId : userIds) {

        // 遍历应用请求的所有权限
        List<String> requestedPermissions = pkg.getRequestedPermissions();
        for (String permission : requestedPermissions) {

            BasePermission bp = mSettings.getPermissionLocked(permission);
            if (bp == null) {
                continue; // 权限不存在
            }

            // 1. Normal权限:自动授予
            if (bp.isNormal()) {
                grantRuntimePermissionInternal(pkg.getPackageName(), permission, userId);
            }

            // 2. Signature权限:检查签名一致性
            else if (bp.isSignature()) {
                if (compareSignatures(bp.getPackageSetting().getSignatures(),
                        pkg.getSigningDetails().getSignatures()) == SIGNATURE_MATCH) {
                    grantRuntimePermissionInternal(pkg.getPackageName(), permission, userId);
                }
            }

            // 3. Dangerous权限:标记为待授予(需要用户在运行时确认)
            else if (bp.isDangerous()) {
                // 不自动授予,等待用户确认
                Log.i(TAG, "Dangerous permission " + permission + " for " + pkg.getPackageName()
                        + " will be granted at runtime");
            }
        }
    }
}

权限类型对比:

权限类型 授予时机 是否需要用户确认 示例
Normal 安装时自动授予 INTERNET, VIBRATE
Dangerous 运行时请求 CAMERA, LOCATION
Signature 签名匹配时授予 DUMP, SET_TIME
SignatureOrSystem 签名匹配或系统应用 SHUTDOWN, REBOOT

阶段6-7: 文件安装与注册生效

文件安装关键步骤:

java 复制代码
// InstallPackageHelper.java
private void installPackageLI(InstallRequest installRequest) {

    final ParsedPackage parsedPackage = installRequest.getParsedPackage();
    final String packageName = parsedPackage.getPackageName();

    // 1. 确定APK最终存储路径
    final File codeFile = new File(
        "/data/app/" + packageName + "-" + generateRandomSuffix());

    // 2. 移动APK文件(从临时目录到最终目录)
    if (!installRequest.getMoveInfo().movePackage(codeFile)) {
        throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
            "Failed to move package");
    }

    // 3. 提取Native库(.so文件)
    NativeLibraryHelper.copyNativeBinariesLI(codeFile, parsedPackage);

    // 4. DEX优化(dex2oat编译)
    performDexOpt(parsedPackage, codeFile);

    // 5. 创建应用数据目录(/data/user/0/<packageName>/)
    mInstaller.createAppData(packageName, userId,
        StorageManager.FLAG_STORAGE_DE, /* Default Encryption */
        appId, seInfo, targetSdkVersion);

    // 6. 更新packages.xml
    mSettings.insertPackageSettingLPw(pkgSetting, parsedPackage);
    mSettings.writeLPr(); // 持久化到文件

    // 7. 注册到PMS缓存
    mPackages.put(packageName, parsedPackage);

    // 8. 发送安装成功广播
    sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, ...);
}

DEX优化(dex2oat):

bash 复制代码
# dex2oat编译流程
dex2oat \
    --dex-file=/data/app/com.example.app/base.apk \
    --oat-file=/data/app/com.example.app/oat/arm64/base.odex \
    --instruction-set=arm64 \
    --compiler-filter=speed \  # 编译策略:speed/speed-profile/quicken
    --profile-file=/data/misc/profiles/cur/0/com.example.app/primary.prof

编译策略对比:

策略 编译时间 运行速度 使用场景
verify 最快 最慢 开发调试
quicken 较慢 首次安装(后台优化)
speed-profile 常用(根据Profile优化热点代码)
speed 最快 系统应用
everything 最慢 最快 极少使用

💡 性能优化: Android 15引入"Baseline Profile"机制,应用可以在APK中打包预编译配置,加速首次启动。


应用签名与证书管理

APK签名的作用

1. 完整性校验:确保APK未被篡改

java 复制代码
// 攻击者尝试修改APK中的DEX文件
APK原始SHA-256: a1b2c3d4...
APK修改后SHA-256: x9y8z7w6...  ← 签名校验失败!

2. 身份认证:证明应用来自声称的开发者

css 复制代码
开发者A的证书 → 签名APK → 上传Google Play
攻击者B尝试用相同包名发布 → 签名不匹配 → 拒绝上传!

3. 更新验证:只有相同签名的APK才能覆盖安装

java 复制代码
// PMS检查更新应用签名
PackageSignatures oldSignatures = oldPackage.signatures;
PackageSignatures newSignatures = newPackage.signatures;

if (!oldSignatures.equals(newSignatures)) {
    throw new PackageManagerException(
        INSTALL_FAILED_UPDATE_INCOMPATIBLE,
        "Package " + packageName + " signatures do not match previously installed version"
    );
}

签名生成与验证流程

开发者侧:生成签名

bash 复制代码
# 1. 生成密钥对(首次)
keytool -genkey -v -keystore my-release-key.jks \
    -keyalg RSA -keysize 2048 -validity 10000 \
    -alias my-key-alias

# 2. 使用apksigner签名APK(V2+V3)
apksigner sign --ks my-release-key.jks \
    --ks-key-alias my-key-alias \
    --out app-release-signed.apk \
    app-release-unsigned.apk

# 3. 验证签名
apksigner verify --verbose app-release-signed.apk

PMS侧:验证签名

java 复制代码
// ApkSignatureSchemeV3Verifier.java
public static VerifiedSigner verify(
        RandomAccessFile apk, SignatureInfo signatureInfo) throws SecurityException {

    // 1. 提取V3签名块
    ByteBuffer sigBlock = signatureInfo.signatureBlock;

    // 2. 查找签名方案V3的Magic值
    ByteBuffer v3Block = findBlock(sigBlock, APK_SIGNATURE_SCHEME_V3_BLOCK_ID);

    // 3. 解析signer块(可能有多个,支持证书轮转)
    List<VerifiedSigner> signers = new ArrayList<>();
    while (v3Block.hasRemaining()) {
        ByteBuffer signer = getLengthPrefixedSlice(v3Block);

        // 4. 提取签名证书
        ByteBuffer signedData = getLengthPrefixedSlice(signer);
        ByteBuffer signatures = getLengthPrefixedSlice(signer);
        byte[] publicKeyBytes = readLengthPrefixedByteArray(signer);

        // 5. 验证签名(使用公钥验证签名的摘要)
        PublicKey publicKey = KeyFactory.getInstance("RSA")
            .generatePublic(new X509EncodedKeySpec(publicKeyBytes));

        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initVerify(publicKey);
        sig.update(signedData);
        if (!sig.verify(readLengthPrefixedByteArray(signatures))) {
            throw new SecurityException("Signature verification failed");
        }

        // 6. 解析证书链
        List<X509Certificate> certs = parseCertificates(signer);

        signers.add(new VerifiedSigner(certs, ...));
    }

    return signers.get(0); // 返回最新的signer
}

证书轮转(Key Rotation) - Android 9+

问题:密钥泄露或过期时,如何更新签名密钥但保持应用可更新?

解决方案:V3签名支持证书轮转

java 复制代码
// 生成lineage文件(证书链)
apksigner rotate --out lineage.bin \
    --old-signer --ks old-key.jks \
    --new-signer --ks new-key.jks

// 使用新密钥签名,同时附上lineage
apksigner sign --ks new-key.jks \
    --lineage lineage.bin \
    --out app-v2.apk \
    app-unsigned.apk

lineage文件结构:

less 复制代码
Lineage {
    signers = [        Signer {            certificate = [旧证书]
            flags = HAS_ROLLED_OVER  // 标记已轮转
        },
        Signer {
            certificate = [新证书]
            flags = CURRENT          // 当前有效证书
        }
    ]
}

PMS验证lineage:

java 复制代码
// 更新应用时,PMS检查lineage
if (oldPackage.signingDetails.hasPastSigningCertificates()) {
    // 旧应用使用了证书轮转
    SigningDetails oldDetails = oldPackage.signingDetails;
    SigningDetails newDetails = newPackage.signingDetails;

    // 检查新证书是否在旧证书的lineage中
    if (oldDetails.hasAncestorOrSelf(newDetails)) {
        // 允许更新
        return;
    }
}

throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
    "New package not signed with certificates from previous version");

包信息查询与缓存机制

PackageManager API

应用可以通过PackageManager查询已安装应用的信息:

java 复制代码
// 获取PackageManager实例
PackageManager pm = context.getPackageManager();

// 1. 查询已安装应用列表
List<ApplicationInfo> installedApps = pm.getInstalledApplications(
    PackageManager.GET_META_DATA);

for (ApplicationInfo app : installedApps) {
    Log.d(TAG, "Package: " + app.packageName);
    Log.d(TAG, "UID: " + app.uid);
    Log.d(TAG, "Data Dir: " + app.dataDir);
}

// 2. 查询特定应用的PackageInfo
try {
    PackageInfo packageInfo = pm.getPackageInfo(
        "com.android.chrome",
        PackageManager.GET_PERMISSIONS | PackageManager.GET_ACTIVITIES);

    // 获取版本信息
    Log.d(TAG, "Version: " + packageInfo.versionName + " (" + packageInfo.versionCode + ")");

    // 获取权限列表
    String[] permissions = packageInfo.requestedPermissions;
    for (String perm : permissions) {
        Log.d(TAG, "Permission: " + perm);
    }

    // 获取Activity列表
    ActivityInfo[] activities = packageInfo.activities;
    for (ActivityInfo activity : activities) {
        Log.d(TAG, "Activity: " + activity.name);
    }

} catch (PackageManager.NameNotFoundException e) {
    Log.e(TAG, "Package not found");
}

// 3. 查询可以处理某个Intent的应用
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.google.com"));
List<ResolveInfo> browsers = pm.queryIntentActivities(intent,
    PackageManager.MATCH_DEFAULT_ONLY);

for (ResolveInfo info : browsers) {
    Log.d(TAG, "Browser: " + info.activityInfo.packageName);
}

// 4. 获取应用图标和名称
Drawable icon = pm.getApplicationIcon("com.android.chrome");
CharSequence label = pm.getApplicationLabel(
    pm.getApplicationInfo("com.android.chrome", 0));

组件解析与Intent匹配

ComponentResolver负责Intent到组件的匹配:

java 复制代码
// ComponentResolver.java
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType, int flags, int userId) {

    // 1. 提取Intent的关键信息
    String action = intent.getAction();
    String category = intent.getCategories() != null ?
        intent.getCategories().iterator().next() : null;
    String dataScheme = intent.getScheme();
    String dataType = resolvedType;

    // 2. 从缓存中查找匹配的Activity
    List<ParsedActivity> candidates = new ArrayList<>();

    // 按action查找
    if (action != null) {
        candidates.addAll(mActivities.get(action));
    }

    // 按category过滤
    if (category != null) {
        candidates.removeIf(activity ->
            !activity.getIntentFilters().stream()
                .anyMatch(filter -> filter.hasCategory(category)));
    }

    // 按data scheme过滤
    if (dataScheme != null) {
        candidates.removeIf(activity ->
            !activity.getIntentFilters().stream()
                .anyMatch(filter -> filter.hasDataScheme(dataScheme)));
    }

    // 3. 转换为ResolveInfo
    List<ResolveInfo> results = new ArrayList<>();
    for (ParsedActivity activity : candidates) {
        ResolveInfo ri = new ResolveInfo();
        ri.activityInfo = generateActivityInfo(activity, flags, userId);
        results.add(ri);
    }

    return results;
}

IntentFilter匹配示例:

xml 复制代码
<!-- AndroidManifest.xml -->
<activity android:name=".BrowserActivity">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http" />
        <data android:scheme="https" />
    </intent-filter>
</activity>
java 复制代码
// 这个Intent会匹配上面的Activity
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com"));
// action匹配 ✓, category匹配 ✓, scheme匹配 ✓

缓存优化

Android 15的多级缓存策略:

java 复制代码
// PackageManagerService缓存架构
class PackageManagerService {
    // L1缓存:包名 → ParsedPackage(内存,HashMap)
    private ArrayMap<String, AndroidPackage> mPackages;

    // L2缓存:组件Intent匹配(内存,专用数据结构)
    private ComponentResolver mComponentResolver;

    // L3缓存:包信息快照(磁盘,packages.xml)
    private Settings mSettings;

    // 查询优化:先查内存,未命中再查磁盘
    public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
        // 1. L1缓存查询
        AndroidPackage pkg = mPackages.get(packageName);
        if (pkg != null) {
            return generatePackageInfo(pkg, flags, userId);
        }

        // 2. L3缓存查询(磁盘)
        PackageSetting ps = mSettings.getPackageLPr(packageName);
        if (ps != null) {
            // 重新解析APK并加载到L1缓存
            pkg = scanPackageLI(ps.getPath(), ...);
            mPackages.put(packageName, pkg);
            return generatePackageInfo(pkg, flags, userId);
        }

        throw new NameNotFoundException("Package " + packageName + " not found");
    }
}

缓存失效场景:

  • 应用安装/卸载/更新
  • 系统升级(OTA)
  • 用户切换(多用户)
  • 包禁用/启用

Apex模块化与动态更新

Apex是什么?

APEX(Android Pony EXpress)是Android 10引入的模块化系统更新机制:

makefile 复制代码
传统OTA升级:
    整个System分区(~3GB) → 下载缓慢,安装耗时

APEX模块化升级:
    单个模块(~50MB) → 快速下载,即时生效

Apex文件结构:

python 复制代码
com.android.media.apex (Apex模块文件)
├── apex_manifest.pb      # 模块元数据(Protocol Buffers格式)
├── apex_payload.img      # ext4镜像(包含实际文件)
│   ├── lib/              # Native库
│   ├── etc/              # 配置文件
│   └── bin/              # 可执行文件
└── apex_pubkey           # 签名公钥

Apex安装流程

Apex模块安装与挂载的完整流程如下图所示:

图4: Apex模块安装与挂载完整流程,展示了从文件准备、签名验证、Staging准备到Loop设备挂载、系统集成的5个核心阶段

java 复制代码
// ApexManager.java (Android 15)
public void installPackage(File apexFile) throws PackageManagerException {

    // 1. 验证Apex签名
    if (!verifyApexSignature(apexFile)) {
        throw new PackageManagerException(INSTALL_FAILED_VERIFICATION_FAILURE,
            "APEX signature verification failed");
    }

    // 2. 解析apex_manifest.pb
    ApexManifest manifest = parseApexManifest(apexFile);
    String packageName = manifest.getName();
    long version = manifest.getVersion();

    // 3. 检查版本兼容性
    ApexInfo currentApex = getApexInfo(packageName);
    if (currentApex != null && currentApex.versionCode >= version) {
        throw new PackageManagerException(INSTALL_FAILED_VERSION_DOWNGRADE,
            "Cannot downgrade APEX");
    }

    // 4. Stage Apex(复制到/data/apex/active/)
    File stagedApex = new File("/data/apex/active/" + packageName + ".apex");
    copyFile(apexFile, stagedApex);

    // 5. 挂载Apex(loop设备 + ext4挂载)
    String mountPoint = "/apex/" + packageName;
    mountApex(stagedApex, mountPoint);

    // 6. 更新动态链接库搜索路径
    updateLinkerConfig(mountPoint + "/lib");

    // 7. 通知PMS更新包信息
    mPackageManager.scanApexPackage(mountPoint);
}

// 挂载Apex镜像
private void mountApex(File apexFile, String mountPoint) throws IOException {
    // 1. 提取apex_payload.img
    ZipFile zipFile = new ZipFile(apexFile);
    ZipEntry payloadEntry = zipFile.getEntry("apex_payload.img");
    File payloadFile = extractEntry(zipFile, payloadEntry);

    // 2. 创建loop设备
    String loopDevice = createLoopDevice(payloadFile.getAbsolutePath());
    // losetup /dev/block/loop0 /data/apex/active/com.android.media.apex/payload.img

    // 3. 挂载ext4文件系统
    mkdir(mountPoint);
    mount(loopDevice, mountPoint, "ext4", MS_RDONLY | MS_NODEV, null);
    // mount -t ext4 -o ro,nodev /dev/block/loop0 /apex/com.android.media
}

Apex激活流程:

bash 复制代码
安装Apex
    ↓
重启系统(必须,因为需要在启动时挂载)
    ↓
init进程扫描/data/apex/active/
    ↓
挂载所有Apex到/apex/目录
    ↓
更新linker配置(/linkerconfig/ld.config.txt)
    ↓
应用可以加载Apex中的库

Android 15的Apex增强

1. 增量Apex更新

java 复制代码
// 只下载变更的块,而非整个Apex文件
ApexFile oldApex = getInstalledApex("com.android.media");
ApexFile newApex = downloadApexFromServer("com.android.media", version);

// 计算差异
byte[] delta = calculateDelta(oldApex, newApex);
// delta大小: ~10MB (而非完整Apex的50MB)

// 应用差异
applyDelta(oldApex, delta, newApex);

2. Apex可更新组件列表(Android 15)

Apex模块 包名 功能 是否可更新
Media com.android.media 媒体编解码器
MediaProvider com.android.mediaprovider 媒体文件管理
Conscrypt com.android.conscrypt TLS/SSL加密
Tethering com.android.tethering 网络共享
Timezone Data com.android.tzdata 时区数据
DocumentsUI com.android.documentsui 文件选择器
Permission Controller com.android.permissioncontroller 权限管理
Runtime com.android.runtime ART虚拟机 ❌(核心组件)

3. Apex回滚机制

java 复制代码
// 如果新Apex导致系统启动失败,init会自动回滚
if (detectBootFailure()) {
    // 删除/data/apex/active/中的新Apex
    deleteApex("/data/apex/active/com.android.media.apex");

    // 恢复/data/apex/backup/中的旧Apex
    restoreApex("/data/apex/backup/com.android.media.apex");

    // 重启系统
    reboot();
}

实战:调试工具与常见问题

dumpsys package命令详解

bash 复制代码
# 1. 查看所有已安装应用
adb shell dumpsys package packages

# 输出示例:
# Package [com.android.chrome] (a1b2c3d4):
#   userId=10086
#   pkg=Package{...}
#   codePath=/data/app/~~xyz123==/chrome-ABC/
#   resourcePath=/data/app/~~xyz123==/chrome-ABC/base.apk
#   legacyNativeLibraryDir=/data/app/~~xyz123==/chrome-ABC/lib
#   primaryCpuAbi=arm64-v8a
#   versionCode=115 minSdk=24 targetSdk=34
#   ...

# 2. 查看特定应用的详细信息
adb shell dumpsys package com.android.chrome

# 3. 查看权限信息
adb shell dumpsys package permissions

# 4. 查看Shared UID
adb shell dumpsys package shared-users

# 5. 查看Apex模块
adb shell dumpsys package apex

# 6. 查看安装Session
adb shell dumpsys package sessions

pm命令速查

bash 复制代码
# 1. 安装APK
adb install app.apk
adb install -r app.apk  # 替换安装(保留数据)
adb install -d app.apk  # 允许降级安装

# 2. 卸载应用
adb uninstall com.example.app
adb uninstall -k com.example.app  # 保留数据目录

# 3. 列出已安装应用
adb shell pm list packages
adb shell pm list packages -s  # 只显示系统应用
adb shell pm list packages -3  # 只显示第三方应用
adb shell pm list packages -d  # 只显示禁用应用

# 4. 禁用/启用应用
adb shell pm disable-user com.example.app
adb shell pm enable com.example.app

# 5. 清除应用数据
adb shell pm clear com.example.app

# 6. 授予/撤销权限
adb shell pm grant com.example.app android.permission.CAMERA
adb shell pm revoke com.example.app android.permission.CAMERA

# 7. 查看应用安装路径
adb shell pm path com.android.chrome
# 输出: package:/data/app/~~xyz123==/chrome-ABC/base.apk

常见问题与解决方案

问题1: INSTALL_FAILED_UPDATE_INCOMPATIBLE

症状:更新应用时报错"签名不匹配"

lua 复制代码
Failure [INSTALL_FAILED_UPDATE_INCOMPATIBLE:
Package com.example.app signatures do not match previously installed version]

原因:

  • 新APK的签名与已安装APK的签名不一致
  • 开发时使用debug签名,发布时使用release签名

解决方案:

bash 复制代码
# 方案1: 完全卸载后重新安装
adb uninstall com.example.app
adb install app-release.apk

# 方案2: 使用相同签名(推荐)
# 确保debug和release使用相同的keystore

问题2: INSTALL_FAILED_NO_MATCHING_ABIS

症状:安装APK时报错"没有匹配的ABI"

css 复制代码
Failure [INSTALL_FAILED_NO_MATCHING_ABIS:Failed to extract native libraries, res=-113]

原因:

  • APK中的Native库(.so文件)架构与设备不匹配
  • 例如:APK只包含armeabi-v7a,但设备是arm64-v8a且没有32位兼容性

解决方案:

gradle 复制代码
// build.gradle - 配置ABI分离
android {
    splits {
        abi {
            enable true
            reset()
            include 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
            universalApk true  // 生成包含所有ABI的通用APK
        }
    }
}

问题3: INSTALL_FAILED_INSUFFICIENT_STORAGE

症状:安装APK时报错"存储空间不足"

css 复制代码
Failure [INSTALL_FAILED_INSUFFICIENT_STORAGE]

检查方法:

bash 复制代码
# 查看存储空间
adb shell df -h /data
# Filesystem      Size  Used  Avail Use% Mounted on
# /dev/block/dm-0  50G   48G   2G  96% /data

# 查看哪些应用占用空间最多
adb shell pm list packages -3 | while read pkg; do
    pkg=$(echo $pkg | cut -d: -f2)
    size=$(adb shell du -sh /data/data/$pkg 2>/dev/null | cut -f1)
    echo "$size $pkg"
done | sort -h

解决方案:

  • 清理缓存:adb shell pm trim-caches 500M
  • 卸载不用的应用
  • 清空应用数据:adb shell pm clear <package>

问题4: 应用安装后无法启动

症状:安装成功,但点击图标无反应

排查步骤:

bash 复制代码
# 1. 检查Manifest中是否有MAIN/LAUNCHER Activity
adb shell dumpsys package com.example.app | grep -A 5 "android.intent.action.MAIN"

# 2. 检查应用是否被禁用
adb shell pm list packages -d | grep com.example.app

# 3. 查看logcat错误
adb logcat | grep -i "exception\|error"

# 4. 检查DEX优化状态
adb shell dumpsys package com.example.app | grep "dexopt"

问题5: Apex模块安装失败

症状:Apex安装成功但重启后未生效

bash 复制代码
# 检查Apex状态
adb shell dumpsys package apex
# Status: ACTIVATED  ← 正常
# Status: STAGED     ← 需要重启
# Status: ERROR      ← 安装失败

# 查看Apex挂载点
adb shell mount | grep /apex

# 查看Apex错误日志
adb logcat -b main -s ApexManager:V

多用户与用户隔离

多用户机制

Android支持多用户(Multi-User)和工作配置文件(Work Profile):

ini 复制代码
设备所有者(Device Owner, userId=0)
├── 用户A (userId=0, Primary User)
│   ├── com.android.chrome (uid=10086)
│   ├── com.whatsapp (uid=10087)
│   └── ...
├── 用户B (userId=10, Secondary User)
│   ├── com.android.chrome (uid=1010086)  # 注意:UID = 10 * 100000 + 10086
│   ├── com.whatsapp (uid=1010087)
│   └── ...
└── 工作配置文件 (userId=11, Managed Profile)
    ├── com.android.chrome (uid=1110086)
    └── com.microsoft.office (uid=1110088)

UID计算公式:

java 复制代码
// UserHandle.java
public static int getUid(int userId, int appId) {
    return userId * PER_USER_RANGE + appId;
}

// PER_USER_RANGE = 100000
// 例如: 用户10的Chrome(appId=10086)
// UID = 10 * 100000 + 10086 = 1010086

数据隔离:

bash 复制代码
# 每个用户有独立的数据目录
/data/user/0/com.android.chrome/    # 用户0
/data/user/10/com.android.chrome/   # 用户10
/data/user/11/com.android.chrome/   # 工作配置文件

工作配置文件(Work Profile)

使用场景:企业MDM管理,实现个人应用与工作应用隔离

java 复制代码
// 创建工作配置文件
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
    Context.DEVICE_POLICY_SERVICE);

if (dpm.isProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE)) {
    Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE);
    intent.putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME,
        new ComponentName(context, MyDeviceAdminReceiver.class));
    startActivityForResult(intent, REQUEST_CODE);
}

// 在工作配置文件中安装应用
dpm.enableSystemApp(adminComponent, "com.microsoft.office");

工作配置文件特点:

  • ✅ 独立的应用沙箱
  • ✅ 企业可远程管理(安装/卸载/禁用应用)
  • ✅ 可设置独立的密码策略
  • ✅ 数据加密独立于主用户

Android 15的PMS增强特性

1. 启动性能优化

并行扫描:

java 复制代码
// PackageManagerService.java (Android 15)
private void scanSystemApps() {
    // 旧版本:顺序扫描
    // scanDirLI(new File("/system/app"), ...);
    // scanDirLI(new File("/system/priv-app"), ...);
    // 总耗时: ~2000ms

    // Android 15:并行扫描
    List<Future<?>> futures = new ArrayList<>();
    ExecutorService executor = Executors.newFixedThreadPool(4);

    futures.add(executor.submit(() -> scanDirLI(new File("/system/app"), ...)));
    futures.add(executor.submit(() -> scanDirLI(new File("/system/priv-app"), ...)));
    futures.add(executor.submit(() -> scanDirLI(new File("/vendor/app"), ...)));
    futures.add(executor.submit(() -> scanDirLI(new File("/product/app"), ...)));

    // 等待所有任务完成
    for (Future<?> future : futures) {
        future.get();
    }
    // 总耗时: ~600ms (提升70%)
}

懒加载PackageInfo:

java 复制代码
// 旧版本:启动时加载所有应用信息到内存
// 内存占用: ~200MB

// Android 15:按需加载
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
    // 1. 先查缓存(LRU Cache,只缓存100个)
    PackageInfo cached = mPackageInfoCache.get(packageName);
    if (cached != null) {
        return cached;
    }

    // 2. 未命中,从packages.xml读取基本信息
    PackageSetting ps = mSettings.getPackageLPr(packageName);

    // 3. 如果需要详细信息(flags != 0),才解析APK
    if (flags != 0) {
        AndroidPackage pkg = parsePackageLI(ps.getCodePath(), flags);
        PackageInfo info = generatePackageInfo(pkg, flags, userId);
        mPackageInfoCache.put(packageName, info);
        return info;
    }

    // 4. 只返回基本信息(快速)
    return generatePackageInfoFromSettings(ps, flags, userId);
}
// 启动内存占用: ~80MB (减少60%)

2. 签名校验增强

弃用SHA-1签名:

java 复制代码
// ApkSignatureVerifier.java (Android 15)
private static void validateSignatureAlgorithm(Signature sig) {
    String algorithm = sig.getAlgorithm();

    // Android 15强制要求SHA-256及以上
    if (algorithm.contains("SHA1") || algorithm.contains("MD5")) {
        throw new SecurityException(
            "Signature algorithm " + algorithm + " is no longer supported. "
            + "Please use SHA256withRSA or stronger.");
    }
}

支持的签名算法(Android 15):

  • ✅ SHA256withRSA (推荐)
  • ✅ SHA384withRSA
  • ✅ SHA512withRSA
  • ✅ SHA256withECDSA
  • ❌ SHA1withRSA (已禁用)
  • ❌ MD5withRSA (已禁用)

3. Apex动态更新优化

增量Apex下载:

java 复制代码
// ApexManager.java (Android 15)
public void downloadAndInstallApex(String packageName, long targetVersion) {
    // 1. 获取当前已安装的Apex
    ApexInfo currentApex = getApexInfo(packageName);

    // 2. 从服务器获取增量包(delta)
    File deltaFile = downloadApexDelta(packageName,
        currentApex.versionCode, targetVersion);
    // 下载大小: ~10MB (而非完整的50MB)

    // 3. 应用增量到本地Apex
    File newApex = applyDelta(currentApex.getPath(), deltaFile);

    // 4. 安装新Apex
    installPackage(newApex);
}

Apex预激活:

java 复制代码
// Android 15新增:无需重启即可激活部分Apex
public void activateApexWithoutReboot(String packageName) {
    ApexInfo stagedApex = getStagedApexInfo(packageName);

    // 检查是否支持动态激活
    if (stagedApex.getMetadata().isActivatableWithoutReboot()) {
        // 1. 卸载旧版本Apex
        unmountApex(packageName);

        // 2. 挂载新版本Apex
        mountApex(stagedApex.getPath(), "/apex/" + packageName);

        // 3. 重启使用该Apex的进程
        killProcessesUsingApex(packageName);

        Log.i(TAG, "Apex " + packageName + " activated without reboot");
    } else {
        Log.i(TAG, "Apex " + packageName + " requires reboot to activate");
    }
}

可动态激活的Apex:

  • ✅ MediaProvider (媒体文件管理)
  • ✅ DocumentsUI (文件选择器)
  • ✅ Timezone Data (时区数据)
  • ❌ Runtime (ART虚拟机,核心组件)
  • ❌ Conscrypt (TLS/SSL,安全关键)

总结与延伸阅读

核心要点回顾

PackageManagerService是Android应用"户籍管理"的核心:

  1. 安装管理: 从APK验证到文件部署,7个阶段确保安全可靠
  2. 签名校验: V1→V2→V3→V4,不断进化的安全机制
  3. 权限管理: UID分配+权限授予,构建应用沙箱
  4. 信息查询: 多级缓存+组件解析,支撑Intent匹配
  5. 模块化: Apex机制实现系统组件动态更新

Android 15的重大优化:

  • ⚡ 启动速度提升70%(并行扫描+懒加载)
  • 🔒 签名安全增强(弃用SHA-1)
  • 📦 Apex增量更新(节省90%下载流量)
  • 💾 内存占用减少60%(LRU缓存)

系列回顾

到目前为止,我们已经完成了Android系统核心服务的三大支柱:

服务 职责 核心文章
Zygote 进程孵化 第13篇
AMS Activity与进程管理 第14篇
PMS 应用包管理 本篇(第16篇)

知识链路:

makefile 复制代码
用户点击图标
    ↓
Launcher查询PMS:这个应用的启动Activity是什么?
    ↓
Launcher通过AMS启动Activity
    ↓
AMS检查PMS:应用进程是否已启动?
    ↓ (未启动)
AMS通过Zygote fork新进程
    ↓
新进程加载PMS注册的应用组件
    ↓
Activity显示!

参考资料

官方文档

源码路径(Android 15)

bash 复制代码
frameworks/base/services/core/java/com/android/server/pm/
├── PackageManagerService.java           # PMS主类
├── InstallPackageHelper.java            # 安装辅助类
├── PackageInstallerSession.java         # 安装Session
├── Settings.java                        # packages.xml读写
├── PackageParser2.java                  # Manifest解析
├── ComponentResolver.java               # 组件解析与Intent匹配
└── ApexManager.java                     # Apex模块管理

frameworks/base/core/java/android/content/pm/
├── PackageManager.java                  # 应用层API
├── PackageInfo.java                     # 包信息数据结构
└── ApplicationInfo.java                 # 应用信息

system/apex/
├── apexd/                               # Apex守护进程
└── docs/                                # Apex文档

系列文章


本文基于Android 15 (API Level 35)源码分析,不同厂商的定制ROM可能存在差异。 欢迎来我中的个人主页找到更多有用的知识和有趣的产品

相关推荐
城东米粉儿4 小时前
Android 计算滑动帧率 笔记
android
城东米粉儿5 小时前
Android Choreographer 和 looper 结合使用 监控
android
城东米粉儿5 小时前
Android inline Hook 笔记
android
城东米粉儿5 小时前
Android 防止 Printer 覆盖笔记
android
Android系统攻城狮9 小时前
Android tinyalsa深度解析之pcm_get_timestamp调用流程与实战(一百一十八)
android·pcm·tinyalsa·android hal·audio hal
yuezhilangniao11 小时前
win10环境变量完全指南:Java、Maven、Android、Flutter -含我的环境备份
android·java·maven
奔跑吧 android12 小时前
【车载Audio】【AudioHal 06】【高通音频架构】【深入浅出 Android Audio HAL:从加载到函数指针绑定的全链路解析】
android·音视频·audioflinger·aosp13·8295·audiohal·高通音频架构
无巧不成书021812 小时前
Kotlin Multiplatform (KMP) 鸿蒙开发整合实战|2026最新方案
android·开发语言·kotlin·harmonyos·kmp