Android源码解析(十二)-- Android系统启动并解析Manifest的流程

作者: android01 发布时间: 2019-08-29 浏览: 176 次 编辑

最近有同学问我关于Manifest何时被系统解析的问题,正好也分析到这一块了,索性这一章就讲解一下android系统何时解析Manifest吧,这里的Manifest指的是android安装文件apk中的androidManifest.xml文件是何时被解析的。
大家应该都知道,Android系统启动之后,我们就可以在一个应用中打开另一个从未打开过的应用,或者是在一个应用中发送广播,如果另外一个应用设置了这个广播的接收器,那么这个应用进程就会被启动并接收该广播并作出相应的处理,这样的例子很多,我们可以猜测到Android系统在启动的时候就会抓取到了系统中所有安装的应用信息(应该是解析apk文件的Manifest信息),即在Android系统的启动过程中就已经解析了系统中安装应用的androidManifest.xml文件并保存起来了,那么这个过程具体是如何的呢?

其实android系统启动过程中解析Manifest的流程是通过PackageManagerService服务来实现的。这里我们重点分析一下PackageManagerService服务是如何解析Manifest的。

首先看一下在SystemServer进程启动过程中是如何启动PackageManagerService服务的:

private void startBootstrapServices() {
    ...
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
    mFirstBoot = mPackageManagerService.isFirstBoot();
    mPackageManager = mSystemContext.getPackageManager();

    ...
}

在SystemServer进程启动过程中会调用SystemServer类的startBootstrapServices方法(主要用于启动ActivityManagerService服务和PackageManagerService服务),然后会在这个方法中会调用PackageManagerService.main静态方法,这个方法主要是用来初始化PackageManagerService服务并执行相关逻辑的。下面我来看一下main方法的具体逻辑:

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);
    ServiceManager.addService("package", m);
    return m;
}

可以发现main方法的实现逻辑主要是创建了一个PackageManagerService对象,并将这个对象添加到ServierManager中为其他组件提供服务。好吧,看来PackageManagerService的初始化操作主要是在PackageManagerService的构造方法中了,下面我们来看一下其构造方法的实现逻辑:

File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");

PackageManagerService的构造方法代码量比较大,这里就不贴出所有的代码了,我们主要和解析Manifest相关的主要代码,在构造方法中有这样几段代码。可以发现在构造方法中,解析了系统中几个apk的安装目录,这几个目录就是系统中安装apk的目录,android系统会默认解析这几个目录下apk文件,也就是说如果我们android手机在其他的目录下存在apk文件系统是不会默认解析的,反过来说,如果我们把我们的apk文件移动到这几个目录下,那么重新启动操作系统,该apk文件就会被系统解析并执行相关的逻辑操作,具体做什么操作呢?我们看下面的实现。

/ overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);

// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_IS_PRIVILEGED,
        scanFlags | SCAN_NO_DEX, 0);

// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);

// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
    vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
    // failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

在我们刚刚的PackageManagerService.mani方法中,解析完刚刚的几个系统目录之后系统会调用scanDirLI方法,那么这个方法主要是做什么用的呢?看它的名字应该是遍历这个系统目录。好吧,这个方法主要就是用于解析上面几个目录下的apk文件的。不信?我们看一下scanDirLI方法的具体实现:

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    final File[] files = dir.listFiles();
    if (ArrayUtils.isEmpty(files)) {
        Log.d(TAG, "No files in app dir " + dir);
        return;
    }

    if (DEBUG_PACKAGE_SCANNING) {
        Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                + " flags=0x" + Integer.toHexString(parseFlags));
    }

    for (File file : files) {
        final boolean isPackage = (isApkFile(file) || file.isDirectory())
                && !PackageInstallerService.isStageName(file.getName());
        if (!isPackage) {
            // Ignore entries which are not packages
            continue;
        }
        try {
            scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                    scanFlags, currentTime, null);
        } catch (PackageManagerException e) {
            Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

            // Delete invalid userdata apps
            if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                    e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                if (file.isDirectory()) {
                    mInstaller.rmPackageDir(file.getAbsolutePath());
                } else {
                    file.delete();
                }
            }
        }
    }
}

可以放下其首先会遍历该目录下的所有文件,并判断是否是apk文件,如果是apk文件则调用scanPackageLI方法,scanPackageLI方法的名字很明显,就是用于解析这个apk文件的。

