首页 > 其他分享 >PMS简单学习【2.PMS开始安装APK-APK安装】

PMS简单学习【2.PMS开始安装APK-APK安装】

时间:2022-11-15 00:56:04浏览次数:46  
标签:PMS args request APK installResult INSTALL 安装 final

PMS简单学习【2.PMS开始安装APK-APK安装】

PMS实际对apk的处理的来源还是要从PackageInstallerSession谈起。之前在PackageInstallerSession进行完安装前的准备工作后,最后会进行
PackageInstallerSession#install()方法的调用,最后会调用PackageManagerService(PMS)的installStage()方法,接下来就到了PMS的旅程了。

在调用PMS的installStage方法后,就会发送一个INIT_COPY的消息。

void installStage(InstallParams params) {
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
    msg.obj = params;
    ......
    mHandler.sendMessage(msg);
}

在发送消息后,就回去调用HandlerParamsstartCopy方法

class PackageHandler extends Handler {
    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                HandlerParams params = (HandlerParams) msg.obj;
                if (params != null) {
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy: " + params);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                    // 会调用 HandlerParams的startCopy()方法
                    params.startCopy();
                    Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                }
                break;
            }

        }

    }
}

接着来看下HandlerParams这个类。这是用来处理用户请求或者是安装的类。其中上面调用的startCopy方法会调用handleStartCopy()handleReturnCode()这两个方法。这两个方法是抽象方法,实际调用还是子类的这俩方法。

private abstract class HandlerParams {
    /** User handle for the user requesting the information or installation. */
    private final UserHandle mUser;
    String traceMethod;
    int traceCookie;
    ......

    final void startCopy() {
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
        handleStartCopy();
        handleReturnCode();
    }

    abstract void handleStartCopy();
    abstract void handleReturnCode();
}

上面说的子类有MultiPackageVerificationParams,用来处理多个包的安装。另一个是InstallParams,相对比而言,这是针对一个apk所需要参数的定义。对于handleStartCopy方法来说,这个方法是调用远程方法来获取包信息和安装位置的值。对于分段Session,验证和安装之间可能存在差异,所以这一段也算是重新验证一下某些条件。由于现在大部分APK大小都比较大,所以很多apk安装都会是分段安装,所以MultiPackageVerificationParamshandleStartCopy方法和handleReturnCode会去分段进行处理,最终分别会调用InstallParams的对应方法,Multi...Params持有一个InstallParams的列表成员变量,这个变量是否有值取决于PackageManagerSessioninstallStage调用的是单个还是多个包的方法。下面真多单个包进行考虑。

class InstallParams extends HandlerParams {
    public void handleStartCopy() {
        if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
            mRet = INSTALL_SUCCEEDED;
            return;
        }
        PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
                mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);

        boolean isStaged = (installFlags & INSTALL_STAGED) != 0;
        if (isStaged) {
            mRet = verifyReplacingVersionCode(
                    pkgLite, requiredInstalledVersionCode, installFlags);
            if (mRet != INSTALL_SUCCEEDED) {
                return;
            }
        }
        // 调用overrideInstallLocation方法,如果需要的话,根据默认安装策略覆盖安装
        mRet = overrideInstallLocation(pkgLite);
    }
}

接着来看下handleReturnCode方法,handleReturnCode方法接着会调用processPendingInstall方法。

class InstallParams extends HandlerParams {
@Override
    void handleReturnCode() {
        processPendingInstall();
    }

    private void processPendingInstall() {
        //创建一个安装参数
        InstallArgs args = createInstallArgs(this);
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            mRet = args.copyApk();
        }
        if (mRet == PackageManager.INSTALL_SUCCEEDED) {
            F2fsUtils.releaseCompressedBlocks(
                    mContext.getContentResolver(), new File(args.getCodePath()));
        }
        // 如果mParentInstallParams不为null,则说明是多个安装包,我们这里关注else,其实最后都调用`processInstallRequestAsync`
        if (mParentInstallParams != null) {
            mParentInstallParams.tryProcessInstallRequest(args, mRet);
        } else {
            PackageInstalledInfo res = createPackageInstalledInfo(mRet);
            processInstallRequestsAsync(
                    res.returnCode == PackageManager.INSTALL_SUCCEEDED,
                    Collections.singletonList(new InstallRequest(args, res)));
        }
    }
}

