APK安裝流程詳解13——PMS中的新安裝流程下(裝載)

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

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

  • 1拷呆、裝載代碼的入口
  • 2、PackageManagerService#processPendingInstall(InstallArgs,int)方法
  • 3、PackageManagerService#installPackageLI(InstallArgs,PackageInstalledInfo)方法解析
  • 4差凹、PackageManagerService#installNewPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法解析
  • 5性穿、PackageManagerService#scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) 方法解析
  • 6慎菲、PackageManagerService#updateSettingsLIupdateSettingsLI(PackageParser.Package, String,String, int[], boolean[], PackageInstalledInfo,UserHandle)方法解析
  • 7癣蟋、 PackageHandler的處理Message的what值為POST_INSTALL的情況解析
  • 8辫狼、總結(jié)

一桂肌、裝載代碼的入口

上篇文章說到進(jìn)行完新安裝流程中的"拷貝代碼流程后"数焊,分兩種情況:

  • 1、如果前面是走"驗(yàn)證流程"崎场,則會(huì)調(diào)用processPendingInstall(args, ret)方法佩耳,如下:

代碼在PackageManagerService.java 1537行

                        if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
                            Slog.i(TAG, "Continuing with installation of " + originUri);
                            state.setVerifierResponse(Binder.getCallingUid(),
                                    PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
                            broadcastPackageVerified(verificationId, originUri,
                                    PackageManager.VERIFICATION_ALLOW,
                                    state.getInstallArgs().getUser());
                            try {
                                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(args, ret);
                        mHandler.sendEmptyMessage(MCS_UNBIND);
  • 2、如果不走"驗(yàn)證流程"谭跨,則會(huì)調(diào)用handleReturnCode()方法干厚,如下:

代碼在PackageManagerService.jav 10253行

        final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                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();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            handleReturnCode();
            return res;
        }

而在handleReturnCode()方法里面也是調(diào)用processPendingInstall(args, ret)方法,如下:

代碼在PackageManagerService.java 10253行

        @Override
        void handleReturnCode() {
            // If mArgs is null, then MCS couldn't be reached. When it
            // reconnects, it will try again to install. At that point, this
            // will succeed.
            if (mArgs != null) {
                processPendingInstall(mArgs, mRet);
            }
        }

我們看到handleReturnCode()來不也是調(diào)用processPendingInstall(InstallArgs,int)方法的螃宙,所以我們說

"裝載代碼"的入口是processPendingInstall(InstallArgs,int)方法蛮瞄。

二、PackageManagerService#processPendingInstall(InstallArgs,int)方法

代碼在PackageManagerService.java 10156行

    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        // Queue up an async operation since the package installation may take a little while.
        // 向mHandler中發(fā)送一個(gè)Runnable對(duì)象谆扎,這里是異步操作挂捅,因?yàn)榘惭b一個(gè)程序包可能需要一些時(shí)間
        mHandler.post(new Runnable() {
            public void run() {
               // 第一部分
               // 清除任務(wù)
                mHandler.removeCallbacks(this);
                 // Result object to be returned
                // 創(chuàng)建一個(gè)PackageInstalledInfo對(duì)象
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.returnCode = currentStatus;
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = new PackageRemovedInfo();
                // 第二部分
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    // 預(yù)安裝階段,主要是檢查安裝包的狀態(tài)堂湖,確保安裝環(huán)境正常闲先,如果安裝環(huán)境有問題會(huì)清理拷貝文件
                    args.doPreInstall(res.returnCode);
                    synchronized (mInstallLock) {
                       // 安裝階段,調(diào)用installPackageLI進(jìn)行安裝
                        installPackageLI(args, res);
                    }
                    // 安裝收尾
                    args.doPostInstall(res.returnCode, res.uid);
                }
                // A restore should be performed at this point if (a) the install
                // succeeded, (b) the operation is not an update, and (c) the new
                // package has not opted out of backup participation.
                final boolean update = res.removedInfo.removedPackage != null;
                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
                boolean doRestore = !update
                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);

                // Set up the post-install work request bookkeeping.  This will be used
                // and cleaned up by the post-install event handling regardless of whether
                // there's a restore pass performed.  Token values are >= 1.
                // 第三部分
                // 計(jì)算一個(gè)ID號(hào)
                int token;
                if (mNextInstallToken < 0) mNextInstallToken = 1;
                token = mNextInstallToken++;

                PostInstallData data = new PostInstallData(args, res);
                // 保存到mRunningInstalls結(jié)構(gòu)中无蜂,以token為key伺糠,而mRunningInstalls是SparseArray結(jié)構(gòu)的
                mRunningInstalls.put(token, data);
                if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);

                 // 第四部分
                 // 安裝成功,且需要備份的情況
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                    // Pass responsibility to the Backup Manager.  It will perform a
                    // restore if appropriate, then pass responsibility back to the
                    // Package Manager to run the post-install observer callbacks
                    // and broadcasts.
                    IBackupManager bm = IBackupManager.Stub.asInterface(
                            ServiceManager.getService(Context.BACKUP_SERVICE));
                    if (bm != null) {
                        if (DEBUG_INSTALL) Log.v(TAG, "token " + token
                                + " to BM for possible restore");
                        try {
                            if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
                                bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                            } else {
                                doRestore = false;
                            }
                        } catch (RemoteException e) {
                            // can't happen; the backup manager is local
                        } catch (Exception e) {
                            Slog.e(TAG, "Exception trying to enqueue restore", e);
                            doRestore = false;
                        }
                    } else {
                        Slog.e(TAG, "Backup Manager not found!");
                        doRestore = false;
                    }
                }
              // 第五部分
                if (!doRestore) {
                    // No restore possible, or the Backup Manager was mysteriously not
                    // available -- just fire the post-install work request directly.
                    if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                    mHandler.sendMessage(msg);
                }
            }
        });
    }

