Skip to content

PackageHandler

追踪 PMS 前台与后台 Handler 的消息投递、安装收尾、验证超时、延迟清理和设置持久化。

基于android-17.0.0_r1
AndroidPackageManagerServicePackageHandlerHandler异步源码阅读

PackageHandler ​

PMS 的 Handler 不是一个“把耗时工作放到后台”的简单线程池。Android 17 中,消息队列承担的是状态机之间的交接:Binder 入口先完成权限和参数检查,再把安装、验证、清理、设置写入或广播收尾投递给特定 Looper;PackageHandler.handleMessage() 取出 token/state,调用 owner 完成下一阶段;需要持久化或回调时,又通过延迟消息、另一个 Handler 或重新进入 PMS 锁保护的路径发布结果。

本篇沿源码回答以下问题:

  • mHandler 和 mBackgroundHandler 为什么分工不同,分别运行在哪个线程;
  • POST_INSTALL 如何从 token 找到 InstallRequest,何时关闭 freezer,何时发安装回调;
  • PACKAGE_VERIFIED 与 CHECK_PENDING_VERIFICATION 如何处理重复、超时和被覆盖的验证状态;
  • WRITE_SETTINGS、WRITE_PACKAGE_LIST 和 dirty restrictions 如何合并、延迟和取消;
  • 消息处理中的 snapshotComputer()、锁、广播和外部 I/O 怎样串起来;
  • 消息不存在、状态已完成、回滚失败、ART 未就绪等异常路径如何降级;
  • 测试应该证明“消息被投递”还是证明“状态机完成”,两者为什么不同。

PMS012 已经讨论锁保护域,本篇只在消息处理需要时引用锁;PMS014 的 PackageManagerNative 不使用这套 Java PackageHandler 主线,因此不在本文展开。

1. 消息架构 ​

1.1 两个 Handler ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
        Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
backgroundThread.start();
Handler backgroundHandler = new Handler(backgroundThread.getLooper(),
        BACKGROUND_HANDLER_CALLBACK);

