Android源码(4) --- 系统 Mainfest 文件解析流程

来源:互联网 发布:python中文下载 编辑:程序博客网 时间:2024/06/08 02:02

系统 Mainfest 文件解析流程

为什么要看 Mainfest 文件解析流程呢,因为解析来分析apk的安装流程需要用到,而且 AndroidMainfest 文件是程序的配置文件,了解其解析流程还是非常有必要哒~ 之前的分析中,系统会在启动时读取所有app的Mainfest以便于启动某个应用。

  • 1.入口
  • 在PackageManagerService 中,系统去解析app文件。那么从构造函数开始吧。
... 贴出关键代码 ...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");    ......       // 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);......
  • 在构造中,首先看到的是取解析这几个目录下的apk,通过scanDirLI(),在scanDirLI()中对当前目录进行遍历,并且调用scanPackageLI()逐个进行解析。
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();                    }                }            }        }    }

而scanPackageLI()最后通过parseBaseApk() 解析出apk的Resources & XmlResourceParser & Package等等相关信息。

private Package parseBaseApk(File apkFile, AssetManager assets, int flags)            throws PackageParserException {        final String apkPath = apkFile.getAbsolutePath();        String volumeUuid = null;        if (apkPath.startsWith(MNT_EXPAND)) {            final int end = apkPath.indexOf('/', MNT_EXPAND.length());            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);        }        mParseError = PackageManager.INSTALL_SUCCEEDED;        mArchiveSourcePath = apkFile.getAbsolutePath();        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);        Resources res = null;        XmlResourceParser parser = null;        try {            res = new Resources(assets, mMetrics, null);            assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                    Build.VERSION.RESOURCES_SDK_INT);            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);            final String[] outError = new String[1];            final Package pkg = parseBaseApk(res, parser, flags, outError);            if (pkg == null) {                throw new PackageParserException(mParseError,                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);            }            pkg.volumeUuid = volumeUuid;            pkg.applicationInfo.volumeUuid = volumeUuid;            pkg.baseCodePath = apkPath;            pkg.mSignatures = null;            return pkg;        } catch (PackageParserException e) {            throw e;        } catch (Exception e) {            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,                    "Failed to read manifest from " + apkPath, e);        } finally {            IoUtils.closeQuietly(parser);        }    }

而到了这里,重点关注一下Package信息的解析过程,其是通过重载函数parseBaseApk()进行解析, 继续看一下(代码太多,贴一下重要部分):

private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,            String[] outError) throws XmlPullParserException, IOException {        ......        final Package pkg = new Package(pkgName);        boolean foundApp = false;        ......        int outerDepth = parser.getDepth();        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")) {                ......                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")) {                    .......                    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")) {                ......                }                XmlUtils.skipCurrentTag(parser);            } else if (tagName.equals("supports-screens")) {                ......                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;            }        }        ......        final int NS = PackageParser.SPLIT_PERMISSIONS.length;        for (int is=0; is<NS; is++) {            final PackageParser.SplitPermissionInfo spi                    = PackageParser.SPLIT_PERMISSIONS[is];            if (pkg.applicationInfo.targetSdkVersion >= spi.targetSdk                    || !pkg.requestedPermissions.contains(spi.rootPerm)) {                continue;            }            for (int in=0; in<spi.newPerms.length; in++) {                final String perm = spi.newPerms[in];                if (!pkg.requestedPermissions.contains(perm)) {                    pkg.requestedPermissions.add(perm);                }            }        }       ......        return pkg;    }
  • 可以看到,这里主要根据名称解析xml文件的节点文件,通过parseBaseApplication()解析 application 结点下的所有activity,service,broadcast,contentprovier等组件信息,最后将信息add进Package类中,返回scanPackageLI后,在通过Settings进行持久存储。接下来查看如何持久化保存的:

在Package信息返回返回scanPackageLI后,在后面调用scanPackageLI();

PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags                | SCAN_UPDATE_SIGNATURE, currentTime, user);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代码:

private PackageParser.Package scanPackageDirtyLI(    ...... int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,                       pkg.applicationInfo.seinfo);    ......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;    }
  • 可以看到读取Mainfest信息后,通过Installer安装类进行应用安装,如何安装呢,继续查看createUserData();
public int createUserData(String uuid, String name, int uid, int userId, String seinfo) {        StringBuilder builder = new StringBuilder("mkuserdata");        builder.append(' ');        builder.append(escapeNull(uuid));        builder.append(' ');        builder.append(name);        builder.append(' ');        builder.append(uid);        builder.append(' ');        builder.append(userId);        builder.append(' ');        builder.append(seinfo != null ? seinfo : "!");        return mInstaller.execute(builder.toString());    }
  • 这里就是通过拼接命令,而实际是由Installer执行的adb命令。

总结

  • Android系统启动时会去指定目录读取Apk的Mainfest信息,随后通过Settings对Package信息进行持久化存储,并重新安装apk
原创粉丝点击