processPendingInstall()方法內(nèi)容不多酱讶,主要是post了一個(gè)消息退盯,這樣安裝過程將以異步的方式繼續(xù)執(zhí)行。

我將這個(gè)方法大致分為4個(gè)部分

  • 第一部分:初始化階段
  • 第二部分:安裝階段
    這個(gè)階段有可以細(xì)分為3個(gè)階段如下:
    • 預(yù)安裝階段:檢查當(dāng)前安裝包的狀態(tài)以及確保SDCARD的掛載,并返回狀態(tài)信息渊迁。在安裝前確保安裝環(huán)境的可靠
    • 安裝階段:對(duì)mInstallLock加鎖慰照,表明同時(shí)只能由一個(gè)安裝包進(jìn)行安裝,然后調(diào)用installPackageLI方法完成具體的安裝操作琉朽。
    • 安裝收尾階段:檢查狀態(tài)毒租,如果安裝失敗,刪除相關(guān)目錄文件箱叁。
  • 第三部分:加入緩存
    用InstallArgs和PackageInstalledInfo構(gòu)造一個(gè)PostInstallData對(duì)象墅垮,讓后把這個(gè)PostInstallData對(duì)方存放在mRunningInstalls里面,mRunningInstalls是一個(gè)SparseArray結(jié)構(gòu)耕漱,key是token算色,這樣后續(xù)查詢相關(guān)信息的東西可以直接訪問mRunningInstalls就可以了。
  • 第四部分:備份部分
    如果需要備份螟够,則調(diào)用BackupManagerService來完成備份灾梦,這里注意備份完畢后,設(shè)置doRestore = false
  • 第五部分:安裝階段結(jié)束
    無論該APK是否已經(jīng)安裝成功(失敗)妓笙,都會(huì)向mHandler發(fā)送一個(gè)what值為POST_INSTALL的Message消息若河。該Message消息的arg1為token,這樣它可以從mRunningInstalls中取得PostInstallData對(duì)象

這這方法里面涉及到四個(gè)重要的函數(shù)寞宫,即

  • args.doPreInstall(res.returnCode)
  • args.doPostInstall(res.returnCode, res.uid)
  • installPackageLI(args, res)
  • PackageHandler的處理Message的what值為POST_INSTALL的情況

其中installPackageLI(args, res)方法至關(guān)重要萧福,我們先在這里簡單的說下:installPackageLI方法進(jìn)行APK的裝載,該函數(shù)內(nèi)部將調(diào)用InstallArgs的doRename對(duì)臨時(shí)文件進(jìn)行改名辈赋。另外鲫忍,還需要掃描此APK文件。這樣實(shí)現(xiàn)了APK的信息映射到PackageManagerService內(nèi)部了炭庙。

我們知道args其實(shí)是FileInstallArgs饲窿,所以上面的args.doPreInstall(res.returnCode)方法和args.doPostInstall(res.returnCode, res.uid)方法其實(shí)對(duì)應(yīng)的都是FileInstallArgs的方法。

(一)焕蹄、FileInstallArgs#doPreInstall(int)方法

代碼在PackageManagerService.java 11108行

        int doPreInstall(int status) {
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                cleanUp();
            }
            return status;
        }

我們看到這個(gè)方法里面判斷是否已經(jīng)成功安裝完成。如果沒有成功安裝完成則調(diào)用cleanUp()方法阀溶,但是我們知道安裝過程中的預(yù)安裝階段status一定是等于PackageManager.INSTALL_SUCCEEDED腻脏,因?yàn)橹挥械扔诓拍苓M(jìn)入到這個(gè)方法。
畢竟是如下的代碼

                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    args.doPreInstall(res.returnCode);
                }

(二)银锻、FileInstallArgs#doPostInstall(int,int)方法

代碼在PackageManagerService.java 11161行

        int doPostInstall(int status, int uid) {
            if (status != PackageManager.INSTALL_SUCCEEDED) {
                cleanUp();
            }
            return status;
        }

在調(diào)用這個(gè)的方法時(shí)候永品,當(dāng)然有一種可能就是安裝成功了,如果安裝成了击纬,則這個(gè)方法什么也不做鼎姐;還有一種可能就是安裝失敗了,安裝失敗了即status不等于PackageManager.INSTALL_SUCCEEDED,所以會(huì)調(diào)用 cleanUp()方法炕桨。

假設(shè)安裝失敗了饭尝,我們來看下cleanUp()方法里面做了什么操作,不過看方法名字献宫,我們猜測(cè)是"清理"操作钥平。

FileInstallArgs#cleanUp()方法

代碼在PackageManagerService.java 11178行

        private boolean cleanUp() {
            //判斷代碼文件是否存在,如果不存在姊途,直接返回涉瘾,不需要清理
            if (codeFile == null || !codeFile.exists()) {
                return false;
            }
            // 如果代碼文件是個(gè)文件夾,則調(diào)用mInstaller.rmPackageDir進(jìn)行清理
            if (codeFile.isDirectory()) {
                mInstaller.rmPackageDir(codeFile.getAbsolutePath());
            } else {
                // 如果代碼文件是文件捷兰,則直接刪除文件夾
                codeFile.delete();
            }
            // 如果存在資源文件立叛,則刪除資源文件
            if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
                resourceFile.delete();
            }
            // 返回清除成功
            return true;
        }

可見cleanUp方法就是清理代碼文件和資源文件。
這里有個(gè)方法就是代碼文件是個(gè)文件夾的時(shí)候贡茅,調(diào)用mInstaller.rmPackageDir(codeFile.getAbsolutePath())進(jìn)行刪除工作秘蛇,在前面文章APK安裝流程詳解5——Installer、InstallerConnection和Installd守護(hù)進(jìn)程我們知道m(xù)Installerq其實(shí)只是一個(gè)代理類友扰,具體制定的是Native的intalld彤叉。那我們來找下Installer的rmPackageDir方法對(duì)應(yīng)的intalld什么操作?

代碼在installd.cpp 218行

