Android包管理机制3 PMS处理APK的安装

在上一篇文章 Android包管理机制2 PackageInstaller安装APK 中,我们学习了 PackageInstaller 是如何安装 APK 的,最后会将 APK 的信息交由 PMS 处理。那么 PMS 是如何处理的呢?这篇文章会详细分析其中的细节。

一 PackageHandler处理安装消息

APK 的信息交由 PMS 后,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作。

先来查看 PackageHandler 处理安装消息的调用时序图。
在这里插入图片描述
接着上一篇文章的代码逻辑来查看 PMS 的 installStage 方法。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

void installStage(String packageName, File stagedDir, String stagedCid,
           IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
           String installerPackageName, int installerUid, UserHandle user,
           Certificate[][] certificates) {
       ...
       final Message msg = mHandler.obtainMessage(INIT_COPY);//1
       final int installReason = fixUpInstallReason(installerPackageName, installerUid,
               sessionParams.installReason);
       final InstallParams params = new InstallParams(origin, null, observer,
               sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
               verificationInfo, user, sessionParams.abiOverride,
               sessionParams.grantedRuntimePermissions, certificates, installReason);//2
       params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
       msg.obj = params;
       ...
       mHandler.sendMessage(msg);//3
   }

注释2处创建 InstallParams,它对应于包的安装数据。注释1处创建了类型为 INIT_COPY 的消息,在注释3处将 InstallParams 通过消息发送出去。

1.1 对INIT_COPY的消息的处理

处理 INIT_COPY 类型的消息的代码如下所示。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

PackageManagerService.PackageHandler

       void doHandleMessage(Message msg) {
           switch (msg.what) {
               case INIT_COPY: {
                   HandlerParams params = (HandlerParams) msg.obj;
                   int idx = mPendingInstalls.size();
                   if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                   //mBound用于标识是否绑定了服务,默认值为false
                   if (!mBound) {//1
                       Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                               System.identityHashCode(mHandler));
                       //如果没有绑定服务,重新绑定,connectToService方法内部如果绑定成功会将mBound置为true
                       if (!connectToService()) {//2
                           Slog.e(TAG, "Failed to bind to media container service");
                           params.serviceError();
                           Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                                   System.identityHashCode(mHandler));
                           if (params.traceMethod != null) {
                               Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod,
                                       params.traceCookie);
                           }
                           //绑定服务失败则return
                           return;
                       } else {
                           //绑定服务成功,将请求添加到ArrayList类型的mPendingInstalls中,等待处理
                           mPendingInstalls.add(idx, params);
                       }
                   } else {
                   //已经绑定服务
                       mPendingInstalls.add(idx, params);
                       if (idx == 0) {
                           mHandler.sendEmptyMessage(MCS_BOUND);//3
                       }
                   }
                   break;
               }
               ....
               }
   }
}

PackageHandler 继承自 Handler,它被定义在 PMS 中,doHandleMessage 方法用于处理各个类型的消息,来查看对 INIT_COPY 类型消息的处理。

注释1处的 mBound 用于标识是否绑定了 DefaultContainerService,默认值为 false。

DefaultContainerService 是用于检查和复制可移动文件的服务,这是一个比较耗时的操作,因此 DefaultContainerService 没有和 PMS 运行在同一进程中,它运行在 com.android.defcontainer 进程,通过 IMediaContainerService 和 PMS 进行 IPC 通信,如下图所示。
在这里插入图片描述

注释2处的 connectToService 方法用来绑 定DefaultContainerService。

注释3处发送 MCS_BOUND 类型的消息,触发处理第一个安装请求。查看注释2处的 connectToService 方法:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#PackageHandler

private boolean connectToService() {
          if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
                  " DefaultContainerService");
          Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
          Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
          if (mContext.bindServiceAsUser(service, mDefContainerConn,
                  Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {//1
              Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
              mBound = true;//2
              return true;
          }
          Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
          return false;
      }

注释2处如果绑定 DefaultContainerService 成功,mBound 会置为 ture 。

注释1处的 bindServiceAsUser 方法会传入 mDefContainerConn,bindServiceAsUser 方法的处理逻辑和我们调用 bindService 是类似的,服务建立连接后,会调用 onServiceConnected 方法:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

class DefaultContainerConnection implements ServiceConnection {
      public void onServiceConnected(ComponentName name, IBinder service) {
          if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
          final IMediaContainerService imcs = IMediaContainerService.Stub
                  .asInterface(Binder.allowBlocking(service));
          mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, Object));//1
      }
      public void onServiceDisconnected(ComponentName name) {
          if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
      }
  }

注释1处发送了 MCS_BOUND 类型的消息,与 PackageHandler.doHandleMessage 方法的注释3处不同的是,这里发送消息带了 Object 类型的参数,这里会对这两种情况来进行讲解,一种是消息不带 Object 类型的参数,一种是消息带 Object 类型的参数。

1.2 对MCS_BOUND类型的消息的处理