继续看一下scanPakcageLI方法的实现:

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        parseFlags |= mDefParseFlags;
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);

        if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
        }

        final PackageParser.Package pkg;
        try {
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        }
        ...
}

好吧,这个方法也比较复杂,这里只是列出重点相关的代码,我们可以发现在这个方法中创建了一个PackagerParser对象,并调用了parsePackage方法,这个方法其实就是解析Manifest的主要方法,我们可以看一下其具体的实现:

public Package parsePackage(File packageFile, int flags) throws PackageParserException {
    if (packageFile.isDirectory()) {
        return parseClusterPackage(packageFile, flags);
    } else {
        return parseMonolithicPackage(packageFile, flags);
    }
}

可以发现,若我们解析的File对象是一个文件夹则执行调用parseClusterPackage方法,否则调用执行parseMonolithicPackage方法,很明显的因为我们这里解析的是apk文件(在上一方法中我们循环遍历得到了apk文件,这里的File对象就代表了一个个的apk文件信息),所以这里会执行parseMonolithicPackage方法,然后我们来看一下parseMonolithicPackage方法:

public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
    if (mOnlyCoreApps) {
        final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
        if (!lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + apkFile);
        }
    }

    final AssetManager assets = new AssetManager();
    try {
        final Package pkg = parseBaseApk(apkFile, assets, flags);
        pkg.codePath = apkFile.getAbsolutePath();
        return pkg;
    } finally {
        IoUtils.closeQuietly(assets);
    }
}

可以看出,这里又调用了parseBaseApk方法:

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
        ...
        final Package pkg = parseBaseApk(res, parser, flags, outError);
        ...
}