前台 Handler 由 injector 创建 PackageHandler:

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
(i, pm) -> {
    HandlerThread thread = new ServiceThread(TAG,
            Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
    thread.start();
    return new PackageHandler(thread.getLooper(), pm);
},

当前 Android 17 的两个队列分别是:

Handler线程处理方式典型消息
mHandlerServiceThread(TAG),默认优先级PackageHandler.handleMessage()安装收尾、验证、广播、回滚、设置写入
mBackgroundHandlerPackageManagerBg,后台优先级Handler.Callbackdirty package restrictions、用户限制写入

两个线程都标记 allowIo = true,但这不等于所有消息都适合长时间阻塞。前台 Handler 还受 Watchdog 监控,PMS 把它的超时时间设为十分钟,因为安装多 GB 应用时可能发生长 I/O;后台队列则承接可合并、可延后的持久化工作。

1.2 Watchdog注册 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
static final long WATCHDOG_TIMEOUT = 1000 * 60 * 10; // ten minutes

mHandler = injector.getHandler();
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

Watchdog 监控的是 Handler 线程是否长时间不响应,不是每条消息是否一定在十分钟内完成。POST_INSTALL、验证和安装后处理可能调用外部服务或等待状态机;如果线程被锁循环、无界等待或异常阻塞,Watchdog 才能提供系统级诊断信号。

2. 消息常量 ​

2.1 常量定义 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
static final int SEND_PENDING_BROADCAST = 1;
static final int POST_INSTALL = 9;
static final int WRITE_SETTINGS = 13;
static final int WRITE_DIRTY_PACKAGE_RESTRICTIONS = 14;
static final int PACKAGE_VERIFIED = 15;
static final int CHECK_PENDING_VERIFICATION = 16;
static final int WRITE_PACKAGE_LIST = 19;
static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
static final int ENABLE_ROLLBACK_STATUS = 21;
static final int ENABLE_ROLLBACK_TIMEOUT = 22;
static final int DEFERRED_NO_KILL_POST_DELETE = 23;
static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
static final int DOMAIN_VERIFICATION = 27;
static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;
static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30;

常量值是进程内协议,不是公开 API。消息的真实 payload 由 arg1、arg2 和 obj 共同决定:例如 POST_INSTALL 用 arg1 作为 token、arg2 表示是否恢复;PACKAGE_VERIFIED 用 arg1 作为 verificationId、obj 放 response;WRITE_PACKAGE_LIST 用 arg1 放 userId。

2.2 延迟参数 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
private static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
private static final long DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED =
        TimeUnit.DAYS.toMillis(1);
private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000;
static final int WRITE_SETTINGS_DELAY = 10 * 1000; // 10 seconds

延迟不是统一的“降低优先级”:

  • 删除旧 code path 默认延迟 3 秒,DeviceConfig flag 可以延长到 1 天;
  • 不杀进程的安装 observer 延迟 500 ms,需要杀进程的 observer 延迟 1000 ms;
  • Settings、package list 和 dirty restrictions 默认延迟 10 秒,用来合并连续变化。

延迟消息的生效时机是“消息从队列取出”而不是“调用 sendMessageDelayed()”。在延迟窗口内,状态可能继续变化,handler 处理时必须重新读取当前 owner 状态,而不能假设投递时的对象仍然有效。

3. Handler入口 ​

3.1 构造与异常恢复 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
final class PackageHandler extends Handler {
    private final PackageManagerService mPm;

    PackageHandler(Looper looper, PackageManagerService pm) {
        super(looper);
        mPm = pm;
    }

    @Override
    public void handleMessage(Message msg) {
        try {
            doHandleMessage(msg);
        } finally {
            Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        }
    }
}

handleMessage() 只负责包住分发函数和恢复线程优先级。某条消息临时提升了线程优先级或处理过程中抛出异常,finally 都会把当前线程恢复为默认优先级;它不负责重试消息,也不吞掉异常。

3.2 switch分发 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
void doHandleMessage(Message msg) {
    switch (msg.what) {
        case SEND_PENDING_BROADCAST: {
            mPm.sendPendingBroadcasts((String) msg.obj, msg.arg1);
            break;
        }
        case POST_INSTALL: {
            // installation completion state machine
            break;
        }
        case PACKAGE_VERIFIED: {
            // verification response state machine
            break;
        }
        case WRITE_SETTINGS: {
            mPm.writeSettings(/*sync=*/false);
            break;
        }
        // ... other message codes
    }
}

源码没有 default 分支。未知 what 会直接走完 switch,不会自动记录错误或重新入队。因此新增消息时必须同时修改常量、投递点和 handler 分支;仅仅发送一个未实现的 code 不会得到失败回调。

4. POST_INSTALL ​

4.1 安装投递 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java

java
if (doRestore) {
    if (packageSetting != null) {
        synchronized (mPm.mLock) {
            packageSetting.setPendingRestore(false);
        }
    }
} else {
    // No restore possible, or the Backup Manager was mysteriously not available.
    Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);

    Message msg = mPm.mHandler.obtainMessage(POST_INSTALL, token, 0);
    mPm.mHandler.sendMessage(msg);
}

InstallPackageHelper 先决定 backup/restore 是否完成;没有 restore 时,直接把 token 投给前台 Handler。这里的消息只表示“可以开始 post-install”,不表示安装 observer 已经收到回调,也不表示广播已经发出。

4.2 取出Request ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case POST_INSTALL: {
    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);

    final InstallRequest request;
    final int token;
    final boolean didRestore;
    synchronized (mPm.mRunningInstalls) {
        request = mPm.mRunningInstalls.get(msg.arg1);
        token = msg.arg1;
        didRestore = (msg.arg2 != 0);
        mPm.mRunningInstalls.delete(token);
    }

    if (request == null) {
        if (DEBUG_INSTALL) {
            Slog.i(TAG, "InstallRequest is null. Nothing to do for post-install token "
                    + token);
        }
        break;
    }

mRunningInstalls 用独立锁保护 token 到 InstallRequest 的映射。Handler 先在锁内取出并删除,再在锁外执行后续流程;这样同一个 token 的重复消息第二次会拿到 null,不会重复关闭 freezer 或发送回调。