消息不带 Object 类型的参数,查看对 MCS_BOUND 类型消息的处理:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

case MCS_BOUND: {
            if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
            if (msg.obj != null) {//1
                mContainerService = (IMediaContainerService) msg.obj;//2
                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                        System.identityHashCode(mHandler));
            }
            if (mContainerService == null) {//3
                if (!mBound) {//4
                      Slog.e(TAG, "Cannot bind to media container service");
                      for (HandlerParams params : mPendingInstalls) {
                          params.serviceError();//5
                          Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                        System.identityHashCode(params));
                          if (params.traceMethod != null) {
                          Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER,
                           params.traceMethod, params.traceCookie);
                          }
                          return;
                      }   
                          //绑定失败,清空安装请求队列
                          mPendingInstalls.clear();
                   } else {
                          //继续等待绑定服务
                          Slog.w(TAG, "Waiting to connect to media container service");
                   }
            } else if (mPendingInstalls.size() > 0) {
              ...
              else {
                   Slog.w(TAG, "Empty queue");
                   }
            break;
        }

如果消息不带 Object 类型的参数,就无法满足注释1处的条件,注释2处的 IMediaContainerService 类型的 mContainerService 也无法被赋值,这样就满足了注释3处的条件。

如果满足注释4处的条件,说明还没有绑定服务,而此前已经在 PackageHandler.doHandleMessage 方法的注释2处调用绑定服务的方法了,这显然是不正常的,因此在注释5处负责处理服务发生错误的情况。如果不满足注释4处的条件,说明已经绑定服务了,就会打印出系统log,告知用户等待系统绑定服务。

消息带 Object 类型的参数

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

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);//5
                                }
                            }
                            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                        }else {
                        Slog.w(TAG, "Empty queue");//6
                    }
            break;
        }

如果 MCS_BOUND 类型消息带 Object 类型的参数就不会满足注释1处的条件,就会调用注释2处的判断,如果安装请求数不大于0就会打印出注释6处的 log,说明安装请求队列是空的。安装完一个 APK 后,就会在注释5处发出 MSC_BOUND 消息,继续处理剩下的安装请求直到安装请求队列为空。

注释3处得到安装请求队列第一个请求 HandlerParams ,如果 HandlerParams 不为 null 就会调用注释4处的 HandlerParams 的 startCopy 方法,用于开始复制 APK 的流程。

二 复制APK

先来查看复制 APK 的时序图。
在这里插入图片描述
HandlerParams 是 PMS 中的抽象类,它的实现类为 PMS 的内部类 InstallParams。HandlerParams 的 startCopy 方法如下所示。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#HandlerParams

final boolean startCopy() {
           boolean res;
           try {
               if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
               //startCopy方法尝试的次数,超过了4次,就放弃这个安装请求
               if (++mRetries > MAX_RETRIES) {//1
                   Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                   mHandler.sendEmptyMessage(MCS_GIVE_UP);//2
                   handleServiceError();
                   return false;
               } else {
                   handleStartCopy();//3
                   res = true;
               }
           } catch (RemoteException e) {
               if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
               mHandler.sendEmptyMessage(MCS_RECONNECT);
               res = false;
           }
           handleReturnCode();//4
           return res;
       }

注释1处的 mRetries 用于记录 startCopy 方法调用的次数,调用 startCopy 方法时会先自动加1,如果次数大于4次就放弃这个安装请求:在注释2处发送 MCS_GIVE_UP 类型消息,将第一个安装请求(本次安装请求)从安装请求队列 mPendingInstalls 中移除掉。

注释4处用于处理复制 APK 后的安装 APK 逻辑,第3小节中会再次提到它。注释3处调用了抽象方法 handleStartCopy,它的实现在 InstallParams 中,如下所示。

frameworks/base/services/core/java/com/android/server/pm/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分区         
           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);//1
           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);//2
             ...
           }
       }
       //根据InstallParams创建InstallArgs对象
       final InstallArgs args = createInstallArgs(this);//3
       mArgs = args;
       if (ret == PackageManager.INSTALL_SUCCEEDED) {
              ...
           if (!origin.existing && requiredUid != -1
                   && isVerificationEnabled(
                         verifierUser.getIdentifier(), installFlags, installerUid)) {
                 ...
           } else{
               ret = args.copyApk(mContainerService, true);//4
           }
       }
       mRet = ret;
   }

handleStartCopy 方法的代码很多,这里截取关键的部分。

注释1处通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 getMinimalPackageInfo 方法,该方法轻量解析 APK 并得到 APK 的少量信息,轻量解析的原因是这里不需要得到 APK 的全部信息,APK 的少量信息会封装到 PackageInfoLite 中。接着在注释2处确定 APK 的安装位置。注释3处创建了 InstallArgs,InstallArgs 是一个抽象类,定义了 APK 的安装逻辑,比如复制和重命名 APK 等,它有3个子类,都被定义在 PMS 中,如下图所示。
在这里插入图片描述
其中 FileInstallArgs 用于处理安装到非 ASEC 的存储空间的 APK,也就是内部存储空间(Data分区),AsecInstallArgs 用于处理安装到 ASEC 中(mnt/asec)即 SD 卡中的 APK。MoveInstallArgs 用于处理已安装 APK 的移动的逻辑。

