APK安裝流程詳解12——PMS中的新安裝流程上(拷貝)

APK安裝流程系列文章整體內容如下:

本片文章的主要內容如下:

  • 1、ApplicationPackageManager中相關方法的跟蹤
    • 1.1 、installPackageWithVerification(Uri,PackageInstallObserver, int, String,Uri, ManifestDigest,ContainerEncryptionParams)方法
    • 1.2、installCommon(Uri,PackageInstallObserver, int, String,int)方法解析
  • 2、PackageManagerService中相關方法的跟蹤
    • 2.1 installPackage(String, IPackageInstallObserver2, int, String,VerificationParams,String)方法解析
    • 2.2 installPackageAsUser(String, IPackageInstallObserver2, int, String, VerificationParams,String, int)方法解析
    • 2.3 PackageHandler的HandlerMesaage方法中what值為INIT_COPY的情況:
    • 2.4 PackageHandler的connectToService方法解析
    • 2.5 PackageHandler的HandlerMesaage方法中what值為MCS_BOUND的情況:
    • 2.6 HandlerParams的startCopy方法
    • 2.7 InstallParams的handleStartCopy方法

從上面一片文章我們知道InstallAppProgress里面最后更新的代碼是調用到PackageManager#installPackageWithVerificationAndEncryption方法,那我們就從這個方法開始進行跟蹤分析

總體流程大致如下:


安裝流程.png

涉及到類的流程如下:


App安裝過程中涉及類.png

我將上面整個安裝流程分為兩大步驟

  • 1、第一步:拷貝安裝包
  • 2、第二步:裝載代碼

本片文章主要講解"拷貝",即將安裝包拷貝到/data目錄下,同時為了保證本篇文章的流程性, 本片文章只講主流程,在主流程涉及到的復雜問題,或者小分支,我會提出問題,但由于簡書的篇幅問題,我就不在本篇文章深入了,我會在這篇文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充去深入講解,但我會在本篇文章中提出問題,并給出答案的索引。

一、ApplicationPackageManager中相關方法的跟蹤

通過前面的研究我們知道PackageManager是個抽象類,具體的實現類是ApplicationPackageManager,那我們就來看下ApplicationPackageManager類的installExistingPackage(String)方法

(一)、installPackageWithVerification(Uri,PackageInstallObserver, int, String,Uri, ManifestDigest,ContainerEncryptionParams)方法解析

代碼在ApplicationPackageManager.java 1370行

    @Override
    public void installPackageWithVerification(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            Uri verificationURI, ManifestDigest manifestDigest,
            ContainerEncryptionParams encryptionParams) {
        final VerificationParams verificationParams = new VerificationParams(verificationURI, null,
                null, VerificationParams.NO_UID, manifestDigest);
        installCommon(packageURI, observer, flags, installerPackageName, verificationParams,
                encryptionParams);
    }

這個方法內部很簡單,就是調用了installCommon方法。那我們繼續跟蹤下

(二)、installCommon(Uri,PackageInstallObserver, int, String,int)方法解析

代碼在ApplicationPackageManager.java 1370行

    private void installCommon(Uri packageURI,
                               PackageInstallObserver observer, int flags, String installerPackageName,
                               VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }
        if (encryptionParams != null) {
            throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
        }

        final String originPath = packageURI.getPath();
        try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }
    }

這個方法內部做了兩次判斷,然后調用的mPM的installPackage方法。我們知道IPackageManager僅僅是AIDL的Binder通道,其實真正的調用方是PackageManagerService的installPackage(String, IPackageInstallObserver2, int, String, VerificationParams,String)方法

二、PackageManagerService中相關方法的跟蹤

(一) installPackage(String, IPackageInstallObserver2, int, String,VerificationParams,String)方法解析