4.3 收尾顺序 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
request.onRestoreFinished();
request.closeFreezer();
request.onInstallCompleted();
request.runPostInstallRunnable();
if (!request.isInstallExistingForUser()) {
    mPm.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
    // No post-install when we run restore from installExistingPackageForUser
    Slog.i(TAG, "Nothing to do for post-install token " + token);
}

Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);

顺序具有状态意义:

  1. onRestoreFinished() 结束 restore 相关状态;
  2. closeFreezer() 释放安装期间的 package freeze;
  3. onInstallCompleted() 更新 request 的安装完成状态;
  4. 执行 request 自己挂入的 post-install runnable;
  5. 普通安装进入 PMS 的 handlePackagePostInstall(),installExistingPackageForUser 则跳过普通 post-install;
  6. 最后结束异步 trace。

如果在第 2 步之前抛异常,freezer 清理责任不能假设由后面的 PMS 逻辑承担;InstallRequest 的实现和调用方需要保证异常路径可清理。

4.4 POST_INSTALL时序 ​

4.5 回调时机 ​

PMS 的安装回调可能还要经过 notifyInstallObserver()、延迟 observer 消息和 package broadcast。比如:

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
void notifyInstallObserver(InstallRequest request) {
    if (request.getObserver() != null) {
        try {
            Bundle extras = extrasForInstallResult(request);
            request.getObserver().onPackageInstalled(request.getName(),
                    request.getReturnCode(), request.getReturnMsg(), extras);
        } catch (RemoteException e) {
            Slog.i(TAG, "Observer no longer exists.");
        }
    }
}

void scheduleDeferredNoKillInstallObserver(InstallRequest request) {
    String packageName = request.getPkg().getPackageName();
    mNoKillInstallObservers.put(packageName, request);
    Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
    mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}

POST_INSTALL 只是进入安装完成状态机;observer 的执行时机、是否杀进程和广播顺序由 handlePackagePostInstall() 及其后续消息决定。

5. 验证消息 ​

5.1 PACKAGE_VERIFIED ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case PACKAGE_VERIFIED: {
    final int verificationId = msg.arg1;
    final PackageVerificationState state =
            mPm.mPendingVerification.get(verificationId);
    if (state == null) {
        Slog.w(TAG, "Verification with id " + verificationId
                + " not found. It may be invalid or overridden by integrity verification");
        break;
    }
    if (state.isVerificationComplete()) {
        Slog.w(TAG, "Verification with id " + verificationId + " already complete.");
        break;
    }

    final PackageVerificationResponse response =
            (PackageVerificationResponse) msg.obj;
    VerificationUtils.processVerificationResponse(verificationId, state, response, mPm);
    break;
}

Handler 不在这里自己决定 allow/reject,而是把 response 和仍未完成的 state 交给 VerificationUtils。两个 guard 处理了两类重复/竞态:验证状态已经被移除,或另一个 verifier 已经先完成验证。

5.2 验证投递 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
final int callingUid = Binder.getCallingUid();
mHandler.post(() -> {
    final int id = verificationId >= 0 ? verificationId : -verificationId;
    final PackageVerificationState state = mPendingVerification.get(id);
    if (state == null) {
        return;
    }
    // Only allow calls from verifiers.
    final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
    final PackageVerificationResponse response =
            new PackageVerificationResponse(verificationCode, callingUid);
    msg.arg1 = id;
    msg.obj = response;
    mHandler.sendMessage(msg);
});

Binder 入口先捕获 callingUid,再把验证结果投递到 PackageHandler 线程;Handler 线程处理时使用这个 response 中的 UID,而不是依赖异步线程上的 Binder calling identity。这个模式避免了 Binder.clearCallingIdentity() 或 Handler 切线程后身份丢失。

5.3 验证超时 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case CHECK_PENDING_VERIFICATION: {
    final int verificationId = msg.arg1;
    final boolean streaming = msg.arg2 != 0;
    final PackageVerificationState state =
            mPm.mPendingVerification.get(verificationId);

    if (state == null || state.isVerificationComplete()) {
        // Not found or complete.
        break;
    }

    final PackageVerificationResponse response =
            (PackageVerificationResponse) msg.obj;
    if (!streaming && state.timeoutExtended(response.callerUid)) {
        // Timeout extended.
        break;
    }

    VerificationUtils.processVerificationResponseOnTimeout(
            verificationId, state, response, mPm);
    break;
}