上面的mRet变量存储着在进行检查是的结果,如果不为PackageManager.INSTALL_SUCCEEDED,那么就会在调用processInstallRequestsAsync时终止。
接下来就是要来异步操作并且排队来进行处理,因为包安装是需要一些时间的。

private void processInstallRequestsAsync(boolean success,
        List<InstallRequest> installRequests) {
    mHandler.post(() -> {
        List<InstallRequest> apexInstallRequests = new ArrayList<>();
        List<InstallRequest> apkInstallRequests = new ArrayList<>();
        for (InstallRequest request : installRequests) {
            if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
                apexInstallRequests.add(request);
            } else {
                apkInstallRequests.add(request);
            }
        }
        //对于APEX和APK的多包情况进行检查
        if (success) {
            for (InstallRequest request : apkInstallRequests) {
                request.args.doPreInstall(request.installResult.returnCode);
            }
            synchronized (mInstallLock) {
                installPackagesTracedLI(apkInstallRequests);
            }
            for (InstallRequest request : apkInstallRequests) {
                request.args.doPostInstall(
                        request.installResult.returnCode, request.installResult.uid);
            }
        }
        for (InstallRequest request : apkInstallRequests) {
            restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                    new PostInstallData(request.args, request.installResult, null));
        }
    });
}

从上面的方法中,我们可以简单的直到了在这里就准备要分发安装事件了,到最后restoreAndPostInstall接着会去进行POST_INSTALL事件的发送。我们可以看一下installPackagesTracedLI()方法干了些什么事情。其实这里可以省略一些内容,最后会调用到installPackagesLI方法,这个才是主角。这个方法以原子方式安装一个或多个包。主要是干了四件事情,Prepare,Scan,ReconcileCommit

Prepare : 分析当前安装的状态,分析包并对其进行初始验证
Scan : 根据prepare阶段中收集的上下文,查询已解析的包
Reconcile : 在彼此和当前系统状态的上下文中验证扫描的包,以确保安装成功
Commit : 提交所有扫描的包并且更新系统状态。这是唯一可以在安装流程中修改系统状态的地方,必须在此阶段确定所有可预测的错误。
从上面的步骤中能看到,这些步骤是环环相扣的,任何一个步骤失败就会导致安装失败。