struct cmdinfo cmds[] = {
    { "ping",                 0, do_ping },
    { "install",              5, do_install },
    { "dexopt",               10, do_dexopt },
    { "markbootcomplete",     1, do_mark_boot_complete },
    { "movedex",              3, do_move_dex },
    { "rmdex",                2, do_rm_dex },
    { "remove",               3, do_remove },
    { "rename",               2, do_rename },
    { "fixuid",               4, do_fixuid },
    { "freecache",            2, do_free_cache },
    { "rmcache",              3, do_rm_cache },
    { "rmcodecache",          3, do_rm_code_cache },
    { "getsize",              8, do_get_size },
    { "rmuserdata",           3, do_rm_user_data },
    { "cpcompleteapp",        6, do_cp_complete_app },
    { "movefiles",            0, do_movefiles },
    { "linklib",              4, do_linklib },
    { "mkuserdata",           5, do_mk_user_data },
    { "mkuserconfig",         1, do_mk_user_config },
    { "rmuser",               2, do_rm_user },
    { "idmap",                3, do_idmap },
    { "restorecondata",       4, do_restorecon_data },
    { "createoatdir",         2, do_create_oat_dir },
    { "rmpackagedir",         1, do_rm_package_dir },
    { "linkfile",             3, do_link_file }
};

大家注意下倒數(shù)第二行,rmpackagedir對(duì)應(yīng)著do_rm_package_dir

代碼在installd.cpp 176 行

static int do_rm_package_dir(char **arg, char reply[REPLY_MAX] __unused)
{
    /* oat_dir */
    return rm_package_dir(arg[0]);
}

那我們看下rm_package_dir函數(shù)

代碼在commands.cpp 1825 行

int rm_package_dir(const char* apk_path)
{
    if (validate_apk_path(apk_path)) {
        ALOGE("invalid apk path '%s' (bad prefix)\n", apk_path);
        return -1;
    }
    return delete_dir_contents(apk_path, 1 /* also_delete_dir */ , NULL /* exclusion_predicate */);
}

首先通過validate_apk_path來檢驗(yàn)下路徑,然后調(diào)用delete_dir_contents函數(shù)來刪除作儿,這個(gè)函數(shù)在utils.cpp 291行歹鱼。里面的代碼很簡單,我就不講解了

下面我們來看下installPackageLI方法的具體實(shí)現(xiàn)

三竞慢、PackageManagerService#installPackageLI(InstallArgs,PackageInstalledInfo)方法解析