超时消息不是无条件 reject:如果非 streaming 验证仍可由该 caller 延长 timeout,handler 直接结束本轮;否则才把超时 response 交给 processVerificationResponseOnTimeout()。状态机 owner 决定最终策略,handler 只负责选择分支。

5.4 验证状态图 ​

6. 设置持久化 ​

6.1 调度Settings ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
void scheduleWriteSettings() {
    // We normally invalidate when we write settings, but in cases where we delay and
    // coalesce settings writes, this strategy would have us invalidate the cache too late.
    // Invalidating on schedule addresses this problem.
    invalidatePackageInfoCache(
            PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_SETTINGS);
    ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
    if (!mHandler.hasMessages(WRITE_SETTINGS)) {
        mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
    }
}

这里先失效客户端 package info/query cache,再检查队列中是否已有 WRITE_SETTINGS。多次调用不会堆积同类消息;缓存失效发生在 schedule 时,而不是十秒后真正写盘时,避免延迟期间读到旧缓存。

6.2 Settings ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java、PackageManagerService.java

java
case WRITE_SETTINGS: {
    mPm.writeSettings(/*sync=*/false);
    break;
}

Handler 只负责把消息转给 PMS;具体写入和消息取消发生在 writeSettings()。

java
void writeSettings(boolean sync) {
    synchronized (mLock) {
        mHandler.removeMessages(WRITE_SETTINGS);
        mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
        writeSettingsLPrTEMP(sync);
        synchronized (mDirtyUsers) {
            mDirtyUsers.clear();
        }
    }
}

Handler 分支本身很短,真正写 Settings 在 PMS 的 mLock 内完成,并取消尚未处理的后台 dirty restrictions 消息。同步写入路径会清理 dirty user 集合,避免同一批状态被再次异步写入。

6.3 包列表 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java、PackageHandler.java

java
void scheduleWritePackageListLocked(int userId) {
    invalidatePackageInfoCache(
            PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_PACKAGE_LIST);
    ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
    if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
        Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
        msg.arg1 = userId;
        mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
    }
}

case WRITE_PACKAGE_LIST: {
    mPm.writePackageList(msg.arg1);
    break;
}

Handler 消息只携带 userId,真正的 Settings 写入由下面的方法在 mLock 内完成。

java
void writePackageList(int userId) {
    synchronized (mLock) {
        mHandler.removeMessages(WRITE_PACKAGE_LIST);
        mSettings.writePackageListLPr(userId);
    }
}

WRITE_PACKAGE_LIST 只保留一个待处理消息,因此连续为不同 user 调用时,后一次调用不会追加第二条消息;读源码时要继续检查调用方是否会合并 user 或另行触发全量写入。消息消费时用 arg1 指定 user,并在 mLock 下调用 Settings。

6.4 dirty集 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
void scheduleWritePackageRestrictions(@CanBeALL @UserIdInt int userId) {
    invalidatePackageInfoCache(
            PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_PACKAGE_RESTRICTIONS);
    if (userId == USER_ALL) {
        synchronized (mDirtyUsers) {
            for (int aUserId : mUserManager.getUserIds()) {
                mDirtyUsers.add(aUserId);
            }
        }
    } else {
        if (!mUserManager.exists(userId)) {
            return;
        }
        synchronized (mDirtyUsers) {
            mDirtyUsers.add(userId);
        }
    }
    if (!mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS)) {
        mBackgroundHandler.sendMessageDelayed(
                mBackgroundHandler.obtainMessage(WRITE_DIRTY_PACKAGE_RESTRICTIONS, this),
                WRITE_SETTINGS_DELAY);
    }
}

这里的合并对象是 mDirtyUsers 集合,不是消息数量。多个 user 在十秒内变脏,会被收集到集合中,只投递一条后台消息。

6.5 后台写盘 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