private void installPackagesLI(List<InstallRequest> requests) {
    // 通过多个ArrayMap 对request的信息进行存储,request信息是保存在一个List当中。分别存储其扫描结果,安装参数,安装信息,准备结果,版本信息,Setting信息。
    final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
    final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
    final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
    final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
    final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
    final Map<String, PackageSetting> lastStaticSharedLibSettings =
            new ArrayMap<>(requests.size());
    final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
    boolean success = false;
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackagesLI");
        // 对每一个请求进行处理,在准备阶段和扫描阶段对所有的request进行处理,最后存入到ArrayMap当中。
        for (InstallRequest request : requests) {
            final PrepareResult prepareResult;
            try {
                // 准备阶段,调用preparePackageLI方法分析安装的状态,分析包并且进行验证
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
                prepareResult =
                        preparePackageLI(request.args, request.installResult);
            } catch (PrepareFailure prepareFailure) {
                request.installResult.setError(prepareFailure.error,
                        prepareFailure.getMessage());
                request.installResult.origPackage = prepareFailure.conflictingPackage;
                request.installResult.origPermission = prepareFailure.conflictingPermission;
                return;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
            request.installResult.installerPackageName =
                    request.args.installSource.installerPackageName;
            // 准备阶段结束会对对应的准备结果进行存储,接着吧安装信息从request当中取出进行存储,以及安装参数信息
            final String packageName = prepareResult.packageToScan.getPackageName();
            prepareResults.put(packageName, prepareResult);
            installResults.put(packageName, request.installResult);
            installArgs.put(packageName, request.args);
            try {
                // 接着就进入了扫描阶段,同上,扫描阶段的结果也会存储到对应的Map当中。
                final ScanResult result = scanPackageTracedLI(
                        prepareResult.packageToScan, prepareResult.parseFlags,
                        prepareResult.scanFlags, System.currentTimeMillis(),
                        request.args.user, request.args.abiOverride);
                if (null != preparedScans.put(result.pkgSetting.pkg.getPackageName(), result)) {
                    request.installResult.setError(
                            PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
                            "Duplicate package " + result.pkgSetting.pkg.getPackageName()
                                    + " in multi-package install request.");
                    return;
                }
                createdAppId.put(packageName, optimisticallyRegisterAppId(result));
                versionInfos.put(result.pkgSetting.pkg.getPackageName(),
                        getSettingsVersionForPackage(result.pkgSetting.pkg));
                if (result.staticSharedLibraryInfo != null) {
                    final PackageSetting sharedLibLatestVersionSetting =
                            getSharedLibLatestVersionSetting(result);
                    if (sharedLibLatestVersionSetting != null) {
                        lastStaticSharedLibSettings.put(result.pkgSetting.pkg.getPackageName(),
                                sharedLibLatestVersionSetting);
                    }
                }
            } catch (PackageManagerException e) {
                request.installResult.setError("Scanning Failed.", e);
                return;
            }
        }
        // 根据准备阶段和扫描阶段的处理结果进行调和工作
        ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                installResults,
                prepareResults,
                mSharedLibraries,
                Collections.unmodifiableMap(mPackages), versionInfos,
                lastStaticSharedLibSettings);
        CommitRequest commitRequest = null;
        synchronized (mLock) {
            Map<String, ReconciledPackage> reconciledPackages;
            try {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages");
                reconciledPackages = reconcilePackagesLocked(
                        reconcileRequest, mSettings.getKeySetManagerService(), mInjector);
            } catch (ReconcileFailure e) {
                for (InstallRequest request : requests) {
                    request.installResult.setError("Reconciliation failed...", e);
                }
                return;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
            // 调和工作完事儿后就到了最后的提交工作
            try {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "commitPackages");
                commitRequest = new CommitRequest(reconciledPackages,
                        mUserManager.getUserIds());
                commitPackagesLocked(commitRequest);
                success = true;
            } finally {
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }
        executePostCommitSteps(commitRequest);
    } finally {
        if (success) {
            for (InstallRequest request : requests) {
                final InstallArgs args = request.args;
                if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
                    continue;
                }
                if (args.signingDetails.signatureSchemeVersion != SIGNING_BLOCK_V4) {
                    continue;
                }
                // 对于增量安装,安装之前绕过验证器。如果此时我们知道包是有效的,向验证者发送通知 base.apk的根哈希。
                final String baseCodePath = request.installResult.pkg.getBaseApkPath();
                final String[] splitCodePaths = request.installResult.pkg.getSplitCodePaths();
                final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
                final int verificationId = mPendingVerificationToken++;
                final String rootHashString = PackageManagerServiceUtils
                        .buildVerificationRootHashString(baseCodePath, splitCodePaths);
                broadcastPackageVerified(verificationId, originUri,
                        PackageManager.VERIFICATION_ALLOW, rootHashString,
                        args.mDataLoaderType, args.getUser());
            }
        } else {
            for (ScanResult result : preparedScans.values()) {
                if (createdAppId.getOrDefault(result.request.parsedPackage.getPackageName(),
                        false)) {
                    cleanUpAppIdCreation(result);
                }
            }
            // TODO(patb): create a more descriptive reason than unknown in future release
            // mark all non-failure installs as UNKNOWN so we do not treat them as success
            for (InstallRequest request : requests) {
                if (request.installResult.freezer != null) {
                    request.installResult.freezer.close();
                }
                if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
                }
            }
        }
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

installPackagesLI方法中能够看到,主要是干了准备,扫描,调和以及提交的工作。

