为了账号安全,请及时绑定邮箱和手机立即绑定

android系统什么时候解析mainfest(很枯燥乏味儿)

标签:
Android

最近面试聊到很多启动过程的事情:

Android系统的启动过程中就已经解析了系统中安装应用的androidManifest.xml文件并保存起来了?

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

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

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

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

...

}

**Android系统的启动过程中就解析系统中安装应用的androidManifest.xml文件并保存起来了?**

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

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

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

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.main方法中,解析完刚刚的几个系统目录之后系统会调用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] = " has more than one ";

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

} else {

Slog.w(TAG, " has more than one ");

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] = " does not specify a target package";

mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;

return null;

}

if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {

outError[0] = " 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 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  declared are mandatory.

featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;

features = ArrayUtils.add(features, featureInfo);

} else {

Slog.w(TAG, "Unknown element under : " + 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;

}



作者:Kinzirva
链接:https://www.jianshu.com/p/a0f5b53a63f7

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消