可以看出,这个parseBaseApk方法调用了其重载的parseBaseApk方法:

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
        && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
        continue;
    }

    String tagName = parser.getName();
    if (tagName.equals("application")) {
        if (foundApp) {
            if (RIGID_PARSER) {
                outError[0] = "<manifest> has more than one <application>";
                mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                return null;
            } else {
                Slog.w(TAG, "<manifest> has more than one <application>");
                XmlUtils.skipCurrentTag(parser);
                continue;
            }
        }

        foundApp = true;
        if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
            return null;
        }
    } else if (tagName.equals("overlay")) {
        pkg.mTrustedOverlay = trustedOverlay;

        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestResourceOverlay);
        pkg.mOverlayTarget = sa.getString(
                com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage);
        pkg.mOverlayPriority = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority,
                -1);
        sa.recycle();

        if (pkg.mOverlayTarget == null) {
            outError[0] = "<overlay> does not specify a target package";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;
        }
        if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
            outError[0] = "<overlay> priority must be between 0 and 9999";
            mParseError =
                PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;
        }
        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("key-sets")) {
        if (!parseKeySets(pkg, res, parser, attrs, outError)) {
            return null;
        }
    } else if (tagName.equals("permission-group")) {
        if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
            return null;
        }
    } else if (tagName.equals("permission")) {
        if (parsePermission(pkg, res, parser, attrs, outError) == null) {
            return null;
        }
    } else if (tagName.equals("permission-tree")) {
        if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
            return null;
        }
    } else if (tagName.equals("uses-permission")) {
        if (!parseUsesPermission(pkg, res, parser, attrs)) {
            return null;
        }
    } else if (tagName.equals("uses-permission-sdk-m")
            || tagName.equals("uses-permission-sdk-23")) {
        if (!parseUsesPermission(pkg, res, parser, attrs)) {
            return null;
        }
    } else if (tagName.equals("uses-configuration")) {
        ConfigurationInfo cPref = new ConfigurationInfo();
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration);
        cPref.reqTouchScreen = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqTouchScreen,
                Configuration.TOUCHSCREEN_UNDEFINED);
        cPref.reqKeyboardType = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqKeyboardType,
                Configuration.KEYBOARD_UNDEFINED);
        if (sa.getBoolean(
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqHardKeyboard,
                false)) {
            cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_HARD_KEYBOARD;
        }
        cPref.reqNavigation = sa.getInt(
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqNavigation,
                Configuration.NAVIGATION_UNDEFINED);
        if (sa.getBoolean(
                com.android.internal.R.styleable.AndroidManifestUsesConfiguration_reqFiveWayNav,
                false)) {
            cPref.reqInputFeatures |= ConfigurationInfo.INPUT_FEATURE_FIVE_WAY_NAV;
        }
        sa.recycle();
        pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);

        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("uses-feature")) {
        FeatureInfo fi = parseUsesFeature(res, attrs);
        pkg.reqFeatures = ArrayUtils.add(pkg.reqFeatures, fi);

        if (fi.name == null) {
            ConfigurationInfo cPref = new ConfigurationInfo();
            cPref.reqGlEsVersion = fi.reqGlEsVersion;
            pkg.configPreferences = ArrayUtils.add(pkg.configPreferences, cPref);
        }

        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("feature-group")) {
        FeatureGroupInfo group = new FeatureGroupInfo();
        ArrayList<FeatureInfo> features = null;
        final int innerDepth = parser.getDepth();
        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                continue;
            }

            final String innerTagName = parser.getName();
            if (innerTagName.equals("uses-feature")) {
                FeatureInfo featureInfo = parseUsesFeature(res, attrs);
                // FeatureGroups are stricter and mandate that
                // any <uses-feature> declared are mandatory.
                featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
                features = ArrayUtils.add(features, featureInfo);
            } else {
                Slog.w(TAG, "Unknown element under <feature-group>: " + innerTagName +
                        " at " + mArchiveSourcePath + " " +
                        parser.getPositionDescription());
            }
            XmlUtils.skipCurrentTag(parser);
        }

        if (features != null) {
            group.features = new FeatureInfo[features.size()];
            group.features = features.toArray(group.features);
        }
        pkg.featureGroups = ArrayUtils.add(pkg.featureGroups, group);

    } else if (tagName.equals("uses-sdk")) {
        if (SDK_VERSION > 0) {
            sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestUsesSdk);

            int minVers = 0;
            String minCode = null;
            int targetVers = 0;
            String targetCode = null;
            
            TypedValue val = sa.peekValue(
                    com.android.internal.R.styleable.AndroidManifestUsesSdk_minSdkVersion);
            if (val != null) {
                if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                    targetCode = minCode = val.string.toString();
                } else {
                    // If it's not a string, it's an integer.
                    targetVers = minVers = val.data;
                }
            }
            
            val = sa.peekValue(
                    com.android.internal.R.styleable.AndroidManifestUsesSdk_targetSdkVersion);
            if (val != null) {
                if (val.type == TypedValue.TYPE_STRING && val.string != null) {
                    targetCode = minCode = val.string.toString();
                } else {
                    // If it's not a string, it's an integer.
                    targetVers = val.data;
                }
            }
            
            sa.recycle();

            if (minCode != null) {
                boolean allowedCodename = false;
                for (String codename : SDK_CODENAMES) {
                    if (minCode.equals(codename)) {
                        allowedCodename = true;
                        break;
                    }
                }
                if (!allowedCodename) {
                    if (SDK_CODENAMES.length > 0) {
                        outError[0] = "Requires development platform " + minCode
                                + " (current platform is any of "
                                + Arrays.toString(SDK_CODENAMES) + ")";
                    } else {
                        outError[0] = "Requires development platform " + minCode
                                + " but this is a release platform.";
                    }
                    mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                    return null;
                }
            } else if (minVers > SDK_VERSION) {
                outError[0] = "Requires newer sdk version #" + minVers
                        + " (current version is #" + SDK_VERSION + ")";
                mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                return null;
            }
            
            if (targetCode != null) {
                boolean allowedCodename = false;
                for (String codename : SDK_CODENAMES) {
                    if (targetCode.equals(codename)) {
                        allowedCodename = true;
                        break;
                    }
                }
                if (!allowedCodename) {
                    if (SDK_CODENAMES.length > 0) {
                        outError[0] = "Requires development platform " + targetCode
                                + " (current platform is any of "
                                + Arrays.toString(SDK_CODENAMES) + ")";
                    } else {
                        outError[0] = "Requires development platform " + targetCode
                                + " but this is a release platform.";
                    }
                    mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK;
                    return null;
                }
                // If the code matches, it definitely targets this SDK.
                pkg.applicationInfo.targetSdkVersion
                        = android.os.Build.VERSION_CODES.CUR_DEVELOPMENT;
            } else {
                pkg.applicationInfo.targetSdkVersion = targetVers;
            }
        }

        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("supports-screens")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestSupportsScreens);

        pkg.applicationInfo.requiresSmallestWidthDp = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_requiresSmallestWidthDp,
                0);
        pkg.applicationInfo.compatibleWidthLimitDp = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_compatibleWidthLimitDp,
                0);
        pkg.applicationInfo.largestWidthLimitDp = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_largestWidthLimitDp,
                0);

        // This is a trick to get a boolean and still able to detect
        // if a value was actually set.
        supportsSmallScreens = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_smallScreens,
                supportsSmallScreens);
        supportsNormalScreens = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_normalScreens,
                supportsNormalScreens);
        supportsLargeScreens = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_largeScreens,
                supportsLargeScreens);
        supportsXLargeScreens = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_xlargeScreens,
                supportsXLargeScreens);
        resizeable = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_resizeable,
                resizeable);
        anyDensity = sa.getInteger(
                com.android.internal.R.styleable.AndroidManifestSupportsScreens_anyDensity,
                anyDensity);

        sa.recycle();
        
        XmlUtils.skipCurrentTag(parser);
        
    } else if (tagName.equals("protected-broadcast")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestProtectedBroadcast);

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        String name = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name);

        sa.recycle();

        if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
            if (pkg.protectedBroadcasts == null) {
                pkg.protectedBroadcasts = new ArrayList<String>();
            }
            if (!pkg.protectedBroadcasts.contains(name)) {
                pkg.protectedBroadcasts.add(name.intern());
            }
        }

        XmlUtils.skipCurrentTag(parser);
        
    } else if (tagName.equals("instrumentation")) {
        if (parseInstrumentation(pkg, res, parser, attrs, outError) == null) {
            return null;
        }
        
    } else if (tagName.equals("original-package")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestOriginalPackage);

        String orig =sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);
        if (!pkg.packageName.equals(orig)) {
            if (pkg.mOriginalPackages == null) {
                pkg.mOriginalPackages = new ArrayList<String>();
                pkg.mRealPackage = pkg.packageName;
            }
            pkg.mOriginalPackages.add(orig);
        }

        sa.recycle();

        XmlUtils.skipCurrentTag(parser);
        
    } else if (tagName.equals("adopt-permissions")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestOriginalPackage);

        String name = sa.getNonConfigurationString(
                com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0);

        sa.recycle();

        if (name != null) {
            if (pkg.mAdoptPermissions == null) {
                pkg.mAdoptPermissions = new ArrayList<String>();
            }
            pkg.mAdoptPermissions.add(name);
        }

        XmlUtils.skipCurrentTag(parser);
        
    } else if (tagName.equals("uses-gl-texture")) {
        // Just skip this tag
        XmlUtils.skipCurrentTag(parser);
        continue;
        
    } else if (tagName.equals("compatible-screens")) {
        // Just skip this tag
        XmlUtils.skipCurrentTag(parser);
        continue;
    } else if (tagName.equals("supports-input")) {
        XmlUtils.skipCurrentTag(parser);
        continue;
        
    } else if (tagName.equals("eat-comment")) {
        // Just skip this tag
        XmlUtils.skipCurrentTag(parser);
        continue;
        
    } else if (RIGID_PARSER) {
        outError[0] = "Bad element under <manifest>: "
            + parser.getName();
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return null;

    } else {
        Slog.w(TAG, "Unknown element under <manifest>: " + parser.getName()
                + " at " + mArchiveSourcePath + " "
                + parser.getPositionDescription());
        XmlUtils.skipCurrentTag(parser);
        continue;
    }
}

