android PKMS-3 session处理流程详解

本文详细介绍了Android系统中PackageInstaller.Session类的使用,涵盖了session的初始化、跨进程通信、安装流程(包括条件判断、数据验证和异步操作)以及安全管理机制。重点讨论了分阶段安装、异步任务管理和安全控制,展示了如何通过CompletableFuture实现安装操作的并发处理。
摘要由CSDN通过智能技术生成
1 概述

关于session的处理流程,我们要先了解session是什么,它用来做什么

PackageInstaller.Session 是 Android 系统提供的一个类,用于在运行时安装应用程序包(APK)文件。通过 PackageInstaller.Session,开发者可以在应用程序中实现动态安装应用程序,而不需要依赖传统的安装方式(如通过应用商店或命令行)。

使用 PackageInstaller 和 PackageInstaller.Session 类,开发者可以进行应用程序的分阶段安装,管理安装过程中的权限请求、安装进度等操作。这样的安装方式可以提供更好的用户体验,同时也增加了安全性和灵活性。

通常情况下,PackageInstaller.Session 会被用于应用程序更新、应用内动态模块安装等场景中。通过该类,开发者可以控制应用程序的安装过程,并且可以处理各种安装过程中可能出现的情况。

2 代码流程
2.1 session的第一次处理
        public void commit(@NonNull IntentSender statusReceiver) {
            try {
                //这里调用IPackageInstallerSession的commit方法
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

IPackageInstallerSession.aidl
interface IPackageInstallerSession 

由此可以知道当次我们进行跨进程通信,systemserver进程

public class PackageInstallerSession extends IPackageInstallerSession.Stub {

    public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
        /**
         * 参数 statusReceiver 用于组件通信
         * 携带的数据 安装包名称  安装包id
         * forTransfer false
         */
        if (hasParentSessionId()) {
            /**
             * case 是否存在父session
             * 
             * 抛出异常,当次session提交失效
             */
            throw new IllegalStateException(
                    "Session " + sessionId + " is a child of multi-package session "
                            + getParentSessionId() +  " and may not be committed directly.");
        }
        //检查当次会话,无问题的话 确定当次session
        if (!markAsSealed(statusReceiver, forTransfer)) {
            return;
        }
        if (isMultiPackage()) {
            /**
             * case 多安装包情况 
             * 
             * 过程 遍历每一个安装包 并且标志 session确定
             * 
             *      只要其中一个安装包session不确定,都会导致安装失败
             */
            synchronized (mLock) {
                boolean sealFailed = false;
                for (int i = mChildSessions.size() - 1; i >= 0; --i) {
                    // seal all children, regardless if any of them fail; we'll throw/return
                    // as appropriate once all children have been processed
                    if (!mChildSessions.valueAt(i).markAsSealed(null, forTransfer)) {
                        sealFailed = true;
                    }
                }
                if (sealFailed) {
                    return;
                }
            }
        }
        //当会话确定完成 这里进行会话派发
        dispatchSessionSealed();
    }

}

总结: 系统应用进程封装session参数传入到system_server进程后,创建PackageinstallSession,开启异步任务向让session保存安装apk的文件数据,接下来执行session.commit跨进程来到PMS进行交互
当前主要进行session确定操作,检查当前系统frp是否开启且禁用安装功能,sessionid是否存在转移
检查完毕 封装会话,派发出去

  private boolean markAsSealed(@Nullable IntentSender statusReceiver, boolean forTransfer) {
        /**
         * 参数 statusReceiver 不为null  forTransfer false
         */
        Preconditions.checkState(statusReceiver != null || hasParentSessionId(),
                "statusReceiver can't be null for the root session");
        assertCallerIsOwnerOrRoot();

        synchronized (mLock) {
            assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
            assertNoWriteFileTransfersOpenLocked();
            //获取SECURE_FRP_MODE的值 FRP 是一种安全功能,旨在保护设备免受未经授权的访问和数据泄露。
            final boolean isSecureFrpEnabled =
                    (Secure.getInt(mContext.getContentResolver(), Secure.SECURE_FRP_MODE, 0) == 1);
            if (isSecureFrpEnabled
                    && !isSecureFrpInstallAllowed(mContext, Binder.getCallingUid())) {
                /**
                 * case frp开启且 frp禁止安装操作
                 * 
                 * 过程  抛出异常,当次安装失败
                 */
                throw new SecurityException("Can't install packages while in secure FRP");
            }

            if (forTransfer) {
                /**
                 * case 检查安装会话是否被修改
                 * 
                 * 过程 修改  检查俩次安装是否相同,相同 抛出异常
                 * 
                 *      未修改  不相同抛出异常
                 */
                mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
                if (mInstallerUid == mOriginalInstallerUid) {
                    throw new IllegalArgumentException("Session has not been transferred");
                }
            } else {
                if (mInstallerUid != mOriginalInstallerUid) {
                    throw new IllegalArgumentException("Session has been transferred");
                }
            }

            setRemoteStatusReceiver(statusReceiver);

            // After updating the observer, we can skip re-sealing.
            if (mSealed) {
                return true;
            }

            try {
                //设置mseal标志位定义当前会话已被seal确定
                sealLocked();
            } catch (PackageManagerException e) {
                return false;
            }
        }

        return true;
    }

2.2 session的第二次处理
    private void dispatchSessionSealed() {
        //发送消息MSG_ON_SESSION_SEALED
        mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
    }

可以看到是通过handler消息机制来驱动的,在android系统中这种驱动方式很常见

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_ON_SESSION_SEALED:
                    handleSessionSealed();
                    break;

上面session刚确认好,这里执行session确认完成流程

    private void handleSessionSealed() {
        assertSealed("dispatchSessionSealed");
        // Persist the fact that we've sealed ourselves to prevent
        // mutations of any hard links we create.
        /**
         * 这里是一个回调操作,了解这个回调的机制流程
         * mCallback 是 PackageInstallerSession的内部类 InternalCallback
         * InternalCallback 执行该方法时
         * 又会引入一个新变量mCallbacks 他实际是一个handler子类
         *  private class Callbacks extends Handler
         * 
         * 通过handler来驱动消息,最后你会看到IPackageInstallerCallback来处理回调
         * 这个一看就知道是aidl回调 那么就是会通知相关进程当前安装的一个状态
         * 可以先看我这个了解下 文章最后我会举一个例子
         */
        mCallback.onSessionSealedBlocking(this);
        //接下来就是apk文件流的验证和提交
        dispatchStreamValidateAndCommit();
    }

    private void dispatchStreamValidateAndCommit() {
        mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
    }

                case MSG_STREAM_VALIDATE_AND_COMMIT:
                    handleStreamValidateAndCommit();
                    break;

    private void handleStreamValidateAndCommit() {
        try {
            // This will track whether the session and any children were validated and are ready to
            // progress to the next phase of install
            //allSessionsReady 作为标志位 默认当前session都已经准备就绪
            boolean allSessionsReady = true;
            //这里如果是多包安装,需要对每个包的数据都进行验证
            /**
             * case 多安装包情况
             * 过程 每个子安装包都要进行数据验证,最后确认Session是否准备就绪
             */
            for (PackageInstallerSession child : getChildSessions()) {
                allSessionsReady &= child.streamValidateAndCommit();
            }
            /**
             * case 普通安装单包情况
             * 
             * 过程 直接进行streamValidateAndCommit 数据包验证操作
             *      验证成功会通过handler驱动下一次操作
             */
            if (allSessionsReady && streamValidateAndCommit()) {
                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
            }
        } catch (PackageManagerException e) {
            //失败了也要通知相关进程或者服务
            destroy();
            String msg = ExceptionUtils.getCompleteMessage(e);
            dispatchSessionFinished(e.error, msg, null);
            maybeFinishChildSessions(e.error, msg);
        }
    }

总结 这里主要针对安装apk的数据进行一个验证,多安装包,分批检查,单安装包,直接检查

    private void handleStreamValidateAndCommit() {
        try {
            // This will track whether the session and any children were validated and are ready to
            // progress to the next phase of install
            //allSessionsReady 作为标志位 默认当前session都已经准备就绪
            boolean allSessionsReady = true;
            //这里如果是多包安装,需要对每个包的数据都进行验证
            /**
             * case 多安装包情况
             * 过程 每个子安装包都要进行数据验证,最后确认Session是否准备就绪
             */
            for (PackageInstallerSession child : getChildSessions()) {
                allSessionsReady &= child.streamValidateAndCommit();
            }
            /**
             * case 普通安装单包情况
             * 
             * 过程 直接进行streamValidateAndCommit 数据包验证操作
             *      验证成功会通过handler驱动下一次操作
             */
            if (allSessionsReady && streamValidateAndCommit()) {
                mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
            }
        } catch (PackageManagerException e) {
            //失败了也要通知相关进程或者服务
            destroy();
            String msg = ExceptionUtils.getCompleteMessage(e);
            dispatchSessionFinished(e.error, msg, null);
            maybeFinishChildSessions(e.error, msg);
        }
    }

总结: session的派发中,第一次进行了session的条件判断确认,第二次进行了session内安装包的数据检查验证,接下来该来到session的第三次处理流程

                case MSG_INSTALL:
                    handleInstall();
                    break;

    private void handleInstall() {
        if (isInstallerDeviceOwnerOrAffiliatedProfileOwner()) {
            //跟踪设备上应用程序的安装情况
            DevicePolicyEventLogger
                    .createEvent(DevicePolicyEnums.INSTALL_PACKAGE)
                    .setAdmin(getInstallSource().installerPackageName)
                    .write();
        }

        /**
         * Stops the installation of the whole session set if one session needs user action
         * in its belong session set. When the user answers the yes,
         * {@link #setPermissionsResult(boolean)} is called and then {@link #MSG_INSTALL} is
         * handled to come back here to check again.
         */
        if (sendPendingUserActionIntentIfNeeded()) {
            return;
        }
        //根据参数params的isStaged检查当前是否分阶段验证
        /** 
         * params 是packageInstallerSession构建的时候传入的 PackageInstaller.SessionParams
         * 封装着安装包的相关信息
         * case 是否分阶段
         * 过程 分阶段使用mStagedSession
         *      不分阶段使用verify
         * 这里分析不分阶段
        */
        if (params.isStaged) {
            mStagedSession.verifySession();
        } else {
            //普通验证流程
            verify();
        }
    }

    private void verify() {
        try {
            /**
             * 可以看到verify主要的工作就是解析安装包的文件数据
             * case 多安装包
             * 过程  遍历子安装包解析每一个子安装包的文件
             * 
             * case 单安装包
             * 
             * 直接解析
             */
            List<PackageInstallerSession> children = getChildSessions();
            if (isMultiPackage()) {
                for (PackageInstallerSession child : children) {
                    child.prepareInheritedFiles();
                    child.parseApkAndExtractNativeLibraries();
                }
            } else {
                prepareInheritedFiles();
                parseApkAndExtractNativeLibraries();
            }
            //解析完成执行
            verifyNonStaged();
        } catch (PackageManagerException e) {
            final String completeMsg = ExceptionUtils.getCompleteMessage(e);
            final String errorMsg = PackageManager.installStatusToString(e.error, completeMsg);
            setSessionFailed(e.error, errorMsg);
            onSessionVerificationFailure(e.error, errorMsg);
        }
    }

解析涉及代码不多,我们也看下解析相关

   private void parseApkAndExtractNativeLibraries() throws PackageManagerException {
        synchronized (mLock) {
            /**
             * 在解析安装包文件前会进行一些case判断
             * case 当前解析的目录/文件是否已经被使用了
             * 
             * case mDestroyed = true
             * 
             * case mSealed = false
             * 
             * 这三种情况都会抛出异常,提示安装失败原因
             */
            if (mStageDirInUse) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session files in use");
            }
            if (mDestroyed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session destroyed");
            }
            if (!mSealed) {
                throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                        "Session not sealed");
            }
            Objects.requireNonNull(mPackageName);
            Objects.requireNonNull(mSigningDetails);
            Objects.requireNonNull(mResolvedBaseFile);
            final PackageLite result;
            /**
             * case 判断当次解析的session是否为apex
             * 
             * 过程 这里我们分析apk安装 所以执行getOrParsePackageLiteLocked 解析拿到PackageLite
             * 
             * 这个PackageLite是包含应用程序相关信息,当然不是完整的,后续会看到他的具体作用
             * 
             * 最后解析apk包含的lib库文件
             */
            if (!isApexSession()) {
                // For mode inherit existing, it would link/copy existing files to stage dir in
                // prepareInheritedFiles(). Therefore, we need to parse the complete package in
                // stage dir here.
                // Besides, PackageLite may be null for staged sessions that don't complete
                // pre-reboot verification.
                result = getOrParsePackageLiteLocked(stageDir, /* flags */ 0);
            } else {
                result = getOrParsePackageLiteLocked(mResolvedBaseFile, /* flags */ 0);
            }
            if (result != null) {
                mPackageLite = result;
                if (!isApexSession()) {
                    synchronized (mProgressLock) {
                        mInternalProgress = 0.5f;
                        computeProgressLocked(true);
                    }
                    extractNativeLibraries(
                            mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
                }
            }
        }
    }