java
private static final Handler.Callback BACKGROUND_HANDLER_CALLBACK = new Handler.Callback() {
    @Override
    public boolean handleMessage(@NonNull Message msg) {
        switch (msg.what) {
            case WRITE_DIRTY_PACKAGE_RESTRICTIONS: {
                PackageManagerService pm = (PackageManagerService) msg.obj;
                pm.writePendingRestrictions();
                return true;
            }
            case WRITE_USER_PACKAGE_RESTRICTIONS: {
                final Runnable r = (Runnable) msg.obj;
                r.run();
                return true;
            }
        }
        return false;
    }
};

后台 callback 先调用 writePendingRestrictions(),该方法只在锁内摘取待写用户,再把 I/O 留在锁外。

java
void writePendingRestrictions() {
    final Integer[] dirtyUsers;
    synchronized (mLock) {
        mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
        synchronized (mDirtyUsers) {
            if (mDirtyUsers.isEmpty()) {
                return;
            }
            dirtyUsers = mDirtyUsers.toArray(Integer[]::new);
            mDirtyUsers.clear();
        }
    }
    mSettings.writePackageRestrictions(dirtyUsers);
}

writePendingRestrictions() 在锁内摘取并清空 dirty user 快照,释放 mLock 后才执行 mSettings.writePackageRestrictions()。这让磁盘 I/O 不阻塞包状态读写;代价是写盘期间新的变化会进入下一轮 dirty 集合,而不是修改本次 dirtyUsers 数组。

7. 其他消息 ​

7.1 延迟删除 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java、PackageHandler.java

java
void scheduleDeferredNoKillPostDelete(CleanUpArgs args) {
    Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
    long deleteDelayMillis = DEFERRED_NO_KILL_POST_DELETE_DELAY_MS;
    deleteDelayMillis = Binder.withCleanCallingIdentity(() ->
            DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED,
                    DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED));
    mHandler.sendMessageDelayed(message, deleteDelayMillis);
}

case DEFERRED_NO_KILL_POST_DELETE: {
    CleanUpArgs args = (CleanUpArgs) msg.obj;
    if (args != null) {
        mPm.cleanUpResources(args.getPackageName(), args.getCodeFile());
    }
} break;

消息只携带 code path 清理参数;如果 payload 为 null,handler 静默结束。延迟删除的目的通常是给旧进程/文件使用留出窗口,cleanUpResources() 的实际删除和异常处理由 PMS owner 完成。

7.2 Instant App ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case INSTANT_APP_RESOLUTION_PHASE_TWO: {
    InstantAppResolver.doInstantAppResolutionPhaseTwo(mPm.mContext,
            mPm.snapshotComputer(),
            mPm.mUserManager,
            mPm.mInstantAppResolverConnection,
            (InstantAppRequest) msg.obj,
            mPm.mInstantAppInstallerActivity,
            mPm.mHandler);
    break;
}

Handler 线程在执行第二阶段前重新取得 Computer snapshot;它不会复用消息投递时的旧对象。解析器可能再次向 mHandler 投递后续消息,因此这是一个消息驱动的多阶段状态机,而非单个同步函数。

7.3 回滚状态与超时 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case ENABLE_ROLLBACK_STATUS: {
    final int enableRollbackToken = msg.arg1;
    final int enableRollbackCode = msg.arg2;
    final VerifyingSession params =
            mPm.mPendingEnableRollback.get(enableRollbackToken);
    if (params == null) {
        Slog.w(TAG, "Invalid rollback enabled token " + enableRollbackToken + " received");
        break;
    }

    mPm.mPendingEnableRollback.remove(enableRollbackToken);
    if (enableRollbackCode != PackageManagerInternal.ENABLE_ROLLBACK_SUCCEEDED) {
        final Uri originUri = Uri.fromFile(params.mOriginInfo.mResolvedFile);
        Slog.w(TAG, "Failed to enable rollback for " + originUri);
        Slog.w(TAG, "Continuing with installation of " + originUri);
    }
    params.handleRollbackEnabled();
    break;
}

失败策略是“记录失败但继续安装”,不是把整个安装标成失败。token 从 mPendingEnableRollback 移除后,重复状态消息会被视为 invalid。超时分支还会向 rollback agent 发送 ACTION_CANCEL_ENABLE_ROLLBACK 广播,通知外部 owner 放弃 rollback enable。

