应用程序扫描安装过程分析

本文详细介绍了Android系统在扫描和解析apk安装包时的内部步骤,包括扫描目录、解析apk内容、更新安装包信息等关键过程。通过对`PackageManagerService.java`中的相关函数分析,揭示了系统如何检查、验证和处理apk文件,以确保安装的正确性和安全性。
摘要由CSDN通过智能技术生成

应用程序扫描过程:
【转自】:http://blog.csdn.net/jwq2011/article/details/52369415
apk安装过程:
【转自】:http://blog.csdn.net/qq_23547831/article/details/51210682

  1. 扫描安装apk文件夹目录:

/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

private void scanDirLI(File dir, final 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());
		//如果不是apk,跳过继续扫描
        if (!isPackage) {
            // Ignore entries which are not packages
            continue;
        }
        try {
			//解析扫描到的apk
            scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                    scanFlags, currentTime, null);
        } catch (PackageManagerException e) {
            Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

			//如果是错误的apk,删除
            // 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);
                removeCodePathLI(file);
            }
        }
    }
}


2. 解析扫描到的apk:

private PackageParser.Package scanPackageTracedLI(PackageParser.Package pkg,
        final int policyFlags, int scanFlags, long currentTime, UserHandle user)
                throws PackageManagerException {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
    // If the package has children and this is the first dive in the function
    // we recursively scan the package with the SCAN_CHECK_ONLY flag set to see
    // whether all packages (parent and children) would be successfully scanned
    // before the actual scan since scanning mutates internal state and we want
    // to atomically install the package and its children.
    if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
        if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
            scanFlags |= SCAN_CHECK_ONLY;
        }
    } else {
        scanFlags &= ~SCAN_CHECK_ONLY;
    }

    final PackageParser.Package scannedPkg;
    try {
        // Scan the parent
        scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags, currentTime, user);
        // Scan the children
        final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
        for (int i = 0; i < childCount; i++) {
            PackageParser.Package childPkg = pkg.childPackages.get(i);
            scanPackageLI(childPkg, policyFlags,
                    scanFlags, currentTime, user);
        }
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
        return scanPackageTracedLI(pkg, policyFlags, scanFlags, currentTime, user);
    }

    return scannedPkg;
}

3. 解析每个apk:解析apk中的AndroidManifest.xml,并更新安装包信息。

private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
        long currentTime, UserHandle user) throws PackageManagerException {
    if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
    //使用PackageParser解析apk安装包
	PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    pp.setDisplayMetrics(mMetrics);

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

    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
		//解析当前扫描的apk,得到apk信息对象
        pkg = pp.parsePackage(scanFile, parseFlags);
    } catch (PackageParserException e) {
        throw PackageManagerException.from(e);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}

 private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
        final int policyFlags, int scanFlags, long currentTime, UserHandle user)
        throws PackageManagerException {
    // If the package has children and this is the first dive in the function
    // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
    // packages (parent and children) would be successfully scanned before the
    // actual scan since scanning mutates internal state and we want to atomically
    // install the package and its children.
    if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
        if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
            scanFlags |= SCAN_CHECK_ONLY;
        }
    } else {
        scanFlags &= ~SCAN_CHECK_ONLY;
    }

    // Scan the parent
    PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,
            scanFlags, currentTime, user);

    // Scan the children
    final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
    for (int i = 0; i < childCount; i++) {
        PackageParser.Package childPackage = pkg.childPackages.get(i);
        scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
                currentTime, user);
    }


    if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
        return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);
    }

    return scannedPkg;
}
  1. 更新apk信息:

