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

APK安裝流程系列文章整體內(nèi)容如下:

本片文章的主要內(nèi)容如下:

  • 1、ApplicationPackageManager中相關(guān)方法的跟蹤
    • 1.1 、installPackageWithVerification(Uri,PackageInstallObserver, int, String,Uri, ManifestDigest,ContainerEncryptionParams)方法
    • 1.2、installCommon(Uri,PackageInstallObserver, int, String,int)方法解析
  • 2、PackageManagerService中相關(guān)方法的跟蹤
    • 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里面最后更新的代碼是調(diào)用到PackageManager#installPackageWithVerificationAndEncryption方法,那我們就從這個(gè)方法開(kāi)始進(jìn)行跟蹤分析

總體流程大致如下:


安裝流程.png

涉及到類的流程如下:


App安裝過(guò)程中涉及類.png

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

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

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

一、ApplicationPackageManager中相關(guān)方法的跟蹤

通過(guò)前面的研究我們知道PackageManager是個(gè)抽象類,具體的實(shí)現(xiàn)類是ApplicationPackageManager,那我們就來(lái)看下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);
    }

這個(gè)方法內(nèi)部很簡(jiǎn)單,就是調(diào)用了installCommon方法。那我們繼續(xù)跟蹤下

(二)、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) {
        }
    }

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

二、PackageManagerService中相關(guān)方法的跟蹤

(一) 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個(gè)參數(shù):

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

我們看到PackageManagerService的installPackage方法什么都沒(méi)做,直接調(diào)用了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權(quán)限
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);

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

         // 判斷當(dāng)前用戶是否被Restricted,回調(diào)onPackageInstalled方法,安裝失敗
        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
            try {
                if (observer != null) {
                    observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
                }
            } catch (RemoteException re) {
            }
            return;
        }
       
        // 第二步
        // 判斷是安裝來(lái)源是 ADB、shell和all_user
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {  
            // 如果發(fā)起端進(jìn)程是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 時(shí),當(dāng)權(quán)限屬于運(yùn)行時(shí)權(quán)限時(shí),需要彈出框,讓用戶授權(quán),對(duì)于system app,應(yīng)該取消彈框授權(quán),而是直接授權(quán)
        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);

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

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

就像這個(gè)方法的名字叫"installPackageAsUser"一樣,這段代碼主要是對(duì)用戶是否有權(quán)限安裝進(jìn)行檢查,以及安裝app是 僅僅給當(dāng)前用戶安裝,還是給所有用戶安裝。通過(guò)上面代碼可以看出,當(dāng)安裝進(jìn)程是shell或者root時(shí),否則大多數(shù)情況下,僅僅安裝給當(dāng)前用戶。

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

該函數(shù)主要做了以下操作:

  • 第一步:獲取權(quán)限,如果被拒絕,則退出執(zhí)行
  • 第二步:設(shè)置installFlags參數(shù),即判斷安裝來(lái)源
  • 第三步:發(fā)送了一個(gè)what值為INIT_COPY的message
    PS:這里注意這個(gè)msg的obj變量對(duì)應(yīng)的InstallParams對(duì)象,這個(gè)InstallParams對(duì)象后面會(huì)多次用到;在構(gòu)造InstallParams的時(shí)候注意他的兩個(gè)參數(shù)為null

該方法的主要流程如下:


installPackageAsUser的主要流程.png

PackageHandler來(lái)執(zhí)行的,PackageHandler繼續(xù)字Handler,那我們來(lái)具體看看執(zhí)行代碼:

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

因?yàn)镻ackageHandler繼承Handler,所以我們來(lái)看下PackageHandler的HandlerMessage方法
代碼在PackageManagerService.java 1131行

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

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

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

1、PackageHandler的doHandleMessage方法

因?yàn)槲覀冎繫essage的what值是INIT_COPY,所以為了節(jié)省空間,我們就不粘貼其他的case了。