7.4 域验证与库清理 ​

源码文件:frameworks/base/services/core/java/com/android/server/pm/PackageHandler.java

java
case DOMAIN_VERIFICATION: {
    int messageCode = msg.arg1;
    Object object = msg.obj;
    mPm.mDomainVerificationManager.runMessage(messageCode, object);
    break;
}

case PRUNE_UNUSED_STATIC_SHARED_LIBRARIES: {
    try {
        mPm.mInjector.getSharedLibrariesImpl().pruneUnusedStaticSharedLibraries(
                mPm.snapshotComputer(), Long.MAX_VALUE,
                Settings.Global.getLong(mPm.mContext.getContentResolver(),
                        Settings.Global.UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD,
                        DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD));
    } catch (IOException e) {
        Log.w(TAG, "Failed to prune unused static shared libraries :" + e.getMessage());
    }
    break;
}

域验证把 code/object 原样委托给 DomainVerificationManager;共享库清理重新取得 snapshot,并把 IOException 转成 warning 后结束消息。清理失败不会自动重试,是否下次再次调度由共享库 owner 决定。

8. 消息与锁 ​

8.1 Handler与锁 ​

PackageHandler 本身没有在 doHandleMessage() 外包 synchronized (mPm.mLock)。每个 owner 按自己的需要获取锁:

  • writeSettings()、writePackageList() 在 PMS 内部获取 mLock;
  • writePendingRestrictions() 只在摘取 dirty users 时获取 mLock,I/O 放锁外;
  • POST_INSTALL 先锁 mRunningInstalls,然后在锁外执行 request 回调和 PMS post-install;
  • INSTANT_APP_RESOLUTION_PHASE_TWO 主要使用 snapshot,不由 handler 自动加大锁。

因此不能因为代码运行在 PackageManager 线程就直接访问任意 mPackages/Settings 字段。线程归属和状态锁是两层独立约束。

8.2 发送边界 ​

延迟写入方法通常只在 dirty 集合锁内更新集合,然后在锁外检查/发送消息;持有 mLock 时发送 Handler 消息也要确认不会同步执行 callback。Handler.sendMessage() 只是入队,但如果使用 post() 到当前 Looper 或调用方随后等待 latch,就可能产生隐式顺序依赖。

8.3 异步snapshot ​

入口获取的 Computer 不能默认跨消息延迟仍然有效。真实代码在需要时重新调用 snapshotComputer():

java
mHandler.post(() -> {
    final Computer snapshot = snapshotComputer();
    // Use the state that is current when this message executes.
});

这保证消息执行时至少从当前 PMS snapshot 入口开始读取;如果业务必须保留投递时的版本,则应显式保存版本/状态并在处理时比较,而不是把 Java 对象随意跨线程共享。

9. 测试与诊断 ​

9.1 测试消息分发 ​

对 PackageHandler 的单元测试应把输入拆成 what、arg1、arg2、obj 和预置 owner state:

消息预置动作断言
POST_INSTALLmRunningInstalls[token] = requestdoHandleMessage()request 被删除,按顺序调用收尾方法
POST_INSTALL 重复map 中无 token再处理相同 token不重复 close/callback
PACKAGE_VERIFIEDpending state 未完成携带 response调用 VerificationUtils
PACKAGE_VERIFIED 已完成state complete再处理不重复完成
CHECK_PENDING_VERIFICATIONtimeout 可延长处理 timeout不立即执行 reject
WRITE_SETTINGShandler 有消息处理调用异步 writeSettings(false)
dirty restrictionsdirty users 非空后台处理锁内摘取、锁外写盘

这些断言证明的是 handler 到 owner 的分发和状态转移,不自动证明 HandlerThread 真正启动、消息延迟精确、磁盘文件内容或 Watchdog 行为。

9.2 安装收尾测试 ​