代碼在PackageManagerService.java 12224行

    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
         // ******* 第一步 *******
         //獲取 installFlags屬性,這個(gè)屬性表明APP安裝到哪里
        final int installFlags = args.installFlags;
         // 安裝包應(yīng)用程序的 包名
        final String installerPackageName = args.installerPackageName;
        // volume的Uuid,后續(xù)講解Android存儲(chǔ)系列的詳細(xì)講解
        final String volumeUuid = args.volumeUuid;
        //根據(jù)安裝包代碼的路徑生成一個(gè)文件
        final File tmpPackageFile = new File(args.getCodePath());
         // 是否需要鎖定
        final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);
         // 是否安裝到外部存儲(chǔ)
        final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
                || (args.volumeUuid != null));
        // 新安裝還是更新安裝的標(biāo)志位
        boolean replace = false;
        int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
        // 判斷是是否是移動(dòng)一個(gè)APP斑举,由于我們是講解的新安裝,所以args.move=null
        if (args.move != null) {
            // moving a complete application; perfom an initial scan on the new install location
            scanFlags |= SCAN_INITIAL;
        }
        // Result object to be returned
        res.returnCode = PackageManager.INSTALL_SUCCEEDED;

        if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
        // Retrieve PackageSettings and parse package
        // 獲取解析包配置的標(biāo)志位
        final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
        PackageParser pp = new PackageParser();
        // 設(shè)置解析包的獨(dú)立進(jìn)程屬性
        pp.setSeparateProcesses(mSeparateProcesses);
         // 設(shè)置解析包的屏幕屬性
        pp.setDisplayMetrics(mMetrics);

        final PackageParser.Package pkg;
        // ******* 第二步 *******
        try {
            // 解析APK病涨,主要是解析AndroidManifest.xml文件富玷,將結(jié)果記錄在PackageParser.Package中
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        }

        // ******* 第三步 *******
        // Mark that we have an install time CPU ABI override.
        pkg.cpuAbiOverride = args.abiOverride;

        String pkgName = res.name = pkg.packageName;
        // 如果這個(gè)待安裝的APP不是測(cè)試包,但是如果環(huán)境為僅允許測(cè)試包則返回
        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {
            if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
                res.setError(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
                return;
            }
        }

        try {
            //收集APK的簽名信息
            // collectCertificates做簽名驗(yàn)證既穆,collectManifestDigest主要是做包的項(xiàng)目清單摘要的收集赎懦,主要適合用來比較兩個(gè)包的是否一樣
            pp.collectCertificates(pkg, parseFlags);
            pp.collectManifestDigest(pkg);
        } catch (PackageParserException e) {
            res.setError("Failed collect during installPackageLI", e);
            return;
        }
        /** 如果安裝程序此前傳入了一個(gè)項(xiàng)目清單文件(manifest),那么將解析到項(xiàng)目清單文件與傳入的進(jìn)行對(duì)比幻工。
          * 安裝器的確傳入了一個(gè)清單励两,PackageInstallerActivity中也解析出APK,那么記錄了這個(gè)清單囊颅,然后進(jìn)行對(duì)比当悔,判斷是否是同一個(gè)APK
          **/
        /* If the installer passed in a manifest digest, compare it now. */
        if (args.manifestDigest != null) {
            if (DEBUG_INSTALL) {
                final String parsedManifest = pkg.manifestDigest == null ? "null"
                        : pkg.manifestDigest.toString();
                Slog.d(TAG, "Comparing manifests: " + args.manifestDigest.toString() + " vs. "
                        + parsedManifest);
            }

            if (!args.manifestDigest.equals(pkg.manifestDigest)) {
                res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");
                return;
            }
        } else if (DEBUG_INSTALL) {
            final String parsedManifest = pkg.manifestDigest == null
                    ? "null" : pkg.manifestDigest.toString();
            Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);
        }

        // Get rid of all references to package scan path via parser.
        pp = null;
        String oldCodePath = null;
        boolean systemApp = false;
        synchronized (mPackages) {
            // Check if installing already existing package
            if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 // 如果安裝的升級(jí)應(yīng)用傅瞻,繼續(xù)使用以前的老包名
                String oldName = mSettings.mRenamedPackages.get(pkgName);
                 // 如果要進(jìn)行安裝的應(yīng)用,已經(jīng)存在盲憎,將是替換安裝嗅骄,則設(shè)置replace=true
                if (pkg.mOriginalPackages != null
                        && pkg.mOriginalPackages.contains(oldName)
                        && mPackages.containsKey(oldName)) {
                    // This package is derived from an original package,
                    // and this device has been updating from that original
                    // name.  We must continue using the original name, so
                    // rename the new package here.
                    // 包名設(shè)置為老的包名
                    pkg.setPackageName(oldName);
                    pkgName = pkg.packageName;
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
                            + oldName + " pkgName=" + pkgName);
                } else if (mPackages.containsKey(pkgName)) {
                    // This package, under its official name, already exists
                    // on the device; we should replace it.
                    replace = true;
                    if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
                }

                // Prevent apps opting out from runtime permissions
                if (replace) {
                  // 如果是替換,即升級(jí)安裝
                    PackageParser.Package oldPackage = mPackages.get(pkgName);
                    // 分別獲取老焙畔、新版本的TargetSdk 
                    final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
                    final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
                     // 如果老的TargetSdk 大于android 5.1 而新的TargetSdk 小于5.1,
                    if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
                            && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                        res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
                                "Package " + pkg.packageName + " new target SDK " + newTargetSdk
                                        + " doesn't support runtime permissions but the old"
                                        + " target SDK " + oldTargetSdk + " does.");
                        return;
                    }
                }
            }

            PackageSetting ps = mSettings.mPackages.get(pkgName);
            // 如果 ps 不為null掸读,同樣說明,已經(jīng)存在一個(gè)具有相同安裝包包名的程序宏多,被安裝儿惫,所以還是處理覆蓋安裝的問題。
            // 這里主要驗(yàn)證包名的簽名伸但,不一致的話肾请,是不能覆蓋安裝的,另外版本號(hào)也不能比安裝的地更胖,否則不能替換安裝
            if (ps != null) {
                if (DEBUG_INSTALL) Slog.d(TAG, "Existing package: " + ps);

                // Quick sanity check that we're signed correctly if updating;
                // we'll check this again later when scanning, but we want to
                // bail early here before tripping over redefined permissions.
                //檢查密鑰集合是否一致
                if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {
                    if (!checkUpgradeKeySetLP(ps, pkg)) {
                        res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package "
                                + pkg.packageName + " upgrade keys do not match the "
                                + "previously installed version");
                        return;
                    }
                } else {
                    try {
                        verifySignaturesLP(ps, pkg);
                    } catch (PackageManagerException e) {
                        res.setError(e.error, e.getMessage());
                        return;
                    }
                }
 
                // 判斷安裝的應(yīng)用是否存在同名的應(yīng)用铛铁,如果存在,判斷應(yīng)用是否帶有系統(tǒng)應(yīng)用的標(biāo)志
                oldCodePath = mSettings.mPackages.get(pkgName).codePathString;
                if (ps.pkg != null && ps.pkg.applicationInfo != null) {
                    systemApp = (ps.pkg.applicationInfo.flags &
                            ApplicationInfo.FLAG_SYSTEM) != 0;
                }
                res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }

            // ******* 第四步 *******
            // Check whether the newly-scanned package wants to define an already-defined perm
            // 檢查APK中定義的所有的權(quán)限是否已經(jīng)被其他應(yīng)用定義了却妨,如果重定義的是系統(tǒng)應(yīng)用定義的權(quán)限饵逐,那么忽略本app定義的這個(gè)權(quán)限。如果重定義的是非系統(tǒng)引用的權(quán)限彪标,那么本次安裝就以失敗返回倍权。
            int N = pkg.permissions.size();
            for (int i = N-1; i >= 0; i--) {
                PackageParser.Permission perm = pkg.permissions.get(i);
                BasePermission bp = mSettings.mPermissions.get(perm.info.name);
                if (bp != null) {
                    // If the defining package is signed with our cert, it's okay.  This
                    // also includes the "updating the same package" case, of course.
                    // "updating same package" could also involve key-rotation.
                    final boolean sigsOk;
                    if (bp.sourcePackage.equals(pkg.packageName)
                            && (bp.packageSetting instanceof PackageSetting)
                            && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
                                    scanFlags))) {
                        sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
                    } else {
                        sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
                                pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
                    }
                    if (!sigsOk) {
                        // If the owning package is the system itself, we log but allow
                        // install to proceed; we fail the install on all other permission
                        // redefinitions.
                        if (!bp.sourcePackage.equals("android")) {
                            res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
                                    + pkg.packageName + " attempting to redeclare permission "
                                    + perm.info.name + " already owned by " + bp.sourcePackage);
                            res.origPermission = perm.info.name;
                            res.origPackage = bp.sourcePackage;
                            return;
                        } else {
                            Slog.w(TAG, "Package " + pkg.packageName
                                    + " attempting to redeclare system permission "
                                    + perm.info.name + "; ignoring new declaration");
                            pkg.permissions.remove(i);
                        }
                    }
                }
            }
        }
        // 如果是帶帶有系統(tǒng)應(yīng)用標(biāo)志的應(yīng)用,卻要安裝在SD卡上捞烟,則報(bào)錯(cuò)返回薄声,安裝失敗原因錯(cuò)誤的安裝路徑
        if (systemApp && onExternal) {
            // Disable updates to system apps on sdcard
            res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                    "Cannot install updates to system apps on sdcard");
            return;
        }

         // ******* 第五步 *******
         // 如果是移動(dòng)APP
        if (args.move != null) {
            // We did an in-place move, so dex is ready to roll
            scanFlags |= SCAN_NO_DEX;
            scanFlags |= SCAN_MOVE;

            synchronized (mPackages) {
                final PackageSetting ps = mSettings.mPackages.get(pkgName);
                if (ps == null) {
                    res.setError(INSTALL_FAILED_INTERNAL_ERROR,
                            "Missing settings for moved package " + pkgName);
                }

                // We moved the entire application as-is, so bring over the
                // previously derived ABI information.
                pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
                pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
            }

        } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
            // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
            scanFlags |= SCAN_NO_DEX;

            try {
                derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
                        true /* extract libs */);
            } catch (PackageManagerException pme) {
                Slog.e(TAG, "Error deriving application ABI", pme);
                res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
                return;
            }

            // Run dexopt before old package gets removed, to minimize time when app is unavailable
            int result = mPackageDexOptimizer
                    .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
                            false /* defer */, false /* inclDependencies */,
                            true /* boot complete */);
            // 實(shí)際為dex2oat操作,用來將apk中的dex文件轉(zhuǎn)換為oat文件
            if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
                return;
            }
        }

         // ******* 第六步 *******
        // 重命名题画,將/data/app/vmdl{安裝會(huì)話}.tmp重命名為/data/apppp/包名-suffix,suffix為1默辨、2...
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            // 如果重命名失敗,則報(bào)錯(cuò)苍息,退出缩幸,安裝失敗原因:無法重命名
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }

         // ******* 第七步 *******
        startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);

        // ******* 第八步 *******
        if (replace) {
          // 覆蓋安裝
            replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                    installerPackageName, volumeUuid, res);
        } else {
            // 首次安裝
            installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                    args.user, installerPackageName, volumeUuid, res);
        }

        // ******* 第九步 *******
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
            }
        }
    }