代碼在PackageManagerService.java 9513行

    @Override
    public void installPackage(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, VerificationParams verificationParams,
            String packageAbiOverride) {
        installPackageAsUser(originPath, observer, installFlags, installerPackageName,
                verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
    }

看下這6個參數:

  • originPath:安裝包的位置,它必須是File類型在content的URI類型。這里傳遞過來的是toString的一個字符串。
  • observer:是IPackageInstallObserver2類型的回調。通知調用者安裝完成
  • installFlags:它的值可能是如下的值中的一個
    • INSTALL_FORWARD_LOCK:表示安裝過程中是否鎖定
    • INSTALL_REPLACE_EXISTING:表示是否替換包
    • INSTALL_ALLOW_TEST:表示是否是測安裝包
      比如在AndoridManifestL里面配置了"android:testOnly",則這里的標志就是INSTALL_ALLOW_TEST
  • installerPackageName:安裝包的包名
  • verificationParams:代表驗證參數用于驗證包安裝
  • packageAbiOverride:一般傳null

我們看到PackageManagerService的installPackage方法什么都沒做,直接調用了PackageManagerService的installPackageAsUser方法

(二) installPackageAsUser(String, IPackageInstallObserver2, int, String, VerificationParams,String, int)方法解析

代碼在PackageManagerService.java 9521行

    @Override
    public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, VerificationParams verificationParams,
            String packageAbiOverride, int userId) {
        // 第一步
        // 檢查是否具有install權限
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);

        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");

         // 判斷當前用戶是否被Restricted,回調onPackageInstalled方法,安裝失敗
        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            try {
                if (observer != null) {
                    observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
                }
            } catch (RemoteException re) {
            }
            return;
        }
       
        // 第二步
        // 判斷是安裝來源是 ADB、shell和all_user
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {  
            // 如果發起端進程是shell或者root,則添加flag:INSTALL_FROM_ADB
            installFlags |= PackageManager.INSTALL_FROM_ADB;

        } else {
            // Caller holds INSTALL_PACKAGES permission, so we're less strict
            // about installerPackageName.
            // 如果不是則從flags中去掉INSTALL_FROM_ADB和INSTALL_ALL_USERS
            installFlags &= ~PackageManager.INSTALL_FROM_ADB;
            installFlags &= ~PackageManager.INSTALL_ALL_USERS;
        }

        UserHandle user;
        if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
            user = UserHandle.ALL;
        } else {
            user = new UserHandle(userId);
        }

        // Only system components can circumvent runtime permissions when installing.
        // Android 6.0 時,當權限屬于運行時權限時,需要彈出框,讓用戶授權,對于system app,應該取消彈框授權,而是直接授權
        if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
                && mContext.checkCallingOrSelfPermission(Manifest.permission
                .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
            throw new SecurityException("You need the "
                    + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                    + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
        }

        verificationParams.setInstallerUid(callingUid);

        final File originFile = new File(originPath);
        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);

        // 第三步
        // 發送"INIT_COPY"消息,構造InstallParams參數
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
                null, verificationParams, user, packageAbiOverride, null);
        mHandler.sendMessage(msg);
    }

這里面涉及到一個mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);關于這個方法的詳解請參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充中的 "一、在PackageManagerService的installPackageAsUser方法里面的代碼" 部分

就像這個方法的名字叫"installPackageAsUser"一樣,這段代碼主要是對用戶是否有權限安裝進行檢查,以及安裝app是 僅僅給當前用戶安裝,還是給所有用戶安裝。通過上面代碼可以看出,當安裝進程是shell或者root時,否則大多數情況下,僅僅安裝給當前用戶。

這里主要是對當前用戶是否有權限安裝app進行檢查,以及安裝的app是僅僅為當前用戶安裝,還是給所有的用戶安裝。從以上代碼可以得出,當安裝進程是shell或者root時,flags中又包含了INSTALL_ALL_USERS時,才會給所有用戶安裝,否則大多數情況下,僅僅安裝給當前的用戶。當我們使用pm命令安裝的時候,可以選擇安裝給哪個用戶,也可以是全部用戶,就是這個原因。

該函數主要做了以下操作:

  • 第一步:獲取權限,如果被拒絕,則退出執行
  • 第二步:設置installFlags參數,即判斷安裝來源
  • 第三步:發送了一個what值為INIT_COPY的message
    PS:這里注意這個msg的obj變量對應的InstallParams對象,這個InstallParams對象后面會多次用到;在構造InstallParams的時候注意他的兩個參數為null

該方法的主要流程如下:


installPackageAsUser的主要流程.png

PackageHandler來執行的,PackageHandler繼續字Handler,那我們來具體看看執行代碼:

(三) PackageHandler的HandlerMesaage方法中what值為INIT_COPY的情況:

因為PackageHandler繼承Handler,所以我們來看下PackageHandler的HandlerMessage方法
代碼在PackageManagerService.java 1131行

        public void handleMessage(Message msg) {
            try {
                doHandleMessage(msg);
            } finally {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            }
        }

前面的文章我們提及了PackageHandler是PackageServiceManager的內部類,并且繼承Handler,所以我們直接看PackageHandler的HandlerMesaage方法,我們看到PackageHandler的HandlerMesaage方法其實是調用doHandleMessage(Message),然后在finally代碼塊里面設置了線程的優先級為后臺線程。

那我們就來看下PackageHandler的doHandleMessage(Message)方法

1、PackageHandler的doHandleMessage方法

因為我們知道Message的what值是INIT_COPY,所以為了節省空間,我們就不粘貼其他的case了。

代碼在PackageManagerService.java 1139行


        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    // 第一步
                   // 取出InstallParams
                    HandlerParams params = (HandlerParams) msg.obj;
                     //  idx為當前需要安裝的APK個數,mPendingInstalls里面保存所有需要安裝的APK解析出來的HandlerParams參數
                    int idx = mPendingInstalls.size();
                    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
                    // If a bind was already initiated we dont really
                    // need to do anything. The pending install
                    // will be processed later on.
                    // 第二步 分兩種情況
                    // 判斷綁定的連接是否已經存在,如果已經綁定了,則mBound為true。如果是第一次調用mBound為false。
                    if (!mBound) {
                        // If this is the only one pending we might
                        // have to bind to the service again.
                         // 第三步 
                         // 連接安裝Service
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            params.serviceError();
                            return;
                        } else {
                            // Once we bind to the service, the first
                            // pending request will be processed.
                             // 如果成功綁定Service后,將新的安裝請求放入到mPendingIntalls中,等待處理
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                         // 如果之前已經綁定過服務,同樣將新的請求到mPendingIntalls中,等待處理
                        mPendingInstalls.add(idx, params);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            // 如果是第一個安裝請求,則直接發送事件MCS_BOUND觸發處理流程
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }
             ...
          }