测试应验证 InstallRequest 的副作用顺序,而不是只断言 handlePackagePostInstall() 被调用:

  1. 输入一个带 restore、freezer、observer 和 post-install runnable 的 request;
  2. 投递 POST_INSTALL(token, didRestore);
  3. 断言 token 从 mRunningInstalls 删除;
  4. 断言 onRestoreFinished → closeFreezer → onInstallCompleted → runnable 的顺序;
  5. 对 installExistingForUser 断言跳过普通 post-install;
  6. 对无 request、重复 token 和 observer RemoteException 断言安全结束。

9.3 持久化测试 ​

设置写入测试要区分“缓存失效已发生”和“文件已写入”:scheduleWriteSettings() 会立即 invalidation,但 WRITE_SETTINGS 十秒后才执行。测试输入连续两次 schedule 调用,断言只保留一条消息;处理消息后再断言 writeSettings(false) 清理消息和 dirty users。真正的 XML 原子写入属于 Settings/ResilientAtomicFile 测试,不应由 Handler 单测代替。

9.4 Watchdog诊断 ​

前台 Handler 被 Watchdog.addThread(mHandler, WATCHDOG_TIMEOUT) 注册。要诊断卡顿,应同时观察:

  • Handler 当前堆栈是否卡在 Installer、锁、Binder 或文件 I/O;
  • 消息队列中是否有同类消息被重复投递;
  • mRunningInstalls、mPendingVerification、mPendingEnableRollback 是否存在悬挂 token;
  • 延迟消息是否被取消后仍有 owner 状态等待回调;
  • 后台 dirty users 是否持续增长而没有完成写入。

10. 阅读路线 ​

阅读 PMS 的 Handler 代码时,按下面顺序追踪:

  1. 从消息常量找到 PackageHandler 分支,记录 payload 的每个字段来源。
  2. 反向搜索 obtainMessage()、sendMessage()、sendMessageDelayed() 和 post(),找到真正的投递方。
  3. 对 token 消息,先找 map/state owner,再看 handler 是否在处理前删除 token,判断重复消息行为。
  4. 对延迟消息,记录 delay、是否 hasMessages() 合并、是否 removeMessages() 取消,以及状态变化发生在延迟窗口时的处理方式。
  5. 对持久化消息,区分缓存 invalidation、消息入队、锁内摘取和锁外 I/O 四个时点。
  6. 对安装/验证消息,画出 owner 状态从 pending 到 complete 的转移,并标出失败时是丢弃、重试、继续主流程还是广播通知。
  7. 检查异步边界后是否重新取得 snapshot、Binder UID 是否提前保存、锁是否在回调前释放。
  8. 最后读测试,明确它证明了分发、状态转移、延迟合并还是实际磁盘/线程行为。

小结 ​

Android 17 的 PMS Handler 是多阶段状态机的调度边界:

  • mHandler 运行 PackageHandler,承接安装收尾、验证、回滚、域验证、广播和延迟清理;mBackgroundHandler 只处理可合并、可延后的限制持久化。
  • 消息 payload 是进程内协议,what、arg1、arg2、obj 必须和投递点成对阅读;未知消息不会自动报错或重试。
  • POST_INSTALL 先从 token map 原子取出并删除 request,再按 restore、freezer、completed、runnable、PMS post-install 的顺序收尾;重复 token 会安全结束。
  • 验证消息在 Handler 侧只做 state lookup、重复/超时 guard,最终 allow/reject 由 VerificationUtils 和验证状态 owner 决定。
  • 设置写入在 schedule 时立即失效客户端缓存,消息阶段负责延迟合并;dirty restrictions 在锁内摘取 user 集合,锁外写盘。
  • Handler 不自动持有 PMS 大锁;每个 owner 自己决定 mLock、局部锁、snapshot 和锁外 I/O 边界。
  • 异步消息执行时必须重新确认状态和 snapshot,不能把投递时对象、Binder calling identity 或 token 生命周期想当然地带过队列。
  • 测试应分别证明消息分发、状态转移、延迟合并、锁外 I/O 和真实文件结果,不能用一个 Handler 单测覆盖整条安装/持久化链。

下一篇 PMS014 将转向 PackageManagerNative,比较 native package manager 接口与 Java IPackageManager/PackageHandler 的边界,以及 JNI/系统服务调用为什么只暴露更窄的能力集合。