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
阅读全文
0 0
- Android源码(4) --- 系统 Mainfest 文件解析流程
- android之mainfest解析
- android mainfest.xml文件解剖
- android mainfest.xml文件解剖
- Mainfest.xml文件的初步解析
- mainfest 解析
- Android mainfest文件 android属性 汇总
- Android mainfest文件 android属性 汇总
- Android mainfest文件中android属性
- Android mainfest文件 android属性 汇总
- Android mainfest文件中android属性
- Android之mainfest文件中android属性
- Android mainfest文件 android属性 汇总
- mainfest文件
- android mainfest
- Android学习笔记之mainfest文件中android属性
- Android学习笔记之mainfest文件中android属性
- Android学习笔记之mainfest文件中android属性
- USACO-Section1.4 milk3[深搜]
- Android源码(3) --- 系统Launcher 启动流程
- webpack+vue.js+elementUI试做后台管理页面
- 阿里云大数据利器Maxcompute学习之-假如你使用过hive
- 关于LayoutInflater.inflate()的参数问题
- Android源码(4) --- 系统 Mainfest 文件解析流程
- 浅谈C语言中变量、常量、数组、字符串、指针的地址
- Fibre Channel光纤通道系统基础(二)
- localStorage实现购物车
- Echarts结合Ajax的动态图表(1)
- 566. Reshape the Matrix
- 无线通信:保证传输质量的三种技术
- Python lambda介绍
- VC6 MFC如何打开用户在编辑框内输入的文件?