对 APK 进行检查后就会在注释4处调用 InstallArgs 的 copyApk 方法进行安装。

不同的 InstallArgs 子类会有着不同的处理,这里以 FileInstallArgs 为例。FileInstallArgs 的 copyApk 方法中会直接 return FileInstallArgs 的 doCopyApk 方法:

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java#FileInstallArgs

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);//1
             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);//2
         ...
         return ret;
     }

注释1处用于创建临时存储目录,比如 /data/app/vmdl18300388.tmp,其中 18300388 是安装的 sessionId。

注释2处通过 IMediaContainerService 跨进程调用 DefaultContainerService 的 copyPackage 方法,这个方法会在 DefaultContainerService
所在的进程中将 APK 复制到临时存储目录,比如 /data/app/vmdl18300388.tmp/base.apk。

目前为止 APK 的复制工作就完成了,接着就是 APK 的安装过程了。

三 安装APK

照例先来查看安装 APK 的时序图。
在这里插入图片描述
我们回到 APK 的复制调用链的头部方法:HandlerParams 的 startCopy 方法,在注释4处会调用 handleReturnCode 方法,它的实现在 InstallParams 中,如下所示。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

 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);//1
                    synchronized (mInstallLock) {
                        installPackageTracedLI(args, res);//2
                    }
                    //安装后收尾
                    args.doPostInstall(res.returnCode, res.uid);//3
                }
              ...
            }
        });
    }

handleReturnCode 方法中只调用了 processPendingInstall 方法,注释1处用于检查 APK 的状态的,在安装前确保安装环境的可靠,如果不可靠会清除复制的 APK 文件,注释3处用于处理安装后的收尾操作,如果安装不成功,删除掉安装相关的目录与文件。主要来看注释2处的 installPackageTracedLI 方法,其内部会调用 PMS 的 installPackageLI 方法。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

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);//1
    } 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);//2
                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) {//3
            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 (shouldCheckUpgradeKeySetLP(signatureCheckPs, scanFlags)) {
                if (!checkUpgradeKeySetLP(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)) {//4
        res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
        return;
    }

    startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);

    try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
            "installPackageLI")) {
       
        if (replace) {//5
         //替换安装   
           ...
            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*/);
        }
        ...
    }
}

installPackageLI 方法的代码有将近500行,这里截取主要的部分,主要做了几件事:

  • 创建 PackageParser 解析 APK
  • 检查 APK 是否存在,如果存在就获取此前没被改名前的包名并在注释1处赋值给 PackageParser.Package 类型的 pkg,在注释3处将标志位 replace 置为 true 表示是替换安装
  • 注释3处,如果 Settings 中保存有要安装的 APK 的信息,说明此前安装过该 APK,则需要校验 APK 的签名信息,确保安全的进行替换
  • 在注释4处将临时文件重新命名,比如前面提到的 /data/app/vmdl18300388.tmp/base.apk,重命名为 /data/app/ 包名-1/base.apk。这个新命名的包名会带上一个数字后缀1,每次升级一个已有的 App,这个数字会不断的累加
  • 系统 APP 的更新安装会有两个限制,一个是系统 APP 不能在 SD 卡上替换安装,另一个是系统 APP 不能被 Instant App 替换
  • 注释5处根据 replace 来做区分,如果是替换安装就会调用 replacePackageLIF 方法,其方法内部还会对系统 APP 和非系统 APP 进行区分处理,如果是新安装 APK 会调用 installNewPackageLIF 方法

这里我们以新安装 APK 为例,会调用 PMS 的 installNewPackageLIF 方法。

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

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);
   }

installNewPackageLIF 主要做了以下3件事:

  • 扫描 APK,将 APK 的信息存储在 PackageParser.Package 类型的 newPackage 中,一个 Package 的信息包含了1个 base APK 以及0个或者多个 split APK
  • 更新该 APK 对应的 Settings 信息,Settings 用于保存所有包的动态设置
  • 如果安装成功就为新安装的应用程序准备数据,安装失败就删除 APK

安装 APK 的过程就讲到这里,就不再往下分析下去,有兴趣的同学可以接着深挖。

四 总结

本文主要讲解了 PMS 是如何处理 APK 安装的,主要有几个步骤:

  • PackageInstaller 安装 APK 时会将 APK 的信息交由 PMS 处理,PMS 通过向 PackageHandler 发送消息来驱动 APK 的复制和安装工作
  • PMS 发送 INIT_COPY 和 MCS_BOUND 类型的消息,控制 PackageHandler 来绑定 DefaultContainerService,完成复制 APK 等工作
  • 复制 APK 完成后,会开始进行安装 APK 的流程,包括安装前的检查、安装 APK 和安装后的收尾工作
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值