准备阶段主要是通过preparePackageLI方法来进行实现的,会根据传入的安装参数进行确认,根据安装参数的flag进行一些权限的判断。这一段代码的篇幅较大,简单来说下个人认为可值得一说的,比如检测到是系统App,并且是sdcard上的apk,就会终止更新,是不被允许的,也不能够被临时apk安装。同时还会在根据Setting来通过已安装包名来确定是更新apk还是新建apk。
扫描阶段主要是通过scanPackageTracedLI方法来对在准备阶段的信息进行扫描,根据传入的request信息和准备阶段的结果对包来进行扫描。
调和阶段主要是通过reconcilePackagesLocked来进行的,会根据准备阶段和扫描阶段的结果对所有的request进行调和,确保能够正确安装。
最后提交阶段通过commitPackagesLocked来进行,这里会根据安装的包名确定如果是系统app的更新操作,则会对旧的进行替换。

小结一下:

到这里大概的安装工作就已经完成了,其实有很多APK解析的过程,这里先是对大概流程进行梳理,回顾一下,这一段代码主要干的事情是从Session接受到安装请求后,发送一个INIT_COPY消息,随后就主要进行handleStartCopy()来进行拷贝以及通过handleReturnCode()来进行安装。

由于本篇文章是基于Android12的源码看的,相比于Android8 的代码,安装部分代码可能有些变动,所以理解上可能有些偏差,这些理解上的偏差相信在之后的不断扩充中会弥补回来。奥里给 淦就完了

标签:PMS,args,request,APK,installResult,INSTALL,安装,final
From: https://www.cnblogs.com/zhenji-li/p/16891098.html

相关文章

  • 13-3 k8s网络插件-flannel安装
    flannel安装:1、yaml方式安装:在已经安装好k8s集群之上部署flannel:获取flannelyaml文件,应用官方的yaml文件:若以下无法下载:https://kubernetes.io/docs/concepts/cl......
  • 2-2、kubernetes安装
    kubernetes安装:master,etcd:node:前提:基于主机名通信;时间同步;关闭firewalld和iptables.serviceOS:centos7.3,extra步骤:etcdcluster,仅maste......
  • apkanalyzer.bat could NOT be found in D:\Program Files\android-sdk的解决方案
    下载Commandlinetools在网站:https://developer.android.google.cn/studio?hl=zh-cn中向下滑动,在Commandlinetoolsonly部分下载工具。压缩包.zip/cmdline-tools/*......
  • 2 ansible安装
    ansible安装http://www.ansible.com.cn/1.1、有环境的情况下,直接yum,需要epelyum源yuminstallepel-release-yyum-yinstallansible1.2、没有环境,可以先下载rp......
  • 云服务器(Linux)安装部署Kafka
    云服务器(Linux)安装部署Kafka前期准备kafka的安装需要依赖于jdk,需要在服务器上提前安装好该环境,这里使用用jdk1.8。下载安装包官网地址:较新的版本已自带Zookeeper,无......
  • ElasticSearch的安装
    windows上安装1、下载指定版本并解压下载地址:https://www.elastic.co/cn/downloads/past-releases#elasticsearch2、配置JDK环境将安装包的JDK目录配置进系统环境变量......
  • hyperworks2021位安装教程
    hyperworks2021位安装教程:1.先使用“百度网盘客户端”下载hw21_EN_x64软件安装包到电脑磁盘英文路径文件夹下,并鼠标右击进行解压缩,然后在文件夹内找到hwDesktop2021.2.exe,......
  • spark (一) 入门 & 安装
    目录基本概念spark核心模块sparkcore(核心)sparksql(结构化数据操作)sparkstreaming(流式数据操作)部署模式local(本地模式)standalone(集群模式)onyarn(集群模式)......
  • ubuntu安装rabbitmq
    系统:Ubuntu20.04tips:一定要在终端sudoapt-getupdate1.安装erlangsudoapt-getinstallerlang-nox2.安装rabbitmqsudoapt-getinstallrabbitmq-serve......
  • 使用conda的CUDA运行编译环境安装软件
     作业调度系统常见作业调度系统有slurmLSFPBS,一般通过moduleload加载自己需要的软件。这些调度系统的使用可以阅读相关文档:GADI/PBS,上海交大/slurm,上科大/LSF使......