总结:主要就是解析apk部分信息拿到ePackageLite,解析native lib库文件
解析完成开始下一步流程

2.3 session的第三次处理
    private void verifyNonStaged()
            throws PackageManagerException {
        synchronized (mLock) {
            markStageDirInUseLocked();
        }
        //这里主要进行session 验证 验证通过执行内部回调函数
        /**
         * 这里通过会话提供器获取会话验证器,来验证当前安装会话
         * 
         * 结果 error 报错  msg  异常消息
         * 
         * 验证安装会话后,会通过匿名函数回调的方式通过handler来给到主线程处理callback任务
         * 
         * case 安装成功
         * 
         * 执行会话验证完成回调
         * 
         * case 安装失败
         * 执行会话验证失败的回调
         */
        mSessionProvider.getSessionVerifier().verify(this, (error, msg) -> {
            mHandler.post(() -> {
                if (dispatchPendingAbandonCallback()) {
                    // No need to continue if abandoned
                    return;
                }
                if (error == INSTALL_SUCCEEDED) {
                    //这里执行该方法
                    onVerificationComplete();
                } else {
                    onSessionVerificationFailure(error, msg);
                }
            });
        });
    }

总结 主要是对安装会话进行验证相关操作,sessionVerifier.verify的具体处理流程的话,后续单独出文章详细分析,先了解宏观的结构

    private void onVerificationComplete() {
        //Android应用安装过程中,引入了"staged"(暂存)阶段作为安装的一部分。在这个阶段,
        //安装系统会将安装操作拆分成多个步骤来执行,而不是一次性完成整个安装过程。
        //这种方式有助于提高安装的效率和稳定性,尤其是对于大型应用或多模块应用的安装
        /**
         * case 检查当次安装是否需要分阶段
         * 过程 这里执行普通安装操作
         */
        if (isStaged()) {
            //分阶段安装操作
            mStagingManager.commitSession(mStagedSession);
            sendUpdateToRemoteStatusReceiver(INSTALL_SUCCEEDED, "Session staged", null);
            return;
        }
        //普通安装操作
        install();
    }