代碼在PackageManagerService.java 1139行


        void doHandleMessage(Message msg) {
            switch (msg.what) {
                case INIT_COPY: {
                    // 第一步
                   // 取出InstallParams
                    HandlerParams params = (HandlerParams) msg.obj;
                     //  idx為當(dāng)前需要安裝的APK個(gè)數(shù),mPendingInstalls里面保存所有需要安裝的APK解析出來(lái)的HandlerParams參數(shù)
                    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.
                    // 第二步 分兩種情況
                    // 判斷綁定的連接是否已經(jīng)存在,如果已經(jīng)綁定了,則mBound為true。如果是第一次調(diào)用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后,將新的安裝請(qǐng)求放入到mPendingIntalls中,等待處理
                            mPendingInstalls.add(idx, params);
                        }
                    } else {
                         // 如果之前已經(jīng)綁定過(guò)服務(wù),同樣將新的請(qǐng)求到mPendingIntalls中,等待處理
                        mPendingInstalls.add(idx, params);
                        // Already bound to the service. Just make
                        // sure we trigger off processing the first request.
                        if (idx == 0) {
                            // 如果是第一個(gè)安裝請(qǐng)求,則直接發(fā)送事件MCS_BOUND觸發(fā)處理流程
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    break;
                }
             ...
          }

我將這個(gè)方法分為三個(gè)部分:

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

這個(gè)方法整體流程如下圖:

INIT_COPY的流程.png

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

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

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

  • 先調(diào)用mPendingInstalls.add(idx, params);
  • 發(fā)送一個(gè)what值是MCS_BOUND 的Message

我們假設(shè)第一次,則先調(diào)用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到了一個(gè)service,這個(gè)service的ComponentName是"DEFAULT_CONTAINER_COMPONENT"這個(gè)常量,那我們就來(lái)看下這個(gè)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之后,設(shè)定進(jìn)程的優(yōu)先級(jí)為T(mén)HREAD_PRIORITY_DEFAULT。然后等bindServiceAsUser這個(gè)方法執(zhí)行完則又把線程的優(yōu)先級(jí)設(shè)為T(mén)HREAD_PRIORITY_BACKGROUND。

這里面涉及到一個(gè)變量mDefContainerConn

1、mDefContainerConn變量

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

    final private DefaultContainerConnection mDefContainerConn =
            new DefaultContainerConnection();

我們知道了mDefContainerConn的類型是DefaultContainerConnection,那我們來(lái)看下DefaultContainerConnection這個(gè)類。

代碼在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");
        }
    }

通過(guò)上面代碼我們知道DefaultContainerConnection實(shí)現(xiàn)了ServiceConnection。所以在連接成功的時(shí)候會(huì)調(diào)用onServiceConnected方法,我們看到當(dāng)連接成功的時(shí)候會(huì)向mHandler發(fā)送一個(gè)Message去,這個(gè)Message的what值為MCS_BOUND。

2、mDefContainerConn變量與DefaultContainerService

上文我們提及到mContext.bindServiceAsUser(service, mDefContainerConn,Context.BIND_AUTO_CREATE, UserHandle.OWNER)方法,其實(shí)就是"綁定"DefaultContainerService。我們知道bind一個(gè)Service,其中負(fù)責(zé)通信的ServiceConnection,而本方法中負(fù)責(zé)通信的就是mDefContainerConn。所以一旦綁定成功會(huì)執(zhí)行mDefContainerConn的onServiceConnected方法。而現(xiàn)實(shí)是當(dāng)綁定成功后在onServiceConnected中將一個(gè)IBinder轉(zhuǎn)換成了一個(gè)IMediaContainerService。這個(gè)就是onServiceConnected回調(diào)函數(shù)中根據(jù)參數(shù)傳進(jìn)來(lái)的IMediaContainerService.Stub的對(duì)象引用創(chuàng)建的一個(gè)遠(yuǎn)程代理對(duì)象,后面PacakgeManagerServic通過(guò)該代理對(duì)象訪問(wèn)DefaultContainerService服務(wù)

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

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