上面注釋已經(jīng)很清楚了,我這里主要這個(gè)方法的整體流程分為9個(gè)部分如下:

  • 第一步:初始化變量
  • 第二步:解析APK
  • 第三步:判斷是新安裝還是升級(jí)安裝竞思,無論是新安裝還是升級(jí)安裝都是需要獲取簽名信息桌粉。
  • 第四步:檢查權(quán)限
  • 第五步:根據(jù)不同的安裝標(biāo)志,來進(jìn)行操作衙四,分為三種情況
    • 移動(dòng)操作:
    • 非鎖定安裝且沒有安裝在SD卡上:新安裝走這里,這里面主要做兩個(gè)操作:第①步是so拷貝患亿,第②步是進(jìn)行dex優(yōu)化传蹈,第③步機(jī)械性dex2oat操作押逼,將dex文件轉(zhuǎn)化為oat。
    • 如果上面兩個(gè)條件都不滿足惦界,則什么也不做
  • 第六步:重命名安裝:將/data/app/vmdl{安裝會(huì)話}.tmp重命名為/data/apppp/包名-suffix,suffix為1挑格、2...
  • 第七步:開始intent filter驗(yàn)證
  • 第八步:這里根據(jù)不同的安裝方式進(jìn)行不同的方式,主要有兩種情況
    • 覆蓋安裝即更新安裝:調(diào)用replacePackageLI方法進(jìn)行覆蓋安裝
    • 首次安裝:調(diào)用installNewPackageLI方法進(jìn)行首次安裝
  • 第九步:安裝收尾沾歪,調(diào)用PackageSetting的queryInstalledUsers設(shè)置安裝用戶

這里面涉及到7個(gè)比較復(fù)雜的方法漂彤,我會(huì)在后面的一片文章中詳細(xì)講解:

因?yàn)槲覀兪鞘状伟惭b斧吐,所以我們來看下installNewPackageLI方法內(nèi)部的執(zhí)行情況

四又固、PackageManagerService#installNewPackageLI(PackageParser.Package, int, int, UserHandle, String, String,PackageInstalledInfo)方法解析

代碼在PackageManagerService.java 11757行

    /*
     * Install a non-existing package.
     */
     // 安裝一個(gè)不存在的安裝包。
    private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
            UserHandle user, String installerPackageName, String volumeUuid,
            PackageInstalledInfo res) {
        // ******** 第一部分 ********
        // Remember this for later, in case we need to rollback this install
        String pkgName = pkg.packageName;

        if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);

        // 判斷是否存在APK的數(shù)據(jù)煤率。如果一個(gè)APK不是經(jīng)過經(jīng)常的卸載流程仰冠,那其歷史數(shù)據(jù)可能還是保留下來的。
        final boolean dataDirExists = Environment
                .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists();
        // 如果package已經(jīng)存在了
        synchronized(mPackages) {
            if (mSettings.mRenamedPackages.containsKey(pkgName)) {
            // 已經(jīng)安裝了具有相同名稱的包蝶糯,盡管它已經(jīng)被重命名為較舊的名稱洋只。所以應(yīng)該走更新流程而不是新安裝流程
                // A package with the same name is already installed, though
                // it has been renamed to an older name.  The package we
                // are trying to install should be installed as an update to
                // the existing one, but that has not been requested, so bail.
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling package running as "
                        + mSettings.mRenamedPackages.get(pkgName));
                return;
            }
            if (mPackages.containsKey(pkgName)) {
                 // 如果已經(jīng)有了相同的包名,則禁止安裝
                // Don't allow installation over an existing package with the same name.
                res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
                        + " without first uninstalling.");
                return;
            }
        }
      // ******** 第二部分 ********
        try {
             //核心調(diào)用: 安裝APK
            PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
                    System.currentTimeMillis(), user);
            // 更新Settings
            updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
            // delete the partially installed application. the data directory will have to be
            // restored if it was already existing
            // 如果安裝失敗昼捍,則執(zhí)行回退操作识虚,刪除創(chuàng)建的文件夾等緩存文件
            if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
                // remove package from internal structures.  Note that we want deletePackageX to
                // delete the package data and cache directories that it created in
                // scanPackageLocked, unless those directories existed before we even tried to
                // install.
                deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
                        dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
                                res.removedInfo, true);
            }

        } catch (PackageManagerException e) {
            res.setError("Package couldn't be installed in " + pkg.codePath, e);
        }
    }