我將這個方法分為三個部分:

  • 第一步,取出參數params,這個params就是之前傳入的InstallParams
  • 第二步,獲取等待安裝隊列的個數,并根據mBound的值進行不同的處理。mBound為true,表示已經綁定,mBound為false表示未綁定。第一次調用則mBound為默認值為false。
  • 第三步,如果是第一次調用,則調用connectToService()方法,如果不是第一次調用,且已經綁定則將params添加到mPendingInstalls(等待安裝隊列)的最后一個位置。如果是第一個安裝請求,則發送MCS_BOUND事件觸發接下來的流程

這個方法整體流程如下圖:

INIT_COPY的流程.png

如果是第一次走這個方法,則方法里面主要有兩個流程

  • 調用connectToService()方法
  • 然后調用mPendingInstalls.add(idx, params);

如果不是第一次走這個方法,則方法里面的主要流程

  • 先調用mPendingInstalls.add(idx, params);
  • 發送一個what值是MCS_BOUND 的Message

我們假設第一次,則先調用connectToService()方法

(四) PackageHandler的connectToService方法解析

代碼在PackageManagerService.java 1104行

        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.OWNER)) {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                mBound = true;
                return true;
            }
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            return false;
        }

這里可以看到bind到了一個service,這個service的ComponentName是"DEFAULT_CONTAINER_COMPONENT"這個常量,那我們就來看下這個ComponentName

代碼在PackageManagerService.java 377行

    static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer";
    static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
            DEFAULT_CONTAINER_PACKAGE,
            "com.android.defcontainer.DefaultContainerService");

所以我們知道bind的service是DefaultContainerService。綁定DefaultContainerService之后,設定進程的優先級為THREAD_PRIORITY_DEFAULT。然后等bindServiceAsUser這個方法執行完則又把線程的優先級設為THREAD_PRIORITY_BACKGROUND。

這里面涉及到一個變量mDefContainerConn

1、mDefContainerConn變量

要想研究mDefContainerConn,我們先來看下它的類型
代碼在PackageManagerService.java 921行

    final private DefaultContainerConnection mDefContainerConn =
            new DefaultContainerConnection();

我們知道了mDefContainerConn的類型是DefaultContainerConnection,那我們來看下DefaultContainerConnection這個類。

代碼在PackageManagerService.java 923行

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

        public void onServiceDisconnected(ComponentName name) {
            if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
        }
    }

通過上面代碼我們知道DefaultContainerConnection實現了ServiceConnection。所以在連接成功的時候會調用onServiceConnected方法,我們看到當連接成功的時候會向mHandler發送一個Message去,這個Message的what值為MCS_BOUND。

2、mDefContainerConn變量與DefaultContainerService

上文我們提及到mContext.bindServiceAsUser(service, mDefContainerConn,Context.BIND_AUTO_CREATE, UserHandle.OWNER)方法,其實就是"綁定"DefaultContainerService。我們知道bind一個Service,其中負責通信的ServiceConnection,而本方法中負責通信的就是mDefContainerConn。所以一旦綁定成功會執行mDefContainerConn的onServiceConnected方法。而現實是當綁定成功后在onServiceConnected中將一個IBinder轉換成了一個IMediaContainerService。這個就是onServiceConnected回調函數中根據參數傳進來的IMediaContainerService.Stub的對象引用創建的一個遠程代理對象,后面PacakgeManagerServic通過該代理對象訪問DefaultContainerService服務

關于DefaultContainerService這里我就不詳細講解了,我會在下一篇文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充中的二、DefaultContainerService詳解,進行講解

關于mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.OWNER)為什么等于bindService這里也不詳細講解了,我們會在后面的文章APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充中的 四、為什么說mContext.bindServiceAsUser等于mContext.bindService 中詳細講解

(五) PackageHandler的HandlerMesaage方法中what值為MCS_BOUND的情況:

代碼在PackageManagerService.java

        void doHandleMessage(Message msg) {
            switch (msg.what) {
                   ...

                case MCS_BOUND: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
                     // 第一步
                    if (msg.obj != null) {
                        mContainerService = (IMediaContainerService) msg.obj;
                    }
                     // 第二步 
                     // 如果綁定DefaultContainerService服務失敗,則不能安裝程序
                    if (mContainerService == null) {
                        // 第二步的情況A
                        if (!mBound) {
                            // Something seriously wrong since we are not bound and we are not
                            // waiting for connection. Bail out.
                            Slog.e(TAG, "Cannot bind to media container service");
                            for (HandlerParams params : mPendingInstalls) {
                                // Indicate service bind error
                                params.serviceError();
                            }
                            mPendingInstalls.clear();
                        } else {
                            Slog.w(TAG, "Waiting to connect to media container service");
                        }
                              // 如果安裝請求隊列不為空
                    } else if (mPendingInstalls.size() > 0) {
                      // 第二步的情況B
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            // 調動startCopy函數處理安裝請求
                            if (params.startCopy()) {
                                // We are done...  look for more work or to
                                // go idle.
                                if (DEBUG_SD_INSTALL) Log.i(TAG,
                                        "Checking for more work or unbind...");
                                // Delete pending install
                                if (mPendingInstalls.size() > 0) {
                                    // 刪除請求安裝隊列的頭元素,即下標為0的元素
                                    mPendingInstalls.remove(0);
                                }
                               // 第二步的情況B下的分支甲              
                                if (mPendingInstalls.size() == 0) {
                                    // 如果安裝請求都處理完了
                                    if (mBound) {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting delayed MCS_UNBIND");
                                        // 通過發送MCS_UNBIND消息處理斷開綁定請求
                                        removeMessages(MCS_UNBIND);
                                        Message ubmsg = obtainMessage(MCS_UNBIND);
                                        // Unbind after a little delay, to avoid
                                        // continual thrashing.
                                        sendMessageDelayed(ubmsg, 10000);
                                    }
                                } else {
                                    // 第二步的情況B下的分支乙
                                    // There are more pending requests in queue.
                                    // Just post MCS_BOUND message to trigger processing
                                    // of next pending install.
                                    // 如果還有未處理的請求,則繼續發送MCS_BOUND消息
                                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                                            "Posting MCS_BOUND for next work");
                                    mHandler.sendEmptyMessage(MCS_BOUND);
                                }
                            }
                        }
                    } else {
                        // Should never happen ideally.
                        Slog.w(TAG, "Empty queue");
                    }
                    break;
                }
       }

我將上面整體流程分為兩個步驟

  • 第一步,獲取mContainerService對象
  • 第二步,根據上面的mContainerService是否為空,進入下面兩個分支
    • 情況A:如果mContainerService為null,則綁定DefaultContainerService失敗,不能安裝,則調用HandlerParams的serviceError方法,并清空mPendingInstalls列表
    • 情況B:如果mContainerService不為null。則獲取安裝等待隊列mPendingInstalls的第一個元素,其實也就是我們最開始添加進去的InstallParams對象。并執行startCopy()方法,執行startCopy()方法后,刪除我們我們獲取的mPendingInstalls第一個元素。這時候又分甲乙兩種情況,甲情況下是沒有元素了,即mPendingInstalls.size=0;乙情況下還有元素,那我們就依次來看下
      • 分支甲:mPendingInstalls為空,且當前的綁定狀態還是"綁定"(mBound為true),則發送一個what為MCS_UNBIND的Message消息解除綁定
      • 分支乙:mPendingInstalls不為空,繼續發送MCS_BOUND消息,繼續處理下一個,只到隊列為空

上面這個方法涉及到一個核心方法即startCopy()來處理安裝請求,通過方法名叫"startCopy"我們猜測是"開始拷貝"的意思,那我們就一起來看下

(六) HandlerParams的startCopy方法

代碼在PackageManagerService.java 10253行

        final boolean startCopy() {
            boolean res;
            // 第一步
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                //MAX_RETIRES目前為4,表示嘗試4次安裝,如果還不成功,則認為安裝失敗
                if (++mRetries > MAX_RETRIES) {
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    // 調用handleStartCopy抽象方法
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            // 第二步
             // 調用handleReturnCode抽象方法,這個方法會在handleStartCopy執行完拷貝相關行為之后,根據handleStartCopy做進一步的處理,主要返回狀態碼
            handleReturnCode();
            return res;
        }

這里會涉及到一個HandlerParams和InstallParams的關系請參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充中的五、HandlerParams與InstallParams簡介

我將上面方法內部劃分為兩個部分

  • 第一部分,即try-catch塊內部,判斷是錯次數,是否大于四次,這里有兩種情況:
    • 如果超過4次,即mRetries>=4則,表示已經嘗試了4次(4次是極限),則發送一個what值為MCS_GIVE_UP的Message放棄本次安裝。
    • 如果沒超過4次, 則調用handleStartCopy()方法,如果在這個方法中出現異常。其中handleStartCopy()為真正的核心方法。如果在handleStartCopy()方法調用的時候產生了異常則發送一個what為MCS_RECONNECT的Message
  • 上面完成之后,調用handleReturnCode()

這個方法涉及了到兩個Message的what值的處理邏輯,這兩個what值分別是MCS_GIVE_UP和MCS_RECONNECT,按我們就來一起來看下:

MCS_RECONNECT代碼在PackageManagerService.java 1226行
MCS_GIVE_UP代碼在PackageManagerService.java 1262行
為了方便我直接把他們合在一起了。

        void doHandleMessage(Message msg) {
            switch (msg.what) {
                ...
                case MCS_RECONNECT: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_reconnect");
                    if (mPendingInstalls.size() > 0) {
                        if (mBound) {
                            disconnectService();
                        }
                        if (!connectToService()) {
                            Slog.e(TAG, "Failed to bind to media container service");
                            for (HandlerParams params : mPendingInstalls) {
                                // Indicate service bind error
                                params.serviceError();
                                Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                                        System.identityHashCode(params));
                            }
                            mPendingInstalls.clear();
                        }
                    }
                    break;
                }
                case MCS_GIVE_UP: {
                    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_giveup too many retries");
                    HandlerParams params = mPendingInstalls.remove(0);
                    Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",
                            System.identityHashCode(params));
                    break;
                }
                ...
            }
        }
  • MCS_RECONNECT:判斷安裝請求隊列mPendingInstalls是否還有元素,如果有元素先斷開綁定,則再次重新調用connectToService方法,我們知道connectToService()內部會再次執行綁定DefaultContainerService,而在綁定成功后會再次發送一個what值為MCS_BOUND的Message,從而又回到了startCopy里面。
  • MCS_GIVE_UP:直接刪除了安裝請求隊列mPendingInstalls里面下標為0的元素。

