Android源码解析之(⼗⼆)--系统启动并解析Manifest的流程转载请标明出处:
最近有同学问我关于Manifest何时被系统解析的问题,正好也分析到这⼀块了,索性这⼀章就讲解⼀下android系统何时解析Manifest 吧,这⾥的Manifest指的是android安装⽂件apk中的l⽂件是何时被解析的。
⼤家应该都知道,Android系统启动之后,我们就可以在⼀个应⽤中打开另⼀个从未打开过的应⽤,或者是在⼀个应⽤中发送⼴播,如果另外⼀个应⽤设置了这个⼴播的接收器,那么这个应⽤进程就会被启动并接收该⼴播并作出相应的处理,这样的例⼦很多,我们可以猜测到Android系统在启动的时候就会抓取到了系统中所有安装的应⽤信息(应该是解析apk⽂件的Manifest信息),即在Android系统的启动过程中就已经解析了系统中安装应⽤的l⽂件并保存起来了,那么这个过程具体是如何的呢?
其实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 = PackageManager();
...
}
在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 = DataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAcInternalPath = new File(dataDir, "app-ac").getPath();
mUrAppDataDir = new File(dataDir, "ur");
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, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find ba frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR
| PackageParr.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new RootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR
| PackageParr.PARSE_IS_PRIVILEGED, scanFlags, 0);
/
exodus/ Collect ordinary system packages.
final File systemAppDir = new RootDirectory(), "app");
scanDirLI(systemAppDir, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = CanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new OemDirectory(), "app");
scanDirLI(oemAppDir, PackageParr.PARSE_IS_SYSTEM
| PackageParr.PARSE_IS_SYSTEM_DIR, scanFlags, 0);中国梦英语作文
在我们刚刚的PackageManagerService.mani⽅法中,解析完刚刚的⼏个系统⽬录之后系统会调⽤scanDirLI⽅法,那么这个⽅法主要是做什么⽤的呢?看它的名字应该是遍历这个系统⽬录。好吧,这个⽅法主要就是⽤于解析上⾯⼏个⽬录下的apk⽂件的。不信?我们看⼀下scanDirLI⽅法的具体实现:
private void scanDirLI(File dir, int parFlags, 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" + HexString(parFlags));
}
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.Name());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
try {
scanPackageLI(file, parFlags | PackageParr.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to par " + file + ": " + e.getMessage());
// Delete invalid urdata apps
if ((parFlags & PackageParr.PARSE_IS_SYSTEM) == 0 &&
< == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
if (file.isDirectory()) {
} el {
file.delete();
}
}
}
}
}
可以放下其⾸先会遍历该⽬录下的所有⽂件,并判断是否是apk⽂件,如果是apk⽂件则调⽤scanPackageLI⽅法,scanPackageLI⽅法的名字很明显,就是⽤于解析这个apk⽂件的。
继续看⼀下scanPakcageLI⽅法的实现:
private PackageParr.Package scanPackageLI(File scanFile, int parFlags, int scanFlags,
long currentTime, UrHandle ur) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
parFlags |= mDefParFlags;
PackageParr pp = new PackageParr();
pp.tSeparateProcess(mSeparateProcess);
pp.tOnlyCoreApps(mOnlyCore);
pp.tDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parFlags |= PackageParr.PARSE_TRUSTED_OVERLAY;
}
final PackageParr.Package pkg;
try {
pkg = pp.parPackage(scanFile, parFlags);
} catch (PackageParrException e) {
throw PackageManagerException.from(e);
}
...
}
好吧,这个⽅法也⽐较复杂,这⾥只是列出重点相关的代码,我们可以发现在这个⽅法中创建了⼀个
PackagerParr对象,并调⽤了parPackage⽅法,这个⽅法其实就是解析Manifest的主要⽅法,我们可以看⼀下其具体的实现:
public Package parPackage(File packageFile, int flags) throws PackageParrException {
if (packageFile.isDirectory()) {
return parClusterPackage(packageFile, flags);
} el {
return parMonolithicPackage(packageFile, flags);
}
}
可以发现,若我们解析的File对象是⼀个⽂件夹则执⾏调⽤parClusterPackage⽅法,否则调⽤执⾏parMonolithicPackage⽅法,很明显的因为我们这⾥解析的是apk⽂件(在上⼀⽅法中我们循环遍历得到了apk⽂件,这⾥的File对象就代表了⼀个个的apk⽂件信息),所以这⾥会执⾏parMonolithicPackage⽅法,然后我们来看⼀下parMonolithicPackage⽅法:
public Package parMonolithicPackage(File apkFile, int flags) throws PackageParrException {
if (mOnlyCoreApps) {
final PackageLite lite = parMonolithicPackageLite(apkFile, flags);
if (!App) {
throw new PackageParrException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AstManager asts = new AstManager();
try {
final Package pkg = parBaApk(apkFile, asts, flags);
return pkg;
} finally {
IoUtils.cloQuietly(asts);queen bee
}
}
可以看出,这⾥⼜调⽤了parBaApk⽅法:
private Package parBaApk(File apkFile, AstManager asts, int flags)
共和党候选人
...
final Package pkg = parBaApk(res, parr, flags, outError);
...
}
可以看出,这个parBaApk⽅法调⽤了其重载的parBaApk⽅法:
while ((type = ()) != XmlPullParr.END_DOCUMENT
&& (type != XmlPullParr.END_TAG || Depth() > outerDepth)) {
if (type == XmlPullParr.END_TAG || type == XmlPullParr.TEXT) {
continue;
}
String tagName = Name();
if (tagName.equals("application")) {
if (foundApp) {
if (RIGID_PARSER) {
outError[0] = "<manifest> has more than one <application>";
mParError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
} el {
Slog.w(TAG, "<manifest> has more than one <application>");
XmlUtils.skipCurrentTag(parr);
continue;
}
}
foundApp = true;
if (!parBaApplication(pkg, res, parr, attrs, flags, outError)) {
return null;
}
} el 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);
monica什么意思
colour怎么读
if (pkg.mOverlayTarget == null) {
outError[0] = "<overlay> does not specify a target package";
mParError = 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";
mParError =
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
XmlUtils.skipCurrentTag(parr);
} el if (tagName.equals("key-ts")) {
if (!parKeySets(pkg, res, parr, attrs, outError)) {
return null;
}
} el if (tagName.equals("permission-group")) {
if (parPermissionGroup(pkg, flags, res, parr, attrs, outError) == null) {
return null;