最近面试聊到很多启动过程的事情:
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