通過上文我們知道這個的HandlerParams的具體實現類是InstallParams,即handleStartCopy方法的具體實現是在InstallParams的handleStartCopy里面。

綜上所述:

startCopy()方法調用其子類的InstallParams的handleStartCopy()來完成拷貝工作的,考慮到安裝過程的不去誒的那個性,startCopy主要工作是進行錯誤處理,當捕獲到handleStartCopy跑出的異常時,startCopy將發送MCS_RECONNECT,在MCS_RECONNECT消息處理中,將會重新綁定
DefaultContainerService,如果綁定成功,那么安裝過程將會重新開始。startCopy也就將會再次調用,重試的次數記錄在mRetries中,當累計重試超過4次時,安裝將失敗,如果安裝失敗,startCopy將會調用handleReturnCode()方法來處理。

下面我們就來看下handleStartCopy的具體實現

(七) InstallParams的handleStartCopy方法

代碼在PackageManagerService.java 10307行


        /*
         * Invoke remote method to get package information and install
         * location values. Override install location based on default
         * policy if needed and then create install arguments based
         * on the install location.
         */
        public void handleStartCopy() throws RemoteException {
            int ret = PackageManager.INSTALL_SUCCEEDED;
            // 第一步
            // If we're already staged, we've firmly committed to an install location
            // 是安裝在手機內部存儲空間還是sdcard中,設置對應標志位
            // 新安裝的情況下stage為false
            if (origin.staged) {
                if (origin.file != null) {
                    installFlags |= PackageManager.INSTALL_INTERNAL;
                    installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                } else if (origin.cid != null) {
                    installFlags |= PackageManager.INSTALL_EXTERNAL;
                    installFlags &= ~PackageManager.INSTALL_INTERNAL;
                } else {
                    throw new IllegalStateException("Invalid stage location");
                }
            }

             // 是否安裝在SD卡上
            final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
             // 是否安裝在內部空間上
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;

            PackageInfoLite pkgLite = null;

             // 檢查APK的安裝位置是否正確
            if (onInt && onSd) {
                // 如果既要安裝到SD卡上也要安裝上內部空間,則由沖突安裝失敗
                // Check if both bits are set.
                Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else {
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                        packageAbiOverride);

                /*
                 * If we have too little free space, try to free cache
                 * before giving up.
                 */
                 // 釋放存儲空間
                if (!origin.staged && pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                    // TODO: focus freeing disk space on the target device
                    final StorageManager storage = StorageManager.from(mContext);
                    final long lowThreshold = storage.getStorageLowBytes(
                            Environment.getDataDirectory());

                    final long sizeBytes = mContainerService.calculateInstalledSize(
                            origin.resolvedPath, isForwardLocked(), packageAbiOverride);

                    if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
                        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                                installFlags, packageAbiOverride);
                   }

                    /*
                     * The cache free must have deleted the file we
                     * downloaded to install.
                     *
                     * TODO: fix the "freeCache" call to not delete
                     *       the file we care about.
                     */
                    if (pkgLite.recommendedInstallLocation
                            == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                        pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                    }
                }
            }
            
            // 第二步
            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 if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                    ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_APK;
                } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    ret = PackageManager.INSTALL_FAILED_INVALID_URI;
                } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                    ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
                } else {
                    // Override with defaults if needed.
                    loc = installLocationPolicy(pkgLite);
                    if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
                        ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
                    } else if (!onSd && !onInt) {
                        // Override install location with flags
                        if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
                            // Set the flag to install on external media.
                            installFlags |= PackageManager.INSTALL_EXTERNAL;
                            installFlags &= ~PackageManager.INSTALL_INTERNAL;
                        } else {
                            // Make sure the flag for installing on external
                            // media is unset
                            installFlags |= PackageManager.INSTALL_INTERNAL;
                            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
                        }
                    }
                }
            }
            // 第三步
           //createInstallArgs 用于創建一個安裝參數對象
            final InstallArgs args = createInstallArgs(this);
            mArgs = args;

             // 第四步
            if (ret == PackageManager.INSTALL_SUCCEEDED) {
                 /*
                 * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
                 * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
                 */
                int userIdentifier = getUser().getIdentifier();
                if (userIdentifier == UserHandle.USER_ALL
                        && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
                    userIdentifier = UserHandle.USER_OWNER;
                }

                /*
                 * Determine if we have any installed package verifiers. If we
                 * do, then we'll defer to them to verify the packages.
                 */
                final int requiredUid = mRequiredVerifierPackage == null ? -1
                        : getPackageUid(mRequiredVerifierPackage, userIdentifier);
                if (!origin.existing && requiredUid != -1
                        && isVerificationEnabled(userIdentifier, installFlags)) {
                    // 對當前包做驗證操作
                    final Intent verification = new Intent(
                            Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
                    verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
                    verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
                            PACKAGE_MIME_TYPE);
                    verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

                    final List<ResolveInfo> receivers = queryIntentReceivers(verification,
                            PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
                            0 /* TODO: Which userId? */);

                    if (DEBUG_VERIFY) {
                        Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
                                + verification.toString() + " with " + pkgLite.verifiers.length
                                + " optional verifiers");
                    }

                    final int verificationId = mPendingVerificationToken++;

                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);

                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
                            installerPackageName);

                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,
                            installFlags);

                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,
                            pkgLite.packageName);

                    verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
                            pkgLite.versionCode);

                    if (verificationParams != null) {
                        if (verificationParams.getVerificationURI() != null) {
                           verification.putExtra(PackageManager.EXTRA_VERIFICATION_URI,
                                 verificationParams.getVerificationURI());
                        }
                        if (verificationParams.getOriginatingURI() != null) {
                            verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
                                  verificationParams.getOriginatingURI());
                        }
                        if (verificationParams.getReferrer() != null) {
                            verification.putExtra(Intent.EXTRA_REFERRER,
                                  verificationParams.getReferrer());
                        }
                        if (verificationParams.getOriginatingUid() >= 0) {
                            verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
                                  verificationParams.getOriginatingUid());
                        }
                        if (verificationParams.getInstallerUid() >= 0) {
                            verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
                                  verificationParams.getInstallerUid());
                        }
                    }

                    final PackageVerificationState verificationState = new PackageVerificationState(
                            requiredUid, args);

                    mPendingVerification.append(verificationId, verificationState);

                    final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                            receivers, verificationState);

                    // Apps installed for "all" users use the device owner to verify the app
                    UserHandle verifierUser = getUser();
                    if (verifierUser == UserHandle.ALL) {
                        verifierUser = UserHandle.OWNER;
                    }

                    /*
                     * If any sufficient verifiers were listed in the package
                     * manifest, attempt to ask them.
                     */
                    if (sufficientVerifiers != null) {
                        final int N = sufficientVerifiers.size();
                        if (N == 0) {
                            Slog.i(TAG, "Additional verifiers required, but none installed.");
                            ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
                        } else {
                            for (int i = 0; i < N; i++) {
                                final ComponentName verifierComponent = sufficientVerifiers.get(i);

                                final Intent sufficientIntent = new Intent(verification);
                                sufficientIntent.setComponent(verifierComponent);
                                mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
                            }
                        }
                    }

                    final ComponentName requiredVerifierComponent = matchComponentForVerifier(
                            mRequiredVerifierPackage, receivers);
                    if (ret == PackageManager.INSTALL_SUCCEEDED
                            && mRequiredVerifierPackage != null) {
                        /*
                         * Send the intent to the required verification agent,
                         * but only start the verification timeout after the
                         * target BroadcastReceivers have run.
                         */
                        verification.setComponent(requiredVerifierComponent);
                        mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
                                android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                                new BroadcastReceiver() {
                                    @Override
                                    public void onReceive(Context context, Intent intent) {
                                        final Message msg = mHandler
                                                .obtainMessage(CHECK_PENDING_VERIFICATION);
                                        msg.arg1 = verificationId;
                                        mHandler.sendMessageDelayed(msg, getVerificationTimeout());
                                    }
                                }, null, 0, null, null);

                        /*
                         * We don't want the copy to proceed until verification
                         * succeeds, so null out this field.
                         */
                        mArgs = null;
                    }
                } else {
                     // 不需要做驗證
                    /*
                     * No package verification is enabled, so immediately start
                     * the remote call to initiate copy using temporary file.
                     */
                    // 調用InstallArgs的copyApk函數
                    ret = args.copyApk(mContainerService, true);
                }
            }

            mRet = ret;
        }
  • 第一步:判斷origin.staged的值,要判斷origin.staged的值,這里origin.staged為false,關于為什么origin.staged為false,請查看APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補充中的七、為什么新安裝的情況下 origin.staged等于false
  • 第二步:設置ret參數
  • 第三步:創建安裝參數對象args
  • 第四步:這里根絕是否需要驗證分為兩種情況
    • 情況1——需要驗證:需要構建一個Intent對象verifaction,并設置相應的參數,然后發送一個廣播進行包驗證。在廣播的onReceive中,則發送了一個what值為CHECK_PENDING_VERIFICATION的message,這個message的arg1的值為verificationId,
    • 情況2——不需要驗證:直接調用InstallArgs的copyApk方法進行包拷貝