(五) 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服務(wù)失敗,則不能安裝程序
                    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");
                        }
                              // 如果安裝請(qǐng)求隊(duì)列不為空
                    } else if (mPendingInstalls.size() > 0) {
                      // 第二步的情況B
                        HandlerParams params = mPendingInstalls.get(0);
                        if (params != null) {
                            // 調(diào)動(dòng)startCopy函數(shù)處理安裝請(qǐng)求
                            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) {
                                    // 刪除請(qǐng)求安裝隊(duì)列的頭元素,即下標(biāo)為0的元素
                                    mPendingInstalls.remove(0);
                                }
                               // 第二步的情況B下的分支甲              
                                if (mPendingInstalls.size() == 0) {
                                    // 如果安裝請(qǐng)求都處理完了
                                    if (mBound) {
                                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                                "Posting delayed MCS_UNBIND");
                                        // 通過(guò)發(fā)送MCS_UNBIND消息處理斷開(kāi)綁定請(qǐng)求
                                        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.
                                    // 如果還有未處理的請(qǐng)求,則繼續(xù)發(fā)送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;
                }
       }

我將上面整體流程分為兩個(gè)步驟

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

上面這個(gè)方法涉及到一個(gè)核心方法即startCopy()來(lái)處理安裝請(qǐng)求,通過(guò)方法名叫"startCopy"我們猜測(cè)是"開(kāi)始拷貝"的意思,那我們就一起來(lái)看下

(六) HandlerParams的startCopy方法

代碼在PackageManagerService.java 10253行

        final boolean startCopy() {
            boolean res;
            // 第一步
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
                //MAX_RETIRES目前為4,表示嘗試4次安裝,如果還不成功,則認(rèn)為安裝失敗
                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 {
                    // 調(diào)用handleStartCopy抽象方法
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            // 第二步
             // 調(diào)用handleReturnCode抽象方法,這個(gè)方法會(huì)在handleStartCopy執(zhí)行完拷貝相關(guān)行為之后,根據(jù)handleStartCopy做進(jìn)一步的處理,主要返回狀態(tài)碼
            handleReturnCode();
            return res;
        }

這里會(huì)涉及到一個(gè)HandlerParams和InstallParams的關(guān)系請(qǐng)參考APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充中的五、HandlerParams與InstallParams簡(jiǎn)介

我將上面方法內(nèi)部劃分為兩個(gè)部分

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

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

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:判斷安裝請(qǐng)求隊(duì)列mPendingInstalls是否還有元素,如果有元素先斷開(kāi)綁定,則再次重新調(diào)用connectToService方法,我們知道connectToService()內(nèi)部會(huì)再次執(zhí)行綁定DefaultContainerService,而在綁定成功后會(huì)再次發(fā)送一個(gè)what值為MCS_BOUND的Message,從而又回到了startCopy里面。
  • MCS_GIVE_UP:直接刪除了安裝請(qǐng)求隊(duì)列mPendingInstalls里面下標(biāo)為0的元素。

通過(guò)上文我們知道這個(gè)的HandlerParams的具體實(shí)現(xiàn)類是InstallParams,即handleStartCopy方法的具體實(shí)現(xiàn)是在InstallParams的handleStartCopy里面。

綜上所述:

startCopy()方法調(diào)用其子類的InstallParams的handleStartCopy()來(lái)完成拷貝工作的,考慮到安裝過(guò)程的不去誒的那個(gè)性,startCopy主要工作是進(jìn)行錯(cuò)誤處理,當(dāng)捕獲到handleStartCopy跑出的異常時(shí),startCopy將發(fā)送MCS_RECONNECT,在MCS_RECONNECT消息處理中,將會(huì)重新綁定
DefaultContainerService,如果綁定成功,那么安裝過(guò)程將會(huì)重新開(kāi)始。startCopy也就將會(huì)再次調(diào)用,重試的次數(shù)記錄在mRetries中,當(dāng)累計(jì)重試超過(guò)4次時(shí),安裝將失敗,如果安裝失敗,startCopy將會(huì)調(diào)用handleReturnCode()方法來(lái)處理。

下面我們就來(lái)看下handleStartCopy的具體實(shí)現(xiàn)

(七) 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
            // 是安裝在手機(jī)內(nèi)部存儲(chǔ)空間還是sdcard中,設(shè)置對(duì)應(yīng)標(biāo)志位
            // 新安裝的情況下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;
             // 是否安裝在內(nèi)部空間上
            final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;

            PackageInfoLite pkgLite = null;

             // 檢查APK的安裝位置是否正確
            if (onInt && onSd) {
                // 如果既要安裝到SD卡上也要安裝上內(nèi)部空間,則由沖突安裝失敗
                // 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.
                 */
                 // 釋放存儲(chǔ)空間
                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 用于創(chuàng)建一個(gè)安裝參數(shù)對(duì)象
            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)) {
                    // 對(duì)當(dāng)前包做驗(yàn)證操作
                    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 {
                     // 不需要做驗(yàn)證
                    /*
                     * No package verification is enabled, so immediately start
                     * the remote call to initiate copy using temporary file.
                     */
                    // 調(diào)用InstallArgs的copyApk函數(shù)
                    ret = args.copyApk(mContainerService, true);
                }
            }

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