這個(gè)方法我主要分為兩個(gè)部分:

  • 第一部分:安裝前檢索,主要是根據(jù)兩個(gè)檢索條件來排除相同的包名的情況:
    - 判斷重命名的包中是否含有相同的包名
    - 判斷已有的安裝包中是否有相同的包名
  • 第二部分:進(jìn)行安裝:主要是調(diào)用scanPackageLI進(jìn)行安裝妒茬,通過這個(gè)方法APK的跟中信息都會(huì)記錄在PackageManagerService中担锤;之后調(diào)用了updateSettingsLI進(jìn)行設(shè)置信息的更新,主要是更新了權(quán)限信息與安裝完成信息乍钻。如果安裝失敗就會(huì)刪除安裝包信息肛循。

這個(gè)方法內(nèi)部有兩個(gè)核心方法如下:

  • PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,System.currentTimeMillis(), user):負(fù)責(zé)安裝
  • updateSettingsLI(newPackage, installerPackageName, null, null, res):安裝后的Setting信息的更新铭腕。

下面我們來看下這個(gè)兩個(gè)方法

五、PackageManagerService#scanPackageLI(File scanFile, int parseFlags, int scanFlags,long currentTime, UserHandle user) 方法解析

代碼在PackageManagerService.java 6466行

    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        boolean success = false;
        try {
            final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
                    currentTime, user);
            success = true;
            return res;
        } finally {
            if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
                removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
            }
        }
    }

scanPackageLI主要調(diào)用了scanPackageDirtyLI多糠,如果調(diào)用失敗則調(diào)用removeDataDirsLI來移除安裝信息累舷,scanPackageDirtyLI的代碼如下:

這塊代碼之前在APK安裝流程詳解8——PackageManagerService的啟動(dòng)流程(下)中的六、6夹孔、PackageManagerService#scanPackageLI(PackageParser.Package, int, int, long, UserHandle)方法解析(首參數(shù)為Package)已經(jīng)講解過了被盈,這里就不詳細(xì)講解了

六、PackageManagerService#updateSettingsLIupdateSettingsLI(PackageParser.Package, String,String, int[], boolean[], PackageInstalledInfo,UserHandle)方法解析

代碼在PackageManagerService.java 12158行

    private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
            String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res,
            UserHandle user) {

        //************** 第一步 **************
        // 包名
        String pkgName = newPackage.packageName;
        synchronized (mPackages) {
            //write settings. the installStatus will be incomplete at this stage.
            //note that the new package setting would have already been
            //added to mPackages. It hasn't been persisted yet.
            // 設(shè)置安裝狀態(tài)搭伤,并寫入
            mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
            mSettings.writeLPr();
        }

        if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.codePath);
        //************** 第二步 **************
        synchronized (mPackages) {
            // 更新權(quán)限
            updatePermissionsLPw(newPackage.packageName, newPackage,
                    UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
                            ? UPDATE_PERMISSIONS_ALL : 0));
            // For system-bundled packages, we assume that installing an upgraded version
            // of the package implies that the user actually wants to run that new code,
            // so we enable the package.
            // 對(duì)于綁定的軟件只怎,如果是更新,我們可以理解用戶要運(yùn)行新程序闷畸,我們啟動(dòng)這個(gè)軟件

             //************** 第三步 **************
            PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps != null) {
                 // 如果系統(tǒng)中擁有同樣包名的設(shè)置PackageSetting
                if (isSystemApp(newPackage)) {
                   // 如果是系統(tǒng)應(yīng)用
                    // NB: implicit assumption that system package upgrades apply to all users
                    // 系統(tǒng)應(yīng)用適用于所有用戶
                    if (DEBUG_INSTALL) {
                        Slog.d(TAG, "Implicitly enabling system package on upgrade: " + pkgName);
                    }
                    
                    //如果存在已經(jīng)安裝該應(yīng)用的用戶組
                    if (res.origUsers != null) {
                        for (int userHandle : res.origUsers) {
                            // 設(shè)置它為啟用狀態(tài) 
                            ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
                                    userHandle, installerPackageName);
                        }
                    }
                    // Also convey the prior install/uninstall state
                    // 修改其對(duì)應(yīng)的用戶狀態(tài)
                    if (allUsers != null && perUserInstalled != null) {
                        for (int i = 0; i < allUsers.length; i++) {
                            if (DEBUG_INSTALL) {
                                Slog.d(TAG, "    user " + allUsers[i]
                                        + " => " + perUserInstalled[i]);
                            }
                            ps.setInstalled(perUserInstalled[i], allUsers[i]);
                        }
                        // these install state changes will be persisted in the
                        // upcoming call to mSettings.writeLPr().
                    }
                }
                // It's implied that when a user requests installation, they want the app to be
                // installed and enabled.
                // 用戶進(jìn)行安裝的時(shí)候尝盼,其實(shí)他們是希望安裝并啟用應(yīng)用程序的。所以設(shè)置他們的屬性
                int userId = user.getIdentifier();
                if (userId != UserHandle.USER_ALL) {
                    ps.setInstalled(true, userId);
                    ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName);
                }
            }
            res.name = pkgName;
            res.uid = newPackage.applicationInfo.uid;
            res.pkg = newPackage;
            mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);
            mSettings.setInstallerPackageName(pkgName, installerPackageName);
            res.returnCode = PackageManager.INSTALL_SUCCEEDED;
            //to update install status
            // 寫入
           //************** 第四步 **************
            mSettings.writeLPr();
        }
    }

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

  • 第一步:設(shè)置更新狀態(tài)
  • 第二步:更新權(quán)限
  • 第三步:調(diào)整包狀態(tài)
  • 第四步:Settings寫入