private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
int policyFlags, int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
PackageSetting ps = null;
//更新系统中的安装包信息
PackageSetting updatedPkg;
// reader
synchronized (mPackages) {
// Look to see if we already know about this package.
//mSettings.mRenamedPackages中保存了所有安装包的名字变更,以键值对的形式保存安装包新的包名和原始包名
String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
// This package has been renamed to its original name. Let’s
// use that.
//通过旧包名从mSettings中取出该apk原始的PackageSetting对象
ps = mSettings.peekPackageLPr(oldName);
}
// If there was no original package, see one for the real package name.
如果没有原始包设置PackageSetting,则使用apk当前包名来获取对应PackageSetting
if (ps == null) {
ps = mSettings.peekPackageLPr(pkg.packageName);
}
// Check to see if this package could be hiding/updating a system
// package. Must look for it either under the original or real
// package name depending on our state.
//mSettings.getDisabledSystemPkgLPr保存了所有已替换的安装包的设置信息,以键值对形式保存每个apk包的
//名和设置:name-PackageSetting
updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);
if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);

        // If this is a package we don't know about on the system partition, we
        // may need to remove disabled child packages on the system partition
        // or may need to not add child packages if the parent apk is updated
        // on the data partition and no longer defines this child package.
        if ((policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
            // If this is a parent package for an updated system app and this system
            // app got an OTA update which no longer defines some of the child packages
            // we have to prune them from the disabled system packages.
            PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(pkg.packageName);
            if (disabledPs != null) {
                final int scannedChildCount = (pkg.childPackages != null)
                        ? pkg.childPackages.size() : 0;
                final int disabledChildCount = disabledPs.childPackageNames != null
                        ? disabledPs.childPackageNames.size() : 0;
                for (int i = 0; i < disabledChildCount; i++) {
                    String disabledChildPackageName = disabledPs.childPackageNames.get(i);
                    boolean disabledPackageAvailable = false;
                    for (int j = 0; j < scannedChildCount; j++) {
                        PackageParser.Package childPkg = pkg.childPackages.get(j);
                        if (childPkg.packageName.equals(disabledChildPackageName)) {
                            disabledPackageAvailable = true;
                            break;
                        }
                     }
                     if (!disabledPackageAvailable) {
                         mSettings.removeDisabledSystemPackageLPw(disabledChildPackageName);
                     }
                }
            }
        }
    }

    boolean updatedPkgBetter = false;
	//如果是系统安装包
    // First check if this is a system package that may involve an update
    if (updatedPkg != null && (policyFlags & PackageParser.PARSE_IS_SYSTEM) != 0) {
        // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
        // it needs to drop FLAG_PRIVILEGED.
        if (locationIsPrivileged(scanFile)) {
            updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
        } else {
            updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
        }

		//如果apk路径已更改
        if (ps != null && !ps.codePath.equals(scanFile)) {
            // The path has changed from what was last scanned...  check the
            // version of the new path against what we have stored to determine
            // what to do.
            if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
            //判断当前apk版本号是否小于原始版本
			if (pkg.mVersionCode <= ps.versionCode) {
                // The system package has been updated and the code path does not match
                // Ignore entry. Skip it.
                if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
                        + " ignored: updated version " + ps.versionCode
                        + " better than this " + pkg.mVersionCode);
                if (!updatedPkg.codePath.equals(scanFile)) {
                    Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg "
                            + ps.name + " changing from " + updatedPkg.codePathString
                            + " to " + scanFile);
                    updatedPkg.codePath = scanFile;
                    updatedPkg.codePathString = scanFile.toString();
                    updatedPkg.resourcePath = scanFile;
                    updatedPkg.resourcePathString = scanFile.toString();
                }
                updatedPkg.pkg = pkg;
                updatedPkg.versionCode = pkg.mVersionCode;

                // Update the disabled system child packages to point to the package too.
                final int childCount = updatedPkg.childPackageNames != null
                        ? updatedPkg.childPackageNames.size() : 0;
                for (int i = 0; i < childCount; i++) {
                    String childPackageName = updatedPkg.childPackageNames.get(i);
                    PackageSetting updatedChildPkg = mSettings.getDisabledSystemPkgLPr(
                            childPackageName);
                    if (updatedChildPkg != null) {
                        updatedChildPkg.pkg = pkg;
                        updatedChildPkg.versionCode = pkg.mVersionCode;
                    }
                }

                throw new PackageManagerException(Log.WARN, "Package " + ps.name + " at "
                        + scanFile + " ignored: updated version " + ps.versionCode
                        + " better than this " + pkg.mVersionCode);
            } else {
                // The current app on the system partition is better than
                // what we have updated to on the data partition; switch
                // back to the system partition version.
                // At this point, its safely assumed that package installation for
                // apps in system partition will go through. If not there won't be a working
                // version of the app
                // writer
                synchronized (mPackages) {
                    // Just remove the loaded entries from package lists.
                    mPackages.remove(ps.name);
                }

                logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                        + " reverting from " + ps.codePathString
                        + ": new version " + pkg.mVersionCode
                        + " better than installed " + ps.versionCode);

                InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                        ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                synchronized (mInstallLock) {
                    args.cleanUpResourcesLI();
                }
                synchronized (mPackages) {
                    mSettings.enableSystemPackageLPw(ps.name);
                }
                updatedPkgBetter = true;
            }
        }
    }

    if (updatedPkg != null) {
        // An updated system app will not have the PARSE_IS_SYSTEM flag set
        // initially
        policyFlags |= PackageParser.PARSE_IS_SYSTEM;

        // An updated privileged app will not have the PARSE_IS_PRIVILEGED
        // flag set initially
        if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
            policyFlags |= PackageParser.PARSE_IS_PRIVILEGED;
        }
    }

    // Verify certificates against what was last scanned
    //安装包校验
	collectCertificatesLI(ps, pkg, scanFile, policyFlags);

    /*
     * A new system app appeared, but we already had a non-system one of the
     * same name installed earlier.
     */
	//先前安装过相同包名的apk,但现在作为系统apk来安装  
    boolean shouldHideSystemApp = false;
    if (updatedPkg == null && ps != null
            && (policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {
        /*
         * Check to make sure the signatures match first. If they don't,
         * wipe the installed application and its data.
         */
		 //apk签名匹配,如果失败,清空apk文件及其数据 
        if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)
                != PackageManager.SIGNATURE_MATCH) {
            logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
                    + " signatures don't match existing userdata copy; removing");
            try (PackageFreezer freezer = freezePackage(pkg.packageName,
                    "scanPackageInternalLI")) {
                deletePackageLIF(pkg.packageName, null, true, null, 0, null, false, null);
            }
            ps = null;
        } else {
			//签名成功
			//如果新增的系统apk版本低于先前安装的apk版本 
            /*
             * If the newly-added system app is an older version than the
             * already installed version, hide it. It will be scanned later
             * and re-added like an update.
             */
            if (pkg.mVersionCode <= ps.versionCode) {
                shouldHideSystemApp = true;
                logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
                        + " but new version " + pkg.mVersionCode + " better than installed "
                        + ps.versionCode + "; hiding system");
            } else {
				//更新系统apk程序,但保存应用数据不变
                /*
                 * The newly found system app is a newer version that the
                 * one previously installed. Simply remove the
                 * already-installed application and replace it with our own
                 * while keeping the application data.
                 */
                logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                        + " reverting from " + ps.codePathString + ": new version "
                        + pkg.mVersionCode + " better than installed " + ps.versionCode);
                InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                        ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                synchronized (mInstallLock) {
                    args.cleanUpResourcesLI();
                }
            }
        }
    }

    // The apk is forward locked (not public) if its code and resources
    // are kept in different files. (except for app in either system or
    // vendor path).
    // TODO grab this value from PackageSettings
    if ((policyFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
        if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
            policyFlags |= PackageParser.PARSE_FORWARD_LOCK;
        }
    }

    // TODO: extend to support forward-locked splits
    String resourcePath = null;
    String baseResourcePath = null;
    if ((policyFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
        if (ps != null && ps.resourcePathString != null) {
            resourcePath = ps.resourcePathString;
            baseResourcePath = ps.resourcePathString;
        } else {
            // Should not happen at all. Just log an error.
            Slog.e(TAG, "Resource path not set for package " + pkg.packageName);
        }
    } else {
        resourcePath = pkg.codePath;
        baseResourcePath = pkg.baseCodePath;
    }

    // Set application objects path explicitly.
    pkg.setApplicationVolumeUuid(pkg.volumeUuid);
    pkg.setApplicationInfoCodePath(pkg.codePath);
    pkg.setApplicationInfoBaseCodePath(pkg.baseCodePath);
    pkg.setApplicationInfoSplitCodePaths(pkg.splitCodePaths);
    pkg.setApplicationInfoResourcePath(resourcePath);
    pkg.setApplicationInfoBaseResourcePath(baseResourcePath);
    pkg.setApplicationInfoSplitResourcePaths(pkg.splitCodePaths);

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

    /*
     * If the system app should be overridden by a previously installed
     * data, hide the system app now and let the /data/app scan pick it up
     * again.
     */
    if (shouldHideSystemApp) {
        synchronized (mPackages) {
            mSettings.disableSystemPackageLPw(pkg.packageName, true);
        }
    }

    return scannedPkg;
}