在查看安装操作时,我们需要先了解,这里的一个用到的一个java语法CompletableFuture 它也是一个异步任务的对象
由java8 引入的一个概念,我们可以管理异步任务的完成状态,并且可以对异步任务的处理结果进行相关处理

如何创建completableFuture对象
CompletableFuture.supplyAsync方法,或者CompletableFuture.runAsync方法

我们也可以组成多个completableFuture对象,通过使用thenApply,thenAccept,thenCombine方法来管理这些异步任务的执行顺序

eg

java
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

public class CompletableFutureDemo {

    public static void main(String[] args) {
        // 创建一个CompletableFuture来执行异步任务
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            try {
                // 模拟耗时操作
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Hello, CompletableFuture!";
        });

        // 定义在CompletableFuture完成后执行的操作
        CompletableFuture<Void> resultFuture = future.thenAccept(result -> {
            System.out.println("Result received: " + result);
        });

        // 等待异步任务完成
        resultFuture.join();

        System.out.println("CompletableFuture demo finished.");
    }
}

如何处理completableFuture抛出的异常
使用exceptionally方法
eg

import java.util.concurrent.CompletableFuture;

public class CompletableFutureExceptionDemo {

    public static void main(String[] args) {
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模拟一个会抛出异常的异步任务
            if (Math.random() < 0.5) {
                throw new RuntimeException("Simulated exception");
            }
            return 42;
        });

        // 使用exceptionally()处理异常
        CompletableFuture<Integer> resultFuture = future.exceptionally(ex -> {
            System.out.println("Exception occurred: " + ex.getMessage());
            return -1; // 返回一个备用结果
        });

        resultFuture.thenAccept(result -> {
            System.out.println("Result: " + result);
        });

        try {
            resultFuture.get(); // 等待异步任务完成
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

使用allof方法可以等待所有任务执行完成
eg

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class CompletableFutureAllOfDemo {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Result from future1");
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Result from future2");
        CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "Result from future3");

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);

        // 等待所有CompletableFuture完成
        allFutures.get();

        System.out.println("All CompletableFuture completed.");
        System.out.println("Result 1: " + future1.get());
        System.out.println("Result 2: " + future2.get());
        System.out.println("Result 3: " + future3.get());
    }