整體流程圖下圖:


handleStartCopy流程.png

這方面里面涉及到的幾個比較難理解的地方,我就不一一解答了,我會下一篇依次解答,如下:

這里面會涉及兩個問題如下:

  • 1、what值為CHECK_PENDING_VERIFICATION的Message的處理內容
  • 2、InstallArgs的copyApk(IMediaContainerService, boolean)方法的具體實現

我在這里說下

1、what值為CHECK_PENDING_VERIFICATION的Message的處理內容

代碼在PackageManagerService.java 1506行

                case CHECK_PENDING_VERIFICATION: {
                    // 獲取verificationId
                    final int verificationId = msg.arg1;
                    // mPendingVerification 是一個SparseArray,鍵值對是verificationId和其對應的PackageVerificationState
                  // PackageVerificationState 可以理解為安裝包驗證的狀態
                   final PackageVerificationState state = mPendingVerification.get(verificationId);
                    // 如果包的驗證狀態不為空,并且驗證沒有超時
                    if ((state != null) && !state.timeoutExtended()) {
                        final InstallArgs args = state.getInstallArgs();
                        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);

                        Slog.i(TAG, "Verification timed out for " + originUri);
                        // 移除verificationId對應的PackageVerificationState對象
                        mPendingVerification.remove(verificationId);
                        // 初始化ret值為 驗證失敗導致安裝失敗
                        int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
                         // 如果默認的包驗證器允許繼續安裝
                        if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
                            Slog.i(TAG, "Continuing with installation of " + originUri);
                            // 設置驗證跟蹤類別,PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT表示:允許在沒有驗證者的情況下進行安裝
                            state.setVerifierResponse(Binder.getCallingUid(),
                                    PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
                            //發送廣播,對應的ACTION為ACTION_PACKAGE_VERIFIED
                            broadcastPackageVerified(verificationId, originUri,
                                    PackageManager.VERIFICATION_ALLOW,
                                    state.getInstallArgs().getUser());
                            try {
                                // 調用args的copyApk(IMediaContainerService,boolean)方法
                                ret = args.copyApk(mContainerService, true);
                            } catch (RemoteException e) {
                                Slog.e(TAG, "Could not contact the ContainerService");
                            }
                        } else {
                             // 如果默認的包驗證器,拒絕驗證,則發送廣播告訴之
                            broadcastPackageVerified(verificationId, originUri,
                                    PackageManager.VERIFICATION_REJECT,
                                    state.getInstallArgs().getUser());
                        }
                        // 調用processPendingInstall方法
                        processPendingInstall(args, ret);
                        // 發送一個what值為MCS_UNBIND的Message
                        mHandler.sendEmptyMessage(MCS_UNBIND);
                    }
                    break;
                }

