美文网首页Android细碎技术知识点
APK安装(一)—— PMS原理分析

APK安装(一)—— PMS原理分析

作者: 猫咪不吃鱼 | 来源:发表于2022-01-10 17:25 被阅读0次

    一、前言

    APK安装概述 中曾提及apk有四种安装场景,但无论是哪一种方式,最终会提交给 PackageManagerService 处理,只是前置的处理链路 不同,所以本篇先对 PMS 这一主要过程进行分析。frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java 【基于Android 9.0】

    二、PMS 做了哪些工作

    1、对特定的一些系统进程信息进行设置处理,并保存到 Settings
    2、解析 /etc/permissions 下相关xml文件取得系统相关权限、系统具备的相关功能等信息
    3、解析 /data/system/package.xml 文件获取已安装应用的相关信息
    4、对相关的 apk 和 jar 进行 dex 优化处理,主要是 /system/framework 目录下的相关jar和apk
    5、依据 sharedUserId 这个配置来确定 apk 运行在哪个进程,然后把运行的相关进程信息加入到 Settings 中,使得系统可以知道每个 apk 运行在哪个进程中
    6、解析 AndroidManifest.xml 文件,提炼文件中的节点信息
    7、扫描本地文件,主要针对系统应用、本地安装应用等等
    8、管理本地 apk ,包括安装、删除等

    三、安装过程

    3.1 准备工作

    前面说到 APK 的信息会提交给 PMS 进行安装的一系列工作,具体是通过 PackageHandler 发送消息来驱动 APK 的复制和安装,其时序图如下:

    上相过程中有几点需要说明:
    1、在 installStage 方法中创建了 InstallParams 对象,它对应于包的安装数据,并创建 INIT_COPY 消息并发送给 PackageHandler 进行处理;
    2、PackageHandler 在处理 INIT_COPY 消息时,会先判断是否绑定了 DefaultContainerService ,这是用于检查和赋值可移动文件的服务,比较耗时,所以和 PMS 并没有运行在同一个进程中,它们之间通过 IMediaContainerService 进行 IPC 通信,没有绑定则会进行绑定,之后 \color{red}{\small{将安装的请求添加到 mPendingInstalls 集合中,等待处理}}

    DefaultContainerConnection 同样是定义在 PMS 中,执行链路如下:

    PackageHandler -> doHandleMessage(INIT_COPY)
                   -> connectToService()
                      {
                         连接成功
                         mBound = true;
                      }
                   -> bindServiceAsUser()
                   -> DefaultContainerConnection 
                        -> onServiceConnected()
                        -> sendMessage(MCS_BOUND)
    

    3、发送 MCS_BOUND 消息时,根据发送的 Message 是否带 Object 分为两种,如下所示:

           void doHandleMessage(Message msg) {
               switch (msg.what) {
                   case INIT_COPY: {
                       ......
                       //mBound用于标识是否绑定了服务,默认值为false
                       if (!mBound) {
                            ......
                           if (!connectToService()) {
                            // 绑定 DefaultContainerConnection 成功之后会发送 
                            // mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, Object));
                            ......
                             //绑定服务失败则return
                               return;
                           } else {
                               //绑定服务成功,将请求添加到ArrayList类型的mPendingInstalls中,等待处理
                               mPendingInstalls.add(idx, params);
                           }
                       } else {
                         //已经绑定服务
                          mHandler.sendEmptyMessage(MCS_BOUND);
                       }
                       break;
                   }
                   ......
                   }
       }
    }
    
    

    4、 MCS_BOUND 消息的处理:

    case MCS_BOUND: {
                if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
                if (msg.obj != null) {
                ...
                }
                if (mContainerService == null) {//1
                 ...
                } else if (mPendingInstalls.size() > 0) {//2
                              HandlerParams params = mPendingInstalls.get(0);//3
                            if (params != null) {
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                        System.identityHashCode(params));
                                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                                if (params.startCopy()) {//4
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Checking for more work or unbind...");
                                     //如果APK安装成功,删除本次安装请求
                                    if (mPendingInstalls.size() > 0) {
                                        mPendingInstalls.remove(0);
                                    }
                                    if (mPendingInstalls.size() == 0) {
                                        if (mBound) {
                                        //如果没有安装请求了,发送解绑服务的请求
                                            if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                    "Posting delayed MCS_UNBIND");
                                            removeMessages(MCS_UNBIND);
                                            Message ubmsg = obtainMessage(MCS_UNBIND);
                                            sendMessageDelayed(ubmsg, 10000);
                                        }
                                    } else {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting MCS_BOUND for next work");
                                       //如果还有其他的安装请求,接着发送MCS_BOUND消息继续处理剩余的安装请求      
                                        mHandler.sendEmptyMessage(MCS_BOUND);
                                    }
                                }
                                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                            }else {
                            Slog.w(TAG, "Empty queue");
                        }
                break;
            }
    
    

    3.2 复制APK

    3.2.1 复制过程时序图

    HandlerParamsPMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParamsHandlerParamsstartCopy 方法如下所示:

    3.2.2 复制过程的源码分析

    PackageManagerService.java#HandlerParams

    final boolean startCopy() {
               boolean res;
               try {
                   if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                   //startCopy方法尝试的次数,超过了4次,就放弃这个安装请求
                   if (++mRetries > MAX_RETRIES) {
                       Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                  //MCS_GIVE_UP类型消息,将本次安装请求从安装请求队列mPendingInstalls中移除掉
                       mHandler.sendEmptyMessage(MCS_GIVE_UP);
                       handleServiceError();
                       return false;
                   } else {
                       handleStartCopy(); // 注释①
                       res = true;
                   }
               } catch (RemoteException e) {
                   if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                   mHandler.sendEmptyMessage(MCS_RECONNECT);
                   res = false;
               }
               handleReturnCode(); 
               return res;
           }
    
    

    注释① 处调用抽象方法 handleStartCopy ,具体实现在 InstallParams 中,如下所示:
    PackageManagerService.java#InstallParams

    public void handleStartCopy() throws RemoteException {
           ...
           //确定APK的安装位置。onSd:安装到SD卡, onInt:内部存储即Data分区,ephemeral:安装到临时存储(Instant Apps安装)            
           final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
           final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
           final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
           PackageInfoLite pkgLite = null;
           if (onInt && onSd) {
             // APK不能同时安装在SD卡和Data分区
               Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
               ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
             //安装标志冲突,Instant Apps不能安装到SD卡中
           } else if (onSd && ephemeral) {
               Slog.w(TAG,  "Conflicting flags specified for installing ephemeral on external");
               ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
           } else {
                //获取APK的少量的信息
               pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                       packageAbiOverride);
               if (DEBUG_EPHEMERAL && ephemeral) {
                   Slog.v(TAG, "pkgLite for install: " + pkgLite);
               }
           ...
           if (ret == PackageManager.INSTALL_SUCCEEDED) {
                //判断安装的位置
               int loc = pkgLite.recommendedInstallLocation;
               if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                   ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
               } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                   ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
               } 
               ...
               }else{
                 loc = installLocationPolicy(pkgLite);   // 注释①
                 ...
               }
           }
           //根据InstallParams创建InstallArgs对象
           final InstallArgs args = createInstallArgs(this);       // 注释②
           mArgs = args;
           if (ret == PackageManager.INSTALL_SUCCEEDED) {
                  ...
               if (!origin.existing && requiredUid != -1
                       && isVerificationEnabled(
                             verifierUser.getIdentifier(), installFlags, installerUid)) {
                     ...
               } else{
                   ret = args.copyApk(mContainerService, true);     // 注释③
               }
           }
           mRet = ret;
       }
    
    

    1、注释① 处确定了 APK 的安装位置。
    2、注释②处创建 InstallArgs 对象,此对象是一个抽象类,定义了 APK 的复制和重命名APK等安装逻辑,在 Android 8.x 及之前的版本中有三个子类:FileInstallArgs、AsecInstallArgs、MoveInstallArgs。其中 FileInstallArgs 用于处理安装到非ASEC的存储空间的APK,即内部存储空间(Data分区);AsecInstallArgs 用于处理安装到ASEC(mnt/asec)即SD卡中的APK;MoveInstallArgs 用于处理已安装APK的移动的逻辑;但在 Android 9.x 之后已经去掉了 AsecInstallArgs
    3、注释③ 处调用 InstallArgscopyApk 方法,这里以 FileInstallArgs 的实现为例,内部会调用 FileInstallArgsdoCopyApk 方法:

    private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            ...
             try {
                 final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
                 //创建临时文件存储目录
                 final File tempDir =
                         mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);  // 注释①
                 codeFile = tempDir;
                 resourceFile = tempDir;
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to create copy file: " + e);
                 return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
             }
             ...
             int ret = PackageManager.INSTALL_SUCCEEDED;
             ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);  // 注释②
             ...
             return ret;
         }
    
    

    1、注释① 处用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp,其中 18300388 是安装的 sessionId
    2、注释②处通过 IMediaContainerService 跨进程调用 DefaultContainerServicecopyPackage 方法,这个方法会在 DefaultContainerService 所在的进程中将 APK 复制到临时存储目录,比如 /data/app/vmdl18300388.tmp/base.apk ,至此 APK 的复制工作结束。

    3.3 APK的安装

    3.3.1 APK安装时序图

    在上述 APK 的赋值调用链的过程中,在 HandlerParamsstartCopy 方法中,会调用 handleReturnCode 方法,时序图如下:

    3.3.2 安装源码分析

    PackageManagerService#handleReturnCode:

     void handleReturnCode() {
        if (mArgs != null) {
            processPendingInstall(mArgs, mRet);
        }
    }
    
        private void processPendingInstall(final InstallArgs args, final int currentStatus) {
            mHandler.post(new Runnable() {
                public void run() {
                    mHandler.removeCallbacks(this);
                    PackageInstalledInfo res = new PackageInstalledInfo();
                    res.setReturnCode(currentStatus);
                    res.uid = -1;
                    res.pkg = null;
                    res.removedInfo = null;
                    if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                        //安装前处理
                        args.doPreInstall(res.returnCode); //注释①
                        synchronized (mInstallLock) {
                            installPackageTracedLI(args, res); //注释②
                        }
                        //安装后收尾
                        args.doPostInstall(res.returnCode, res.uid); //注释③
                    }
                  ...
                      // 安装结束发送消息
                      Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  // 注释⑤
                      mHandler.sendMessage(msg);
                }
            });
        }
    
    

    注释① 处检查APK的状态,在安装前确保安装环境的可靠,如果不可靠会清除复制的APK文件,注释③ 处会检测是否安装成功,失败则删除安装相关的目录和文件。安装完成之后在 注释⑤ 处会发送 POST_INSALL 消息通知已安装完成,此处稍后会说明。
    注释② 处的 installPackageTracedLI 会调用 PMSinstallPackageLI 方法:
    PackageManagerService.java#installPackageLI:

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        ...
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setDisplayMetrics(mMetrics);
        pp.setCallback(mPackageParserCallback);
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
        final PackageParser.Package pkg;
        try {
            //解析APK
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);  //注释①
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
        ...
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // 检查APK是否存在
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                String oldName = mSettings.getRenamedPackageLPr(pkgName);//获取没被改名前的包名
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    pkg.setPackageName(oldName);  //注释②
                    pkgName = pkg.packageName;
                    replace = true;//设置标志位表示是替换安装
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
                            + oldName + " pkgName=" + pkgName);
                } 
                ...
            }
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            //查看Settings中是否存有要安装的APK的信息,如果有就获取签名信息
            if (ps != null) {   //注释③
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);
                PackageSetting signatureCheckPs = ps;
                if (pkg.applicationInfo.isStaticSharedLibrary()) {
                    SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
                    if (libraryEntry != null) {
                        signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
                    }
                }
                //检查签名的正确性
               if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
                        if (!ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
                            res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                    + pkg.packageName + " upgrade keys do not match the "
                                    + "previously installed version");
                            return;
                        }
                } 
                ...
            }
    
            int N = pkg.permissions.size();
            for (int i = N-1; i >= 0; i--) {
               //遍历每个权限,对权限进行处理
                PackageParser.Permission perm = pkg.permissions.get(i);
                BasePermission bp = mSettings.mPermissions.get(perm.info.name);
             
                }
            }
        }
        if (systemApp) {
            if (onExternal) {
                //系统APP不能在SD卡上替换安装
                res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                        "Cannot install updates to system apps on sdcard");
                return;
            } else if (instantApp) {
                //系统APP不能被Instant App替换
                res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
                        "Cannot update a system app with an instant app");
                return;
            }
        }
        ...
        //重命名临时文件
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {  //注释④
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
    
        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
    
        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
           
            if (replace) {  //注释⑤
             //替换安装   
               ...
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res, args.installReason);
            } else {
            //安装新的APK
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res, args.installReason);
            }
        }
    
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                //更新应用程序所属的用户
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
                ps.setUpdateAvailable(false /*updateAvailable*/);
            }
            ...
        }
    }
    
    

    这里需要说明几点:
    1、注释③处,会先检测 Settings 中保存有要安装的 APK 信息,则说明安装该 APK ,因此需要检验APK 的签名信息,确保安全的进行替换。
    2、注释④处,会对临时文件重新命名,例如 /data/app/vmdl18300388.tmp/base.apk,重命名为 /data/app/包名-oONlnRRPYyleU63AveqbYA==/base.apk。新的包名后面带上的一串字母和数字的混合字符串,是使用MD5的方式对随机生成的16个字符进行加密之后的产物。
    3、注释⑤处,根据 replace 来做区分,如果是替换安装就会调用replacePackageLIF方法,其方法内部还会对系统APP和非系统APP进行区分处理,如果是新安装APK会调用installNewPackageLIF方法

    PackageManagerService.java#installNewPackageLIF

    private void installNewPackageLIF(PackageParser.Package pkg, final int policyFlags,
               int scanFlags, UserHandle user, String installerPackageName, String volumeUuid,
               PackageInstalledInfo res, int installReason) {
           ...
           try {
               //扫描APK
               PackageParser.Package newPackage = scanPackageTracedLI(pkg, policyFlags, scanFlags,
                       System.currentTimeMillis(), user);
               //更新Settings信息
               updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
               if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                   //安装成功后,为新安装的应用程序准备数据
                   prepareAppDataAfterInstallLIF(newPackage);
    
               } else {
                   //安装失败则删除APK
                   deletePackageLIF(pkgName, UserHandle.ALL, false, null,
                           PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
               }
           } catch (PackageManagerException e) {
               res.setError("Package couldn't be installed in " + pkg.codePath, e);
           }
           Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
       }
    
    

    3.4 发送消息

    在上面 processPendingInstall 方法的源码分析中,在 注释⑤ 处会发送 POST_INSTALL 消息通知安装完成,那么接下来就来具体看一看在 PackageHandler 中是怎么处理这个消息的。

    class PackageHandler extends Handler {
        void doHandleMessage(Message msg) {
            switch (msg.what) {
                ... ...
    
                case POST_INSTALL: {
                    ... ...
                     handlePackagePostInstall();
                    ... ...
                    break;
                }
            }
        }
    }
    
    private void handlePackagePostInstall(...){
          //   如果已经成功的安装了应用,在发送广播之前先授予一些必要的权限
          grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
                                            args.installGrantPermissions);
    
          // 安装完成之后发送"ACTION_PACKAGE_ADDED"广播
           sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                        packageName, extras, null, null, firstUsers);
    
          // 如果是升级更新安装,还会发送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED广播
        // 这两个广播不同之处在于PACKAGE_REPLACE将携带一个extra信息
          sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                                            packageName, extras, null, null, updateUsers);
          sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                            null, null, packageName, null, updateUsers);
    
          // 执行gc操作
          Runtime.getRuntime().gc();
    
          // 调用FileInstallArgs的doPostDeleteLI进行资源清理
         res.removedInfo.args.doPostDeleteLI(true);
    
         // 回调onPackageInstalled方法
         installObserver.onPackageInstalled(res.name, res.returnCode,
                            res.returnMsg, extras);
    }
    

    以上为主要的方法摘要,具体可总结为:
    1、第一步:这里主要是先将安装信息从安装列列表中移除,这个也是前面在processPendingInstall中添加的
    2、第二步:安装成功后,获取运行时权限
    3、第三步:获取权限后,发送ACTION_PACKAGE_ADDED广播,告诉Laucher之流,更新icon
    4、第四步:如果是升级更新则在发送两条广播

    • ACTION_PACKAGE_REPLACED:一个新版本的应用安装到设备上,替换换之前已经存在的版本
    • ACTION_MY_PACKAGE_REPLACED:应用的新版本替换旧版本被安装,只发给被更新的应用自己

    5、第五步:如果安装包中设置了PRIVATE_FLAG_FORWARD_LOCK或者被要求安装在SD卡上,则调用sendResourcesChangedBroadcast方法来发送一个资源更改的广播
    6、第六步:如果该应用是一个浏览器,则要清除浏览器设置,重新检查浏览器设置
    7、第七步:强制调用gc,出发JVM进行垃圾回收操作
    8、第八步:删除旧的安装信息
    9、回调回调 IPackageInstallObserver2 的 packageInstalled 方法。告诉 PackageInstaller 安装结果。从而实现了安装回调到UI层

    3.5 安装总结

    上述几部分大致说明 PMS 处理 APK 的主要步骤,可总结如下:
    1、当 PackageInstallerAPK 的信息提交给 PMS 处理,PMS 会通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作
    2、PMS 发送 INIT_COPYMCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService 来完成 APK 的复制等工作
    3、复制 APK 完成之后,则开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作。

    参考

    [ 1 ] https://maoao530.github.io/2017/01/18/package-install/
    [ 2 ] https://blog.csdn.net/yiranfeng/article/details/103941371
    [ 3 ] http://liuwangshu.cn/framework/pms/3-pms-install.html
    [ 4 ] https://www.freesion.com/article/5119749905/
    [ 5 ] https://www.jianshu.com/p/9ddb930153b7

    相关文章

      网友评论

        本文标题:APK安装(一)—— PMS原理分析

        本文链接:https://www.haomeiwen.com/subject/frglcrtx.html