接着我们接着看install函数

    private CompletableFuture<Void> install() {
        //获取异步任务集合里面存放着安装的结果 
        //从这里可以猜到installNonStaged可能是具体的安装操作
        List<CompletableFuture<InstallResult>> futures = installNonStaged();
        //根据集合的数量创建对应数组
        CompletableFuture<InstallResult>[] arr = new CompletableFuture[futures.size()];
        //这里当安装操作都完成后,获取安装的结果和异常,执行异步任务
        return CompletableFuture.allOf(futures.toArray(arr)).whenComplete((r, t) -> {
            if (t == null) {
                //没有异常的情况,处理异步任务完成的结果,派发通知当前安装会话完成
                setSessionApplied();
                for (CompletableFuture<InstallResult> f : futures) {
                    InstallResult result = f.join();
                    result.session.dispatchSessionFinished(
                            INSTALL_SUCCEEDED, "Session installed", result.extras);
                }
            } else {
                //安装存在异常,设置会话失败,派发通知当前安装会话失败
                PackageManagerException e = (PackageManagerException) t.getCause();
                setSessionFailed(e.error,
                        PackageManager.installStatusToString(e.error, e.getMessage()));
                dispatchSessionFinished(e.error, e.getMessage(), null);
                maybeFinishChildSessions(e.error, e.getMessage());
            }
        });
    }