我們發現無論是否需要驗證,最后都是調用InstallArgs的copyApk(IMediaContainerService,boolean)方法,而且這兩個入參都是固定的,第一個參數為mContainerService,第二個參數為true。所以最后的拷貝肯定是通過copyApk來執行的,通過上面的代碼我們知道這個InstallArgs其實是FileInstallArgs。所以我們只需要關注FileInstallArgs的copyApk(IMediaContainerService,boolean)方法即可

2、FileInstallArgs的copyApk(IMediaContainerService,boolean)方法

代碼在PackageManagerService.java 11050行

        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            // 上面我們已經說了,在新安裝的情況下origin.staged=false
            if (origin.staged) {
                if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                codeFile = origin.file;
                resourceFile = origin.file;
                return PackageManager.INSTALL_SUCCEEDED;
            }
           // 第一步
           // 獲取目錄
            try {
                // 創建目錄
                final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);
                codeFile = tempDir;
                resourceFile = tempDir;
            } catch (IOException e) {
                Slog.w(TAG, "Failed to create copy file: " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }

            final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
                @Override
                public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
                    if (!FileUtils.isValidExtFilename(name)) {
                        throw new IllegalArgumentException("Invalid filename: " + name);
                    }
                    try {
                        final File file = new File(codeFile, name);
                        final FileDescriptor fd = Os.open(file.getAbsolutePath(),
                                O_RDWR | O_CREAT, 0644);
                        Os.chmod(file.getAbsolutePath(), 0644);
                        return new ParcelFileDescriptor(fd);
                    } catch (ErrnoException e) {
                        throw new RemoteException("Failed to open: " + e.getMessage());
                    }
                }
            };

            int ret = PackageManager.INSTALL_SUCCEEDED;
             // 第二步
            // 真正的文件拷貝
            ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
            if (ret != PackageManager.INSTALL_SUCCEEDED) {
                Slog.e(TAG, "Failed to copy package");
                return ret;
            }
             // 第三步
            // 獲取庫的跟目錄
            final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
            NativeLibraryHelper.Handle handle = null;
            // 拷貝 Native代碼 即so文件
            try {
                handle = NativeLibraryHelper.Handle.create(codeFile);
                ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                        abiOverride);
            } catch (IOException e) {
                Slog.e(TAG, "Copying native libraries failed", e);
                ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
            } finally {
                IoUtils.closeQuietly(handle);
            }
            return ret;
        }