至此安裝結(jié)束佑菩。

七盾沫、 PackageHandler的處理Message的what值為POST_INSTALL的情況解析

代碼在PackageManagerService.java1333行

        void doHandleMessage(Message msg) {
            switch (msg.what) {
            ...
                case POST_INSTALL: {
                    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
                    //***************** 第一步 ****************
                    // 我們知道m(xù)sg的arg1是token,arg2是0
                    PostInstallData data = mRunningInstalls.get(msg.arg1);
                   // 因?yàn)橐呀?jīng)安裝成功了殿漠,所以在 正在安裝列表中刪除了這個(gè)選項(xiàng)
                    mRunningInstalls.delete(msg.arg1);
                    boolean deleteOld = false;

                    if (data != null) {
                        InstallArgs args = data.args;
                        PackageInstalledInfo res = data.res;
   
                        // 如果安裝成功
                        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                            final String packageName = res.pkg.applicationInfo.packageName;
                            res.removedInfo.sendBroadcast(false, true, false);
                            Bundle extras = new Bundle(1);
                            extras.putInt(Intent.EXTRA_UID, res.uid);

                            // Now that we successfully installed the package, grant runtime
                            // permissions if requested before broadcasting the install.
                             // 如果已經(jīng)成功的安裝了應(yīng)用赴精,在發(fā)送廣播之前先授予一些必要的權(quán)限
                            // 這些權(quán)限在 installPackageAsUser 中創(chuàng)建 InstallParams 時(shí)傳遞的,為null绞幌。
                            if ((args.installFlags
                                    & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {

                                 //***************** 第二步 ****************
                                grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
                                        args.installGrantPermissions);
                            }

                            // Determine the set of users who are adding this
                            // package for the first time vs. those who are seeing
                            // an update.
                            // 看一下當(dāng)前應(yīng)用對(duì)于那些用戶是第一此安裝蕾哟,那些用戶是更新升級(jí)安裝
                            int[] firstUsers;
                            int[] updateUsers = new int[0];
                            if (res.origUsers == null || res.origUsers.length == 0) {
                                //所有用戶都是第一次安裝
                                firstUsers = res.newUsers;
                            } else {
                                firstUsers = new int[0];
                                // 這里通過剛剛已經(jīng)安裝該包的用戶中選出 那些之前安裝過該包的用戶
                                for (int i=0; i<res.newUsers.length; i++) {
                                    int user = res.newUsers[i];
                                    boolean isNew = true;
                                    for (int j=0; j<res.origUsers.length; j++) {
                                        if (res.origUsers[j] == user) {
                                             // 找到以前安裝過該包的用戶
                                            isNew = false;
                                            break;
                                        }
                                    }
                                    if (isNew) {
                                        int[] newFirst = new int[firstUsers.length+1];
                                        System.arraycopy(firstUsers, 0, newFirst, 0,
                                                firstUsers.length);
                                        newFirst[firstUsers.length] = user;
                                        firstUsers = newFirst;
                                    } else {
                                        int[] newUpdate = new int[updateUsers.length+1];
                                        System.arraycopy(updateUsers, 0, newUpdate, 0,
                                                updateUsers.length);
                                        newUpdate[updateUsers.length] = user;
                                        updateUsers = newUpdate;
                                    }
                                }
                            }

                            //***************** 第三步 ****************
                           // 安裝完成之后發(fā)送"ACTION_PACKAGE_ADDED"廣播
                            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                    packageName, extras, null, null, firstUsers);
                            final boolean update = res.removedInfo.removedPackage != null;
                            if (update) {
                                extras.putBoolean(Intent.EXTRA_REPLACING, true);
                            }

                            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                                    packageName, extras, null, null, updateUsers);

                            //***************** 第四步 ****************
                            if (update) {
                                // 如果是升級(jí)更新安裝,還會(huì)發(fā)送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED廣播
                                // 這兩個(gè)廣播不同之處在于PACKAGE_REPLACE將攜帶一個(gè)extra信息
                                sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                                        packageName, extras, null, null, updateUsers);
                                sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                                        null, null, packageName, null, updateUsers);


                                //***************** 第五步 ****************
                                // treat asec-hosted packages like removable media on upgrade
                                // 判斷該包是否設(shè)置了PRIVATE_FLAG_FORWARD_LOCK標(biāo)志或者是要求安裝在SD卡上
                                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
                                    if (DEBUG_INSTALL) {
                                        Slog.i(TAG, "upgrading pkg " + res.pkg
                                                + " is ASEC-hosted -> AVAILABLE");
                                    }
                                    int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
                                    ArrayList<String> pkgList = new ArrayList<String>(1);
                                    pkgList.add(packageName);
                                    sendResourcesChangedBroadcast(true, true,
                                            pkgList,uidArray, null);
                                }
                            }
                            if (res.removedInfo.args != null) {
                                // Remove the replaced package's older resources safely now
                                // 刪除被替換應(yīng)用的資源目錄
                                deleteOld = true;
                            }


                             //***************** 第六步 ****************
                            // If this app is a browser and it's newly-installed for some
                            // users, clear any default-browser state in those users
                             // 針對(duì)Browser做一些處理
                            if (firstUsers.length > 0) {
                                // the app's nature doesn't depend on the user, so we can just
                                // check its browser nature in any user and generalize.
                                // 判斷是否是瀏覽器應(yīng)用
                                if (packageIsBrowser(packageName, firstUsers[0])) {
                                    synchronized (mPackages) {
                                        for (int userId : firstUsers) {
                                            mSettings.setDefaultBrowserPackageNameLPw(null, userId);
                                        }
                                    }
                                }
                            }
                            // Log current value of "unknown sources" setting
                            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
                                getUnknownSourcesSettings());
                        }

                        //***************** 第七步 ****************
                        // Force a gc to clear up things
                        // 執(zhí)行g(shù)c操作
                        Runtime.getRuntime().gc();
                        // We delete after a gc for applications  on sdcard.

                        //***************** 第八步 ****************
                         // 執(zhí)行刪除操作
                        if (deleteOld) {
                            synchronized (mInstallLock) {
                                // 調(diào)用FileInstallArgs的doPostDeleteLI進(jìn)行資源清理
                                res.removedInfo.args.doPostDeleteLI(true);
                            }
                        }

                        //***************** 第九步 ****************
                        if (args.observer != null) {
                            try {
                                Bundle extras = extrasForInstallResult(res);
                                // 回調(diào)onPackageInstalled方法
                                args.observer.onPackageInstalled(res.name, res.returnCode,
                                        res.returnMsg, extras);
                            } catch (RemoteException e) {
                                Slog.i(TAG, "Observer no longer exists.");
                            }
                        }
                    } else {
                        Slog.e(TAG, "Bogus post-install token " + msg.arg1);
                    }
                } break;
       ...
      }

