近日研习古书《菜根谭》,提到“闲时吃紧,忙里偷闲”,大意为君子闲时要有吃紧的心思,忙处要有悠闲的趣味。
前阵子忙完了一个底层驱动的feature的任务,这两天稍闲,遂准备老生常谈,总结一下开机时apk的加载。
至于Android系统从开机到如何启动到PMS,不做赘述,想了解的朋友可以参看我的另外一篇博文:http://blog.csdn.net/dkbdkbdkb/article/details/51799791 关于zygote启动的,里边有SystemServer的相关启动部分。
1)开启PMS的代码来自SystemServer.java的initAndLoop方法。
public void initAndLoop() {
......
pm = PackageManagerService.main(context, installer,
factoryTest != SystemServer.FACTORY_TEST_OFF,
onlyCore);
......
}
2) 调用到了PMS的main方法,跟进。
public static final IPackageManager main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
ServiceManager.addService("package", m);
return m;
}
3) 接下来我们看new一个PMS实例做了哪些具体工作。
File vendorAppDir = new File(Environment.getRootDirectory(), "/vendor/app");
mVendorInstallObserver = new AppDirObserver(
vendorAppDir.getPath(), OBSERVER_EVENTS, true, false, false);
mVendorInstallObserver.startWatching();
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
/// M: [ALPS00098646] Mtprof tool
addBootEvent(new String("Android:PMS_scan_done:" + vendorAppDir.getPath().toString()));
核心的流程就是调用scanDirLi方法,扫描了许多系统app存放的目录,诸如system/app, data/app,vendor/app等。。。
4)scanDirLi实现:
.......
for (i=0; i<files.length; i++) {
File file = new File(dir, files[i]);
if (!isPackageFilename(files[i])) {
// Ignore entries which are not apk's
continue;
}
/** M: [ALPS00118168] Add PMS scan package time log @{ */
startScanTime = SystemClock.uptimeMillis();
Log.d(TAG,"scan package: " + file.toString() + " , start at: " + startScanTime + "ms.");
PackageParser.Package pkg = scanPackageLI(file,
flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
endScanTime = SystemClock.uptimeMillis();
Log.d(TAG,"scan package: " + file.toString() + " , end at: " + endScanTime + "ms. elapsed time = " + (endScanTime - startScanTime) + "ms.");
/** @} */
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
// Delete the apk
Slog.w(TAG, "Cleaning up failed install of " + file);
file.delete();
}
}
.........
该方法首先检测传入的Dir的路径下的file是否为apk文件,如果是的话,调用scanPackageLi继续往下走。
5)scanPackageLi
private PackageParser.Package scanPackageLI(File scanFile,
int parseFlags, int scanMode, long currentTime, UserHandle user) {
mLastScanError = PackageManager.INSTALL_SUCCEEDED;
String scanPath = scanFile.getPath();
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
parseFlags |= mDefParseFlags;
PackageParser pp = new PackageParser(scanPath);
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
final PackageParser.Package pkg = <span style="color:#ff0000;">pp.parsePackage</span>(scanFile,
scanPath, mMetrics, parseFlags);
if (pkg == null) {
mLastScanError = pp.getParseError();
return null;
}
.......
PackageParser.Package scannedPkg = <span style="color:#ff0000;">scanPackageLI</span>(pkg, parseFlags, scanMode
| 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) {
/*
* We have to grant systems permissions before we hide, because
* grantPermissions will assume the package update is trying to
* expand its permissions.
*/
grantPermissionsLPw(pkg, true);
mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
return scannedPkg;
}
红色字体的两个方法是主线。
6)我们先看第一条线:parsePackage
public Package parsePackage(File sourceFile, String destCodePath,
DisplayMetrics metrics, int flags) {
......
mArchiveSourcePath = sourceFile.getPath();
......
XmlResourceParser parser = null;
AssetManager assmgr = null;
Resources res = null;
boolean assetError = true;
try {
assmgr = new AssetManager();
int cookie = assmgr.addAssetPath(mArchiveSourcePath);
if (cookie != 0) {
res = new Resources(assmgr, metrics, null);
assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
assetError = false;
} else {
Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
}
} catch (Exception e) {
Slog.w(TAG, "Unable to read AndroidManifest.xml of "
+ mArchiveSourcePath, e);
}
......
String[] errorText = new String[1];
Package pkg = null;
Exception errorException = null;
try {
// XXXX todo: need to figure out correct configuration.
pkg = parsePackage(res, parser, flags, errorText);
} catch (Exception e) {
errorException = e;
mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
}
......
parser.close();
assmgr.close();
// Set code and resource paths
pkg.mPath = destCodePath;
pkg.mScanPath = mArchiveSourcePath;
//pkg.applicationInfo.sourceDir = destCodePath;
//pkg.applicationInfo.publicSourceDir = destRes;
pkg.mSignatures = null;
return pkg;
}
每一个Apk文件都是一个归档文件,它里面包含了Android应用程序的配置文件AndroidManifest.xml,这里主要就是要对这个配置文件就行解析了,从Apk归档文件中得到这个配置文件后,就调用另一外版本的parsePackage函数对这个应用程序进行解析了:
public class PackageParser {
......
private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
......
String pkgName = parsePackageName(parser, attrs, flags, outError);
......
final Package pkg = new Package(pkgName);
......
int type;
......
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
......
while ((type=parser.next()) != parser.END_DOCUMENT
&& (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == parser.END_TAG || type == parser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("application")) {
......
if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
} else if (tagName.equals("permission-group")) {
......
} else if (tagName.equals("permission")) {
......
} else if (tagName.equals("permission-tree")) {
......
} else if (tagName.equals("uses-permission")) {
......
} else if (tagName.equals("uses-configuration")) {
......
} else if (tagName.equals("uses-feature")) {
......
} else if (tagName.equals("uses-sdk")) {
......
} else if (tagName.equals("supports-screens")) {
......
} else if (tagName.equals("protected-broadcast")) {
......
} else if (tagName.equals("instrumentation")) {
......
} else if (tagName.equals("original-package")) {
......
} else if (tagName.equals("adopt-permissions")) {
......
} else if (tagName.equals("uses-gl-texture")) {
......
} else if (tagName.equals("compatible-screens")) {
......
} else if (tagName.equals("eat-comment")) {
......
} else if (RIGID_PARSER) {
......
} else {
......
}
}
......
return pkg;
}
......
}
这里就是对AndroidManifest.xml文件中的各个标签进行解析了,这里我们只简单看一下application标签的解析,这是通过调用parseApplication函数来进行的。
7)PackageParser.parseApplication
public class PackageParser {
......
private boolean parseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
......
int type;
while ((type=parser.next()) != parser.END_DOCUMENT
&& (type != parser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == parser.END_TAG || type == parser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false);
......
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true);
......
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, attrs, flags, outError);
......
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
......
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, attrs, flags, outError);
......
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
......
} else if (tagName.equals("uses-library")) {
......
} else if (tagName.equals("uses-package")) {
......
} else {
......
}
}
return true;
}
......
}
这里就是对AndroidManifest.xml文件中的application标签进行解析了,我们常用到的标签就有activity、service、receiver和provider,这里解析完成后,一层层返回到5)中,调用另一个版本的scanPackageLI函数把来解析后得到的应用程序信息保存下来。
8)PackageManagerService.scanPackageLI
class PackageManagerService extends IPackageManager.Stub {
......
// Keys are String (package name), values are Package. This also serves
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
final HashMap<String, PackageParser.Package> mPackages =
new HashMap<String, PackageParser.Package>();
......
// All available activities, for your resolving pleasure.
final ActivityIntentResolver mActivities =
new ActivityIntentResolver();
// All available receivers, for your resolving pleasure.
final ActivityIntentResolver mReceivers =
new ActivityIntentResolver();
// All available services, for your resolving pleasure.
final ServiceIntentResolver mServices = new ServiceIntentResolver();
// Keys are String (provider class name), values are Provider.
final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =
new HashMap<ComponentName, PackageParser.Provider>();
......
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
......
synchronized (mPackages) {
......
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
......
int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
mProvidersByComponent.put(new ComponentName(p.info.packageName,
p.info.name), p);
......
}
N = pkg.services.size();
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
......
}
N = pkg.receivers.size();
r = null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
......
}
N = pkg.activities.size();
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
......
}
......
}
......
return pkg;
}
......
}
这个函数主要就是把前面解析应用程序得到的package、provider、service、receiver和activity等信息保存在PackageManagerService服务中了。
这仅仅是相当于静默安装完成,至于呈现给用户看的app图标还需要HOME去提取信息去显示。