整體流程圖下圖:


handleStartCopy流程.png

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

這里面會(huì)涉及兩個(gè)問(wèn)題如下:

  • 1、what值為CHECK_PENDING_VERIFICATION的Message的處理內(nèi)容
  • 2、InstallArgs的copyApk(IMediaContainerService, boolean)方法的具體實(shí)現(xiàn)

我在這里說(shuō)下

1、what值為CHECK_PENDING_VERIFICATION的Message的處理內(nèi)容

代碼在PackageManagerService.java 1506行

                case CHECK_PENDING_VERIFICATION: {
                    // 獲取verificationId
                    final int verificationId = msg.arg1;
                    // mPendingVerification 是一個(gè)SparseArray,鍵值對(duì)是verificationId和其對(duì)應(yīng)的PackageVerificationState
                  // PackageVerificationState 可以理解為安裝包驗(yàn)證的狀態(tài)
                   final PackageVerificationState state = mPendingVerification.get(verificationId);
                    // 如果包的驗(yàn)證狀態(tài)不為空,并且驗(yàn)證沒(méi)有超時(shí)
                    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對(duì)應(yīng)的PackageVerificationState對(duì)象
                        mPendingVerification.remove(verificationId);
                        // 初始化ret值為 驗(yàn)證失敗導(dǎo)致安裝失敗
                        int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
                         // 如果默認(rèn)的包驗(yàn)證器允許繼續(xù)安裝
                        if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
                            Slog.i(TAG, "Continuing with installation of " + originUri);
                            // 設(shè)置驗(yàn)證跟蹤類別,PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT表示:允許在沒(méi)有驗(yàn)證者的情況下進(jìn)行安裝
                            state.setVerifierResponse(Binder.getCallingUid(),
                                    PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
                            //發(fā)送廣播,對(duì)應(yīng)的ACTION為ACTION_PACKAGE_VERIFIED
                            broadcastPackageVerified(verificationId, originUri,
                                    PackageManager.VERIFICATION_ALLOW,
                                    state.getInstallArgs().getUser());
                            try {
                                // 調(diào)用args的copyApk(IMediaContainerService,boolean)方法
                                ret = args.copyApk(mContainerService, true);
                            } catch (RemoteException e) {
                                Slog.e(TAG, "Could not contact the ContainerService");
                            }
                        } else {
                             // 如果默認(rèn)的包驗(yàn)證器,拒絕驗(yàn)證,則發(fā)送廣播告訴之
                            broadcastPackageVerified(verificationId, originUri,
                                    PackageManager.VERIFICATION_REJECT,
                                    state.getInstallArgs().getUser());
                        }
                        // 調(diào)用processPendingInstall方法
                        processPendingInstall(args, ret);
                        // 發(fā)送一個(gè)what值為MCS_UNBIND的Message
                        mHandler.sendEmptyMessage(MCS_UNBIND);
                    }
                    break;
                }