installNonStaged函数返回异步任务集合
现看installNonStaged函数

    private List<CompletableFuture<InstallResult>> installNonStaged() {
        try {
            //构建异步任务集合
            List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
            //首先构建异步任务加入集合
            CompletableFuture<InstallResult> future = new CompletableFuture<>();
            futures.add(future);
            //将异步任务关联到InstallParams中
            final InstallParams installingSession = makeInstallParams(future);
            if (isMultiPackage()) {
                //case1 多apk情况
                //获取子会话
                final List<PackageInstallerSession> childSessions = getChildSessions();
                //创建子会话集合
                List<InstallParams> installingChildSessions = new ArrayList<>(childSessions.size());
                for (int i = 0; i < childSessions.size(); ++i) {
                    //遍历子会话,构建异步任务关联子会话,
                    final PackageInstallerSession session = childSessions.get(i);
                    future = new CompletableFuture<>();
                    futures.add(future);
                    final InstallParams installingChildSession = session.makeInstallParams(future);
                    if (installingChildSession != null) {
                        installingChildSessions.add(installingChildSession);
                    }
                }
                //这里子会话不为空
                if (!installingChildSessions.isEmpty()) {
                    //执行安装操作
                    installingSession.installStage(installingChildSessions);
                }
            } else if (installingSession != null) {
                //对于普通安装 正常执行安装操作
                installingSession.installStage();
            }

            return futures;
        } catch (PackageManagerException e) {
            List<CompletableFuture<InstallResult>> futures = new ArrayList<>();
            futures.add(CompletableFuture.failedFuture(e));
            return futures;
        }
    }

总结: 在这里可以看到根据多包安装还是单包安装,我们会构建CompletableFuture对象,通过makeInstallParams函数将future关联到InstallParams中,当InstallParams准备好后,会调用installStage函数执行安装操作并且将InstallParams传递进去

接着回到install函数,当所有异步任务完成并且没有异常的话,设置安装session被使用,主线程等待异步任务完成,异步任务完成后执行dispatchSessionFinished派发出去通知已安装

到这里我们跟踪了installSession的整个处理流程

接下来我们会详细去看安装的流程细节,还有dispatchSessionFinished的派发最后通知了谁

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
img
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

欢迎大家一键三连支持,若需要文中资料,直接扫描文末CSDN官方认证微信卡片免费领取↓↓↓(文末还有ChatGPT机器人小福利哦,大家千万不要错过)

PS:群里还设有ChatGPT机器人,可以解答大家在工作上或者是技术上的问题
图片

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值