我將上面的內容大體上分為三個步驟

  • 第一步:創建目錄文件
  • 第二步:進行代碼拷貝
  • 第三步:進行Native代碼拷貝

這里重點說下這行代碼ret = imcs.copyPackage(origin.file.getAbsolutePath(), target)。我們知道imc其實是一個遠程的代理,實際的調用方式DefaultContainerService的成員變量mBinder。那我們就一起來看下DefaultContainerService的成員變量mBinder的copyPackage方法的執行

3、DefaultContainerService#mBinder#copyPackage方法

代碼在DefaultContainerService.java 128行

        /**
         * Copy package to the target location.
         *
         * @param packagePath absolute path to the package to be copied. Can be
         *            a single monolithic APK file or a cluster directory
         *            containing one or more APKs.
         * @return returns status code according to those in
         *         {@link PackageManager}
         */
        @Override
        public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
            // 第一步:
            if (packagePath == null || target == null) {
                return PackageManager.INSTALL_FAILED_INVALID_URI;
            }

             // 第二步:
            PackageLite pkg = null;
            try {
                final File packageFile = new File(packagePath);
                pkg = PackageParser.parsePackageLite(packageFile, 0);
                // 第三步
                return copyPackageInner(pkg, target);
            } catch (PackageParserException | IOException | RemoteException e) {
                Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
                return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            }
        }

太好了,居然有注釋,那我們就來看下注釋

將包復制到目標位置

  • 入參:packagePath:要復制包的絕對路徑,可以是一個APK文件,也可以是多個APK文件
  • 出參:安裝的狀態碼

我將這方法內部整體流程分為三步

  • 第一步,判斷入參packagePath和target是否為空,如果為空則直接返回安裝失敗,失敗原因不正確的地址路徑
  • 第二步,通過PackageParser的PackageParser方法解析出"輕量級"的安裝包內容。
  • 第三步,調用copyPackageInner(PackageLite, IParcelFileDescriptorFactory)方法

PS:在第二步和第三步中有個try-catch塊,如果其中出現異常,則直接返回安裝失敗,失敗原因是空間不足。

所以我們看出這個copyPackage方法內部,其實沒有真正的實現包拷貝,而是類似于"預加載"的作用,先獲取一個"輕量級"的安裝包。

那我們來看下copyPackageInner方法

4、DefaultContainerService#copyPackageInner方法

代碼在DefaultContainerService.java 367行

    private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
            throws IOException, RemoteException {
         // 第一部分
        copyFile(pkg.baseCodePath, target, "base.apk");
         // 第二部分
        if (!ArrayUtils.isEmpty(pkg.splitNames)) {
            for (int i = 0; i < pkg.splitNames.length; i++) {
                copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
            }
        }
        return PackageManager.INSTALL_SUCCEEDED;
    }

我將這個方法的內部執行分為兩個部分

  • 第一部分:調用copyFile,注意這里傳入兩個的參數是baseCodePath和"base.apk"
  • 第二部分:如果PackageLite有APK拆分,則遍歷所有的分APK,再依次調用copyFile

我們看到這里調用了copyFile(String, IParcelFileDescriptorFactory, String)方法來進行的代碼拷貝,那我們就來看下copyFile里面是怎么執行的

5、DefaultContainerService#copyFile(String, IParcelFileDescriptorFactory, String)方法

代碼在DefaultContainerService.java 379行

    private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
            throws IOException, RemoteException {
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
        InputStream in = null;
        OutputStream out = null;
        try {
            // 輸入流
            in = new FileInputStream(sourcePath);
             // 輸出流
            out = new ParcelFileDescriptor.AutoCloseOutputStream(
                    target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
            // 進行拷貝
            Streams.copy(in, out);
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
        }
    }

我們看到這個方法很簡單,就是構建輸入流,構建輸出流,然后調用Streams的方法進行拷貝。

這里順帶說下Streams.java這個類,這個類是libcore/io/Streams.java。copy方法如下:

    /**
     * Copies all of the bytes from {@code in} to {@code out}. Neither stream is closed.
     * Returns the total number of bytes transferred.
     */
    public static int copy(InputStream in, OutputStream out) throws IOException {
        int total = 0;
        byte[] buffer = new byte[8192];
        int c;
        while ((c = in.read(buffer)) != -1) {
            total += c;
            out.write(buffer, 0, c);
        }
        return total;
    }

通過這個方法完成了安裝包的拷貝功能

至此 PMS中的新安裝流程上(拷貝)已經全部講解完成

上一篇文章 APK安裝流程詳解11——普通應用安裝簡介
下一篇文章 APK安裝流程詳解13——PackageManagerService中的新安裝流程下(裝載)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容