调用这个

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

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

    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
        pkg = pp.parsePackage(scanFile, parseFlags);
    } catch (PackageParserException e) {
        throw PackageManagerException.from(e);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
  1. 解析apk: 从Manifest.xml中解析

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

private Package parseClusterPackage(File packageDir, int flags) throws PackageParserException {
final PackageLite lite = parseClusterPackageLite(packageDir, 0);

    if (mOnlyCoreApps && !lite.coreApp) {
        throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                "Not a coreApp: " + packageDir);
    }

	//创建一个资源管理器对象
    final AssetManager assets = new AssetManager();
    try {
        // Load the base and all splits into the AssetManager
        // so that resources can be overriden when parsing the manifests.
        loadApkIntoAssetManager(assets, lite.baseCodePath, flags);

        if (!ArrayUtils.isEmpty(lite.splitCodePaths)) {
            for (String path : lite.splitCodePaths) {
                loadApkIntoAssetManager(assets, path, flags);
            }
        }

        final File baseApk = new File(lite.baseCodePath);
        final Package pkg = parseBaseApk(baseApk, assets, flags);
        if (pkg == null) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                    "Failed to parse base APK: " + baseApk);
        }

        if (!ArrayUtils.isEmpty(lite.splitNames)) {
            final int num = lite.splitNames.length;
            pkg.splitNames = lite.splitNames;
            pkg.splitCodePaths = lite.splitCodePaths;
            pkg.splitRevisionCodes = lite.splitRevisionCodes;
            pkg.splitFlags = new int[num];
            pkg.splitPrivateFlags = new int[num];

            for (int i = 0; i < num; i++) {
                parseSplitApk(pkg, i, assets, flags);
            }
        }

        pkg.setCodePath(packageDir.getAbsolutePath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } finally {
        IoUtils.closeQuietly(assets);
    }
}

打开xml解析文件:

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];
		//解析AndroidManifest.xml文件,得到Package对象  
        final Package pkg = parseBaseApk(res, parser, flags, outError);
        if (pkg == null) {
            throw new PackageParserException(mParseError,
                    apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
        }

        pkg.setVolumeUuid(volumeUuid);
        pkg.setApplicationVolumeUuid(volumeUuid);
        pkg.setBaseCodePath(apkPath);
        pkg.setSignatures(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);
    }
}

开始解析文件:

private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    final String splitName;
    final String pkgName;

    try {
        Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
        pkgName = packageSplit.first;
        splitName = packageSplit.second;

        if (!TextUtils.isEmpty(splitName)) {
            outError[0] = "Expected base APK, but found split " + splitName;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }
    } catch (PackageParserException e) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
        return null;
    }

    final Package pkg = new Package(pkgName);

    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);

    pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    pkg.baseRevisionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
    pkg.mVersionName = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
        pkg.mVersionName = pkg.mVersionName.intern();
    }

    pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

    sa.recycle();

    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}

frameworks/base/core/res/res/values/attrs_manifest.xml文件已经定义。下表为各个标签对应的解析函数:
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值