終于到了裝載的最后一個(gè)流程了莲蜘,我將這個(gè)方法內(nèi)部分為9個(gè)部分

  • 第一步:這里主要是先將安裝信息從安裝列列表中移除谭确,這個(gè)也是前面在processPendingInstall中添加的
  • 第二步:安裝成功后,獲取運(yùn)行時(shí)權(quán)限
  • 第三步:獲取權(quán)限后票渠,發(fā)送ACTION_PACKAGE_ADDED廣播逐哈,告訴Laucher之流,來新客人了问顷,趕緊把icon啥的放上去昂秃。
  • 第四步:如果是升級(jí)更新則在發(fā)送兩條廣播
    • ACTION_PACKAGE_REPLACED:一個(gè)新版本的應(yīng)用安裝到設(shè)備上,替換換之前已經(jīng)存在的版本
    • ACTION_MY_PACKAGE_REPLACED:應(yīng)用的新版本替換舊版本被安裝杜窄,只發(fā)給被更新的應(yīng)用自己
  • 第五步:如果安裝包中設(shè)置了PRIVATE_FLAG_FORWARD_LOCK或者被要求安裝在SD卡上肠骆,則調(diào)用sendResourcesChangedBroadcast方法來發(fā)送一個(gè)資源更改的廣播
  • 第六步:如果該應(yīng)用是一個(gè)瀏覽器,則要清除瀏覽器設(shè)置塞耕,重新檢查瀏覽器設(shè)置蚀腿。
  • 第七步:強(qiáng)制調(diào)用gc,出發(fā)JVM進(jìn)行垃圾回收操作扫外。
  • 第八步:刪除舊的安裝信息唯咬。
  • 第九步:回調(diào)回調(diào)args.observer.packageInstalled方法纱注。告訴PackageInstaller安裝結(jié)果。從而實(shí)現(xiàn)了安裝回調(diào)到UI層胆胰。

八、總結(jié)

1刻获、安裝大致流程圖

install_apk

安裝過程:復(fù)制apk安裝包到/data/app目錄下蜀涨,解壓并掃描安裝包,向資源管理器注入apk資源蝎毡,解析AndroidManifest文件厚柳,并在/data/data目錄下創(chuàng)建對(duì)應(yīng)的應(yīng)用數(shù)據(jù)目錄,然后針對(duì)dalvik/art環(huán)境優(yōu)化dex文件沐兵,保存到dalvik-cache目錄别垮,將AndroidManifest文件解析出的組件、權(quán)限注冊(cè)到PackageManagerService扎谎,完成后發(fā)送廣播碳想。

2、安裝詳細(xì)時(shí)序圖

上面的圖太模糊了毁靶,不利于了解細(xì)節(jié)胧奔,這里"偷"了一張書序圖,以便大家分析

時(shí)序圖.png

點(diǎn)擊放大查看高清無碼大圖

3预吆、整體架構(gòu)圖

整體架構(gòu)圖.png

上一篇文章 APK安裝流程詳解12——PackageManagerService中的新安裝流程上(拷貝)
下一篇文章 APK安裝流程詳解14——PMS中的新安裝流程上(拷貝)補(bǔ)充

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末龙填,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拐叉,更是在濱河造成了極大的恐慌岩遗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤瘦,死亡現(xiàn)場(chǎng)離奇詭異宿礁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)廷粒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門窘拯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人坝茎,你說我怎么就攤上這事涤姊。” “怎么了嗤放?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵思喊,是天一觀的道長。 經(jīng)常有香客問我次酌,道長恨课,這世上最難降的妖魔是什么舆乔? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮剂公,結(jié)果婚禮上希俩,老公的妹妹穿的比我還像新娘。我一直安慰自己纲辽,他們只是感情好颜武,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著拖吼,像睡著了一般鳞上。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吊档,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天篙议,我揣著相機(jī)與錄音,去河邊找鬼怠硼。 笑死鬼贱,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拒名。 我是一名探鬼主播吩愧,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼增显!你這毒婦竟也來了雁佳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤同云,失蹤者是張志新(化名)和其女友劉穎糖权,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體炸站,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡星澳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旱易。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片禁偎。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖阀坏,靈堂內(nèi)的尸體忽然破棺而出如暖,到底是詐尸還是另有隱情,我是刑警寧澤忌堂,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布盒至,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏枷遂。R本人自食惡果不足惜樱衷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望酒唉。 院中可真熱鬧矩桂,春花似錦、人聲如沸黔州。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽流妻。三九已至,卻和暖如春笆制,著一層夾襖步出監(jiān)牢的瞬間绅这,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工在辆, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留证薇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓匆篓,卻偏偏與公主長得像浑度,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子鸦概,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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