我們發(fā)現(xiàn)無(wú)論是否需要驗(yàn)證,最后都是調(diào)用InstallArgs的copyApk(IMediaContainerService,boolean)方法,而且這兩個(gè)入?yún)⒍际枪潭ǖ模谝粋€(gè)參數(shù)為mContainerService,第二個(gè)參數(shù)為true。所以最后的拷貝肯定是通過(guò)copyApk來(lái)執(zhí)行的,通過(guò)上面的代碼我們知道這個(gè)InstallArgs其實(shí)是FileInstallArgs。所以我們只需要關(guān)注FileInstallArgs的copyApk(IMediaContainerService,boolean)方法即可

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

代碼在PackageManagerService.java 11050行

        int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
            // 上面我們已經(jīng)說(shuō)了,在新安裝的情況下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 {
                // 創(chuàng)建目錄
                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;
            }
             // 第三步
            // 獲取庫(kù)的跟目錄
            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;
        }

我將上面的內(nèi)容大體上分為三個(gè)步驟

  • 第一步:創(chuàng)建目錄文件
  • 第二步:進(jìn)行代碼拷貝
  • 第三步:進(jìn)行Native代碼拷貝

這里重點(diǎn)說(shuō)下這行代碼ret = imcs.copyPackage(origin.file.getAbsolutePath(), target)。我們知道imc其實(shí)是一個(gè)遠(yuǎn)程的代理,實(shí)際的調(diào)用方式DefaultContainerService的成員變量mBinder。那我們就一起來(lái)看下DefaultContainerService的成員變量mBinder的copyPackage方法的執(zhí)行

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

太好了,居然有注釋,那我們就來(lái)看下注釋

將包復(fù)制到目標(biāo)位置

  • 入?yún)ⅲ簆ackagePath:要復(fù)制包的絕對(duì)路徑,可以是一個(gè)APK文件,也可以是多個(gè)APK文件
  • 出參:安裝的狀態(tài)碼

我將這方法內(nèi)部整體流程分為三步

  • 第一步,判斷入?yún)ackagePath和target是否為空,如果為空則直接返回安裝失敗,失敗原因不正確的地址路徑
  • 第二步,通過(guò)PackageParser的PackageParser方法解析出"輕量級(jí)"的安裝包內(nèi)容。
  • 第三步,調(diào)用copyPackageInner(PackageLite, IParcelFileDescriptorFactory)方法

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

所以我們看出這個(gè)copyPackage方法內(nèi)部,其實(shí)沒(méi)有真正的實(shí)現(xiàn)包拷貝,而是類似于"預(yù)加載"的作用,先獲取一個(gè)"輕量級(jí)"的安裝包。

那我們來(lái)看下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;
    }

我將這個(gè)方法的內(nèi)部執(zhí)行分為兩個(gè)部分

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

我們看到這里調(diào)用了copyFile(String, IParcelFileDescriptorFactory, String)方法來(lái)進(jìn)行的代碼拷貝,那我們就來(lái)看下copyFile里面是怎么執(zhí)行的

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));
            // 進(jìn)行拷貝
            Streams.copy(in, out);
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
        }
    }

我們看到這個(gè)方法很簡(jiǎn)單,就是構(gòu)建輸入流,構(gòu)建輸出流,然后調(diào)用Streams的方法進(jìn)行拷貝。

這里順帶說(shuō)下Streams.java這個(gè)類,這個(gè)類是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;
    }

通過(guò)這個(gè)方法完成了安裝包的拷貝功能

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,071評(píng)論 3 414
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 175,409評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,569評(píng)論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,360評(píng)論 6 404
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 54,895評(píng)論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評(píng)論 3 440
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,123評(píng)論 0 286
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,643評(píng)論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,559評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,742評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評(píng)論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 43,981評(píng)論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,363評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,622評(píng)論 1 280
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,354評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,707評(píng)論 2 370

推薦閱讀更多精彩內(nèi)容