在这个parseBaseApk方法中有一个while循环,该循环主要就是用于解析AndroidManifest.xml文件中的节点信息。在开始解析application节点的时候,同时调用了parseBaseApplication方法,该方法解析了application节点下的activity,service,broadcast,contentprovier等组件的定义信息:

while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
        && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
        continue;
    }

    String tagName = parser.getName();
    if (tagName.equals("activity")) {
        Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
                owner.baseHardwareAccelerated);
        if (a == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.activities.add(a);

    } else if (tagName.equals("receiver")) {
        Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
        if (a == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.receivers.add(a);

    } else if (tagName.equals("service")) {
        Service s = parseService(owner, res, parser, attrs, flags, outError);
        if (s == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.services.add(s);

    } else if (tagName.equals("provider")) {
        Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
        if (p == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.providers.add(p);

    } else if (tagName.equals("activity-alias")) {
        Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
        if (a == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

        owner.activities.add(a);

    } else if (parser.getName().equals("meta-data")) {
        // note: application meta-data is stored off to the side, so it can
        // remain null in the primary copy (we like to avoid extra copies because
        // it can be large)
        if ((owner.mAppMetaData = parseMetaData(res, parser, attrs, owner.mAppMetaData,
                outError)) == null) {
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }

    } else if (tagName.equals("library")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestLibrary);

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        String lname = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestLibrary_name);

        sa.recycle();

        if (lname != null) {
            lname = lname.intern();
            if (!ArrayUtils.contains(owner.libraryNames, lname)) {
                owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
            }
        }

        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("uses-library")) {
        sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestUsesLibrary);

        // Note: don't allow this value to be a reference to a resource
        // that may change.
        String lname = sa.getNonResourceString(
                com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
        boolean req = sa.getBoolean(
                com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
                true);

        sa.recycle();

        if (lname != null) {
            lname = lname.intern();
            if (req) {
                owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
            } else {
                owner.usesOptionalLibraries = ArrayUtils.add(
                        owner.usesOptionalLibraries, lname);
            }
        }

        XmlUtils.skipCurrentTag(parser);

    } else if (tagName.equals("uses-package")) {
        // Dependencies for app installers; we don't currently try to
        // enforce this.
        XmlUtils.skipCurrentTag(parser);

    } else {
        if (!RIGID_PARSER) {
            Slog.w(TAG, "Unknown element under <application>: " + tagName
                    + " at " + mArchiveSourcePath + " "
                    + parser.getPositionDescription());
            XmlUtils.skipCurrentTag(parser);
            continue;
        } else {
            outError[0] = "Bad element under <application>: " + tagName;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return false;
        }
    }
}

这样,经过这里循环遍历,整个androidManifest的节点信息就被解析并保存在了Package对象中。可以看到我们平时在Manifest中定义的各种节点,其实都是在这里有所体现。当androidManifest.xml文件被解析完成之后会调用我们刚刚介绍的scanPackageLI的重载方法,将解析完成的Package对象信息保存的Setting对象中,这个对象用于保存app的安装信息,具体实现是在方法:

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException

当解析完成manifest文件之后会调用其重载方法:

// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
        | SCAN_UPDATE_SIGNATURE, currentTime, user);

