程序員自我修養(yǎng)之PMS APP安裝流程

倉庫網(wǎng)址:http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

一、Android包信息體和解析器關(guān)系

image.png
image.png

二、文件復(fù)制

  • PMS處理安裝HandlerParams安裝參數(shù)流程圖


    image.png
  • PackageManagerService.java#installStage方法介紹

1、創(chuàng)建了一個InstallParams對象
2、創(chuàng)建并發(fā)送了一個INIT_COPY的Message消息。
3、InstallParams繼承自HandlerParams,用來記錄安裝應(yīng)用的參數(shù)。
InstallParams中有一個成員變量mArgs,是一個抽象類型InstallArgs,主要是用來執(zhí)行APK的復(fù)制,真正的實(shí)現(xiàn)類包括:
FileInstallArgs:用來完成非ASEC應(yīng)用的安裝,ASEC全稱是Android Secure External Cache
MoveInstallArgs:用來完成已安裝應(yīng)用的移動安裝

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);
       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);
       params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
       msg.obj = params;
       ...
       //發(fā)送信息拷貝INIT_COPY 信息
       mHandler.sendMessage(msg);
   }
  • PackageManagerService.java#PackageHandler

connectToService(): 用于檢查和復(fù)制可移動文件的服務(wù)
發(fā)送MCS_BOUND信息,觸發(fā)處理第一個安裝請求

       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用于標(biāo)識是否綁定了服務(wù),默認(rèn)值為false
                   if (!mBound) { 
                       Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS",
                               System.identityHashCode(mHandler));
                      //connectToService里面的DefaultContainerService是用于檢查和復(fù)制可移動文件的服務(wù)
                       if (!connectToService()) {  
                           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);
                           }
                           //綁定服務(wù)失敗則return
                           return;
                       } else { 
                           //綁定服務(wù)成功,將請求添加到ArrayList類型的mPendingInstalls中,等待處理
                           mPendingInstalls.add(idx, params);
                       }
                   } else {  
                   //已經(jīng)綁定服務(wù)
                       mPendingInstalls.add(idx, params);
                       if (idx == 0) {   //5
                           //發(fā)送MCS_BOUND類型的消息,觸發(fā)處理第一個安裝請求
                           mHandler.sendEmptyMessage(MCS_BOUND);
                       }
                   }
                   break;
               }
               ....
               }
   }
}
  • MCS_BOUND 流程處理:
case MCS_BOUND: {
            if (mContainerService == null) {         //判斷是否已經(jīng)綁定了服務(wù)
                if (!mBound) {            //綁定服務(wù)的標(biāo)識位,沒有綁定成功
                      Slog.e(TAG, "Cannot bind to media container service");
                      for (HandlerParams params : mPendingInstalls) {
                          params.serviceError();
                          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 {             // 綁定成功
                          //繼續(xù)等待綁定服務(wù)
                          Slog.w(TAG, "Waiting to connect to media container service");
                   }
            } else if (mPendingInstalls.size() > 0) {        //安裝APK的隊列
                          HandlerParams params = mPendingInstalls.get(0);   //安裝隊列有參數(shù)
                        if (params != null) {
                            Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                    System.identityHashCode(params));
                            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy");
                            if (params.startCopy()) {               //HandlerParams開始拷貝
                                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) {  //安裝隊列沒有參數(shù)
                                    if (mBound) {            //已經(jīng)綁定服務(wù),需要發(fā)送一個解綁MCS_UNBIND的message
                                    //如果沒有安裝請求了,發(fā)送解綁服務(wù)的請求
                                        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");
                                   //如果還有其他的安裝請求,接著發(fā)送MCS_BOUND消息繼續(xù)處理剩余的安裝請求       
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                        }else {
                        Slog.w(TAG, "Empty queue");
                    }
            break;
        }
  • PMS處理復(fù)制APP流程圖
    DefaultContainerService : 真正處理復(fù)制APP文件的類


    image.png
  • PackageManagerService.java#HandlerParams#startCopy

1、嘗試安裝次數(shù)是否超過4次,超過就移除安裝的列表數(shù)據(jù)
2、handleStartCopy : //復(fù)制APK文件
3、handleReturnCode : //開始安裝APK

final boolean startCopy() {
           boolean res;
           try {
               if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
               //startCopy方法嘗試的次數(shù),超過了4次,就放棄這個安裝請求
               if (++mRetries > MAX_RETRIES) {
                   Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                   mHandler.sendEmptyMessage(MCS_GIVE_UP);  //發(fā)送放棄安裝信息
                   handleServiceError();
                   return false;
               } else {
                   handleStartCopy();      //復(fù)制APK文件
                   res = true;
               }
           } catch (RemoteException e) {
               if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
               mHandler.sendEmptyMessage(MCS_RECONNECT);
               res = false;
           }
           handleReturnCode();   //處理復(fù)制APK后的安裝APK邏輯
           return res;
       }
  • PackageManagerService.java#InstallParams#handleStartCopy

1、獲取APP的部分安裝信息
2、獲取APP的安裝位置
3、InstallArgs復(fù)制APP----> FileInstallArgs復(fù)制APP---->DefaultContainerService復(fù)制APP
InstallArgs做為抽象類,F(xiàn)ileInstallArgs和MoveInstallArgs繼承InstallArgs
FileInstallArgs : 對data/data/包名(系統(tǒng)應(yīng)用)
MoveInstallArgs : 用于處理已安裝APK的移動

public void handleStartCopy() throws RemoteException {
       ...
       //確定APK的安裝位置。onSd:安裝到SD卡, onInt:內(nèi)部存儲即Data分區(qū),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分區(qū)
           Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
           ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
         //安裝標(biāo)志沖突,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);     //確定APP安裝的位置
             ...
           }
       }
       //根據(jù)InstallParams創(chuàng)建InstallArgs對象
       final InstallArgs args = createInstallArgs(this);    InstallArgs作用時:復(fù)制和重命名APK
       mArgs = args;
       if (ret == PackageManager.INSTALL_SUCCEEDED) {
              ...
           if (!origin.existing && requiredUid != -1
                   && isVerificationEnabled(
                         verifierUser.getIdentifier(), installFlags, installerUid)) {
                 ...
           } else{
               ret = args.copyApk(mContainerService, true);     // InstallArgs開始復(fù)制APP
           }
       }
       mRet = ret;
   }

private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
        ...
         try {
             final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
             //創(chuàng)建臨時文件存儲目錄
             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;
     }

三、安裝APK

  • APP安裝流程圖


    image.png

1、在安裝前檢查是否環(huán)境的可靠,如果不可靠會清除復(fù)制的APK文件。
2、installPackageTracedLI其內(nèi)部會調(diào)用PMS的installPackageLI方法,進(jìn)行APP安裝。
3、處理安裝后操作,如果安裝不成功,刪除掉安裝相關(guān)的目錄與文件。

  final boolean startCopy() {
    ......
    handleStartCopy();  //APP文件復(fù)制拷貝
    .....
    //開始安裝APP
  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);
                }
              ...
            }
        });
    }

參考文章:
https://www.kancloud.cn/alex_wsc/android-deep2/413437
http://liuwangshu.cn/tags/Android%E6%A1%86%E6%9E%B6%E5%B1%82/
出版的“Android進(jìn)階解密”這本書值得看

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容