这样,解析的manifest文件信息就会被保存到Settings中,并持久化,然后执行安装apk的操作,我们可以看一下该重载方法的具体实现:

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

可以发现其内部调用了scanPackageDirtyLI方法,这个方法就是实际实现持久化manifest信息并安装APK操作的:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    ...
    // And now re-install the app.
    ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
                  pkg.applicationInfo.seinfo);
    ...
}

可以发现其内部调用了createDataDirLI,该方法主要实现安装apk的操作。

private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(volumeUuid, packageName,
                    UserHandle.getUid(user, uid), user, seinfo);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}

查看该方法的实现:

public int install(String uuid, String name, int uid, int gid, String seinfo) {
    StringBuilder builder = new StringBuilder("install");
    builder.append(' ');
    builder.append(escapeNull(uuid));
    builder.append(' ');
    builder.append(name);
    builder.append(' ');
    builder.append(uid);
    builder.append(' ');
    builder.append(gid);
    builder.append(' ');
    builder.append(seinfo != null ? seinfo : "!");
    return mInstaller.execute(builder.toString());
}

怎么样?很熟悉吧,这里的Installer其实调用的就是我们平时运行android项目很熟悉的install命令,原来android系统安装apk文件底层都是调用的adb命令。

总结:

  • android系统启动之后会解析固定目录下的apk文件,并执行解析,持久化apk信息,重新安装等操作;

  • 解析Manifest流程:Zygote进程 --> SystemServer进程 --> PackgeManagerService服务 --> scanDirLI方法 --> scanPackageLI方法 --> PackageParser.parserPackage方法;

  • 解析完成Manifest之后会将apk的Manifest信息保存在Settings对象中并持久化,然后执行重新安装的操作;