写了一个通过PackageManager获取手机上所有已安装app的信息Demo,欢迎下载收藏!
http://download.csdn.net/download/m0_37094131/10033582
通过上一篇博客中,我们可以了解到系统启动之后会注册各种系统服务,其中有一个就是PackageManagerService(简称PMS)。PMS启动后,会扫描系统中已安装的apk目录。
- 系统App的安装目录为 /system/app
- 第三方应用的目录为 /data/app
PMS的作用:
PMS会解析apk包下的AndroidManifest.xml文件得到App的相关信息,而每一个AndroidManifest.xml文件下的< application>节点下又包含了Activity、Service等组件的注册信息,当PMS扫描并且解析完这些信息后就构建了整个apk的信息树。
好了,大致讲啦一些PMS的作用,那么现在就开始来分析一下,该类的源代码吧,分析一下,它到底是如何完成它的工作的(解析已安装apk信息)!
第一步:先看看该类(PackageManagerService)的构造函数
(这里先说一句,我之前是想在AndroidStudio中看该类的源代码的,无奈的是,看到的是红色的字,ctrl+鼠标左键点击不进去,所以后来只能通过导出sdk中的源代码目录,放到Source Insight 软件中看了)
开始分析:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
return m;
}
分析该类中的main函数,得知是去调用构造方法!
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
mDefParseFlags = 0;
mSeparateProcesses = separateProcesses.split(",");
Slog.w(TAG, "Running with debug.separate_processes: "
+ separateProcesses);
}
} else {
mDefParseFlags = 0;
mSeparateProcesses = null;
}
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
其实该类的构造方法中的代码数量过多,所以我只选取部分代码进行发布,读者有兴趣可以自行去看看完整的源码。
File frameworkDir = new File(Environment.getRootDirectory(), "framework");
final VersionInfo ver = mSettings.getInternalVersion();
mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
这段代码中第一行写的是,加载FrameWork资源。
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirTracedLI(privilegedAppDir, mDefParseFlags
| 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");
scanDirTracedLI(systemAppDir, mDefParseFlags
| 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
}
scanDirTracedLI(vendorAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirTracedLI(oemAppDir, mDefParseFlags
| PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
该段代码写的是去扫描不同文件夹下的目录,去scan其中的apk文件!,以上都是和系统核心应用有关的!
// Remove any shared userIDs that have no associated packages
mSettings.pruneSharedUsersLPw();
//若不是系统核心应用
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
//去跟踪扫描第三方app安装的目录
scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
| PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
scanDirLI(mEphemeralInstallDir, mDefParseFlags
| PackageParser.PARSE_IS_EPHEMERAL,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
/**
* Remove disable package settings for any updated system
* apps that were removed via an OTA. If they're not a
* previously-updated app, remove them completely.
* Otherwise, just revoke their system-level permissions.
*/
该段代码中就写到了去扫描第三方app安装的目录文件夹。其实细心的读者可能都已经发现了,不管是系统核心应用文件夹,还是第三方app的文件夹,都是通过scanDirTracedLI()该函数去扫描的,现在进入到该方法中,去查看该方法的逻辑!
private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir");
try {
scanDirLI(dir, parseFlags, scanFlags, currentTime);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
其实该方法内部调用的是scanDirLI这个方法,那么在去进这个方法中查看
private void scanDirLI(File dir, final 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 {
scanPackageTracedLI(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);
removeCodePathLI(file);
}
}
}
}
该方法中,会去判断该文件夹目录下,是否存在Apk文件,若不存在,则继续寻找,若存在Apk文件,则调用scanPackageTracedLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
该函数去扫描Apk文件中的Package。那么去看下该方法。
/**
* Traces a package scan.
* @see #scanPackageLI(File, int, int, long, UserHandle)
*/
private PackageParser.Package scanPackageTracedLI(File scanFile, final int parseFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage");
try {
return scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
该方法和之前的scanDirTracedLI方法类似,该方法内部实际上调用的是scanPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);该方法。
/**
* Scans a package and returns the newly parsed package.
* Returns {@code null} in case of errors and the error code is stored in mLastScanError
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
//创建一个PackageParser解析包的对象
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final PackageParser.Package pkg;
try {
//调用解析包的方法
pkg = pp.parsePackage(scanFile, parseFlags);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}
该方法就是真正的去扫描Package包了,每个App都有一个唯一的包名(Package)。它的代码中有解析包的代码逻辑,它并不在PMS该类中,而是在PackageParser类中,一个专门解析包的类中!现在去看一下pp.parsePackage(scanFile, parseFlags);该方法的逻辑!
/**
* Parse the package at the given location. Automatically detects if the
* package is a monolithic style (single APK file) or cluster style
* (directory of APKs).
* <p>
* This performs sanity checking on cluster style packages, such as
* requiring identical package name and version codes, a single base APK,
* and unique split names.
* <p>
* Note that this <em>does not</em> perform signature verification; that
* must be done separately in {@link #collectCertificates(Package, int)}.
*
* @see #parsePackageLite(File, int)
*/
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile, flags);
} else {
return parseMonolithicPackage(packageFile, flags);
}
}
在这个方法中,它有分类,若该packageFile是一个目录,则调用parseClusterPackage方法,若该packageFile是一个单独的apk文件,则直接调用parseMonolithicPackage这个方法!由于我们分析的是单个apk,所以就去这个方法去看一下!
* {@link #parsePackage(File, int)}. Eventually this method will
* be marked private.
*/
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
if (mOnlyCoreApps) {
if (!lite.coreApp) {
throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"Not a coreApp: " + apkFile);
}
}
final AssetManager assets = new AssetManager();
try {
//解析apk
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.setCodePath(apkFile.getAbsolutePath());
pkg.setUse32bitAbi(lite.use32bitAbi);
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
我们可以看到在该方法中,它其实调用了parseBaseApk()这个方法,然后返回给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.setVolumeUuid(volumeUuid);
pkg.setApplicationVolumeUuid(volumeUuid);
pkg.setBaseCodePath(apkPath);
pkg.setSignatures(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);
}
}
重点看这一行代码:parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
/** File name in an APK for the Android manifest. */
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
这里我们发现了,解析apk文件,首先会去解析AndroidManifest.xml文件,用AssetManager的assets对象去加载xml文件资源!
得到这个parser之后,又会去调用 final Package pkg = parseBaseApk(res, parser, flags, outError);这是一个重载方法,之前的没有parser这个参数。现在就去看一下该重载方法。
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
final String splitName;
final String pkgName;
try {
Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
pkgName = packageSplit.first;
splitName = packageSplit.second;
if (!TextUtils.isEmpty(splitName)) {
outError[0] = "Expected base APK, but found split " + splitName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
} catch (PackageParserException e) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
return null;
}
final Package pkg = new Package(pkgName);
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null) {
pkg.mVersionName = pkg.mVersionName.intern();
}
pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}
该方法中其实就已经有一些大家可能熟悉的东西了,比如
pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
没错这里的mVersionCode和mVersionName就是每个app都有的版本号,以及版本名称。然后在该方法的末尾又写了 return parseBaseApkCommon(pkg, null, res, parser, flags, outError);这行代码,现在去看下这个方法!
private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
IOException {
mParseInstrumentationArgs = null;
mParseActivityArgs = null;
mParseServiceArgs = null;
mParseProviderArgs = null;
int type;
boolean foundApp = false;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifest);
String str = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0);
if (str != null && str.length() > 0) {
String nameError = validateName(str, true, false);
if (nameError != null && !"android".equals(pkg.packageName)) {
outError[0] = "<manifest> specifies bad sharedUserId name \""
+ str + "\": " + nameError;
mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID;
return null;
}
pkg.mSharedUserId = str.intern();
pkg.mSharedUserLabel = sa.getResourceId(
com.android.internal.R.styleable.AndroidManifest_sharedUserLabel, 0);
}
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation = pkg.installLocation;
从这个方法名中可以得知这是一个解析apk中都共用的一些逻辑代码。这里我想跟读者说一声,有些时候看源码会略显枯燥,但大部分是因为看不懂代码的逻辑,就会感觉到枯燥,其实,有些时候看不懂代码逻辑,可以去看看方法名称的含义,一般每个方法名称,以及每个类的名称,都会有其中特定的含义,都会帮助你更好的理解源代码的内涵!由于该方法的代码数过多,所以我就挑一些主要的源码来复制一下!
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 (acceptedTags != null && !acceptedTags.contains(tagName)) {
Slog.w(TAG, "Skipping unsupported element under <manifest>: "
+ tagName + " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//节点名 --application
if (tagName.equals(TAG_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, flags, outError)) {
return null;
}
} else if (tagName.equals(TAG_OVERLAY)) {
sa = res.obtainAttributes(parser,
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(TAG_KEY_SETS)) {
if (!parseKeySets(pkg, res, parser, outError)) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_GROUP)) {
if (parsePermissionGroup(pkg, flags, res, parser, outError) == null) {
return null;
}
//标签名 permission
} else if (tagName.equals(TAG_PERMISSION)) {
if (parsePermission(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_PERMISSION_TREE)) {
if (parsePermissionTree(pkg, res, parser, outError) == null) {
return null;
}
//标签名 uses-permisson
} else if (tagName.equals(TAG_USES_PERMISSION)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION_SDK_M)
|| tagName.equals(TAG_USES_PERMISSION_SDK_23)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
} else if (tagName.equals(TAG_USES_CONFIGURATION)) {
ConfigurationInfo cPref = new ConfigurationInfo();
sa = res.obtainAttributes(parser,
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(TAG_USES_FEATURE)) {
FeatureInfo fi = parseUsesFeature(res, parser);
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(TAG_FEATURE_GROUP)) {
FeatureGroupInfo group = new FeatureGroupInfo();
ArrayList<FeatureInfo> 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, parser);
// FeatureGroups are stricter and mandate that
// any <uses-feature> declared are mandatory.
featureInfo.flags |= FeatureInfo.FLAG_REQUIRED;
features = ArrayUtils.add(features, featureInfo);
} else {
Slog.w(TAG, "Unknown element under <feature-group>: " + 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(TAG_USES_SDK)) {
这里面其实就是一个xml解析标签的过程,而从其中的if ,else if判断的字符串,我们可以得知,
private static final String TAG_MANIFEST = "manifest";
private static final String TAG_APPLICATION = "application";
private static final String TAG_OVERLAY = "overlay";
private static final String TAG_KEY_SETS = "key-sets";
private static final String TAG_PERMISSION_GROUP = "permission-group";
private static final String TAG_PERMISSION = "permission";
private static final String TAG_PERMISSION_TREE = "permission-tree";
private static final String TAG_USES_PERMISSION = "uses-permission";
private static final String TAG_USES_PERMISSION_SDK_M = "uses-permission-sdk-m";
private static final String TAG_USES_PERMISSION_SDK_23 = "uses-permission-sdk-23";
private static final String TAG_USES_CONFIGURATION = "uses-configuration";
private static final String TAG_USES_FEATURE = "uses-feature";
private static final String TAG_FEATURE_GROUP = "feature-group";
private static final String TAG_USES_SDK = "uses-sdk";
private static final String TAG_SUPPORT_SCREENS = "supports-screens";
private static final String TAG_PROTECTED_BROADCAST = "protected-broadcast";
private static final String TAG_INSTRUMENTATION = "instrumentation";
private static final String TAG_ORIGINAL_PACKAGE = "original-package";
private static final String TAG_ADOPT_PERMISSIONS = "adopt-permissions";
private static final String TAG_USES_GL_TEXTURE = "uses-gl-texture";
private static final String TAG_COMPATIBLE_SCREENS = "compatible-screens";
private static final String TAG_SUPPORTS_INPUT = "supports-input";
private static final String TAG_EAT_COMMENT = "eat-comment";
private static final String TAG_PACKAGE = "package";
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
这其实就对应了AndroidManifest.xml文件下的所有节点,下面列着不全
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /
<uses-feature android:name="android.hardware.camera" /> <!-- 使用照相机权限 -->
<uses-feature android:name="android.hardware.camera.autofocus" />
<application
android:allowBackup="true"
其实每个if 判断都对应着一个具体的方法,比如
parsePermissionTree()和parseUsesPermission()方法就是对应的解析到有对应节点后的方法!
else if (tagName.equals(TAG_PERMISSION_TREE)) {
if (parsePermissionTree(pkg, res, parser, outError) == null) {
return null;
}
} else if (tagName.equals(TAG_USES_PERMISSION)) {
if (!parseUsesPermission(pkg, res, parser)) {
return null;
}
当然,作为我们平常在配置文件中写的最多的四大组件之类的注册,所以我们重点看application该标签下的代码吧!
if (tagName.equals(TAG_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, flags, outError)) {
return null;
}
重点看parseBaseApplication(pkg, res, parser, flags, outError)这个方法,这个就是解析application标签下的具体逻辑!
private boolean parseBaseApplication(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {
final ApplicationInfo ai = owner.applicationInfo;
final String pkgName = owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestApplication);
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {
Activity a = parseActivity(owner, res, parser, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {
Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {
Service s = parseService(owner, res, parser, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {
Provider p = parseProvider(owner, res, parser, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
} else if (tagName.equals("activity-alias")) {
Activity a = parseActivityAlias(owner, res, parser, flags, outError);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (parser.getName().equals("meta-data")) {
// note: application meta-data is stored off to the side, so it can
// remain null in the primary copy (we like to avoid extra copies because
// it can be large)
if ((owner.mAppMetaData = parseMetaData(res, parser, owner.mAppMetaData,
outError)) == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
} else if (tagName.equals("library")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestLibrary_name);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (!ArrayUtils.contains(owner.libraryNames, lname)) {
owner.libraryNames = ArrayUtils.add(owner.libraryNames, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-library")) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestUsesLibrary);
// Note: don't allow this value to be a reference to a resource
// that may change.
String lname = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_name);
boolean req = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestUsesLibrary_required,
true);
sa.recycle();
if (lname != null) {
lname = lname.intern();
if (req) {
owner.usesLibraries = ArrayUtils.add(owner.usesLibraries, lname);
} else {
owner.usesOptionalLibraries = ArrayUtils.add(
owner.usesOptionalLibraries, lname);
}
}
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals("uses-package")) {
// Dependencies for app installers; we don't currently try to
// enforce this.
XmlUtils.skipCurrentTag(parser);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Unknown element under <application>: " + tagName
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
} else {
outError[0] = "Bad element under <application>: " + tagName;
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
}
}
看到这里,应该就明白了吧!里面有着activity,Service,provider,meta-data,library等标签了吧,这个解析过程总算是有了一个里程碑式的突破,从parseBaseApplication中我们看到这个过程就是普通的xml解析,根据不同的标签来调用不同的解析方法,例如,解析Activity则会调用,parseActivity方法,然后返回一个Activity实例,并且将这个实例添加到Package对象的activites列表中。比如parseActivity()
private Activity parseActivity(Package owner, Resources res,
XmlResourceParser parser, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestActivity);
if (mParseActivityArgs == null) {
mParseActivityArgs = new ParseComponentArgs(owner, outError,
R.styleable.AndroidManifestActivity_name,
R.styleable.AndroidManifestActivity_label,
R.styleable.AndroidManifestActivity_icon,
R.styleable.AndroidManifestActivity_roundIcon,
R.styleable.AndroidManifestActivity_logo,
R.styleable.AndroidManifestActivity_banner,
mSeparateProcesses,
R.styleable.AndroidManifestActivity_process,
R.styleable.AndroidManifestActivity_description,
R.styleable.AndroidManifestActivity_enabled);
}
mParseActivityArgs.tag = receiver ? "<receiver>" : "<activity>";
mParseActivityArgs.sa = sa;
mParseActivityArgs.flags = flags;
Activity a = new Activity(mParseActivityArgs, new ActivityInfo());
if (outError[0] != null) {
sa.recycle();
return null;
继续解析activity标签下的标签
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
if (parser.getName().equals("intent-filter")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
a.intents.add(intent);
}
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false, false, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
if (owner.preferredActivityFilters == null) {
owner.preferredActivityFilters = new ArrayList<ActivityIntentInfo>();
}
owner.preferredActivityFilters.add(intent);
}
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
return null;
}
} else if (!receiver && parser.getName().equals("layout")) {
parseLayout(res, parser, a);
} else {
if (!RIGID_PARSER) {
Slog.w(TAG, "Problem in package " + mArchiveSourcePath + ":");
if (receiver) {
Slog.w(TAG, "Unknown element under <receiver>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
} else {
Slog.w(TAG, "Unknown element under <activity>: " + parser.getName()
+ " at " + mArchiveSourcePath + " "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
if (receiver) {
outError[0] = "Bad element under <receiver>: " + parser.getName();
} else {
outError[0] = "Bad element under <activity>: " + parser.getName();
}
return null;
}
}
}
到最后也就加到List< Activity> activities列表中
owner.activities.add(a);
这里只是单独举例,其他的三大组件,也是类似的逻辑,读者有兴趣的话,可以自行观看!
此时,回到之前的scanPackageLI()方法。
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
boolean success = false;
try {
final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
success = true;
return res;
} finally {
if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
// DELETE_DATA_ON_FAILURES is only used by frozen paths
destroyAppDataLIF(pkg, UserHandle.USER_ALL,
StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
}
}
}
其中调用了 final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
currentTime, user);
这个方法,现在看看该方法。
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
final int policyFlags, final int scanFlags, long currentTime, UserHandle user)
throws PackageManagerException {
final File scanFile = new File(pkg.codePath);
if (pkg.applicationInfo.getCodePath() == null ||
pkg.applicationInfo.getResourcePath() == null) {
// Bail out. The resource and code paths haven't been set.
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Code and resource paths haven't been set correctly");
}
// Apply policy
if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
if (pkg.applicationInfo.isDirectBootAware()) {
// we're direct b
由于代码过长,我们只看下面的关键代码
r = null;
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");
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
r.append(a.info.name);
}
}
这里我们看到,将上一步解析到的Activity,Service添加到了mActivitys,mServices中,这些类型定义是PMS中的字段,
public static final class SharedLibraryEntry {
public final String path;
public final String apk;
SharedLibraryEntry(String _path, String _apk) {
path = _path;
apk = _apk;
}
}
// Currently known shared libraries.
final ArrayMap<String, SharedLibraryEntry> mSharedLibraries =
new ArrayMap<String, SharedLibraryEntry>();
// 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();
// All available providers, for your resolving pleasure.
final ProviderIntentResolver mProviders = new ProviderIntentResolver();
// Mapping from provider base names (first directory in content URI codePath)
// to the provider information.
final ArrayMap<String, PackageParser.Provider> mProvidersByAuthority =
new ArrayMap<String, PackageParser.Provider>();
// Mapping from instrumentation class names to info about them.
final ArrayMap<ComponentName, PackageParser.Instrumentation> mInstrumentation =
new ArrayMap<ComponentName, PackageParser.Instrumentation>();
// Mapping from permission names to info about them.
final ArrayMap<String, PackageParser.PermissionGroup> mPermissionGroups =
new ArrayMap<String, PackageParser.PermissionGroup>();
// Packages whose data we have transfered into another package, thus
// should no longer exist.
final ArraySet<String> mTransferedPackages = new ArraySet<String>();
// Broadcast actions that are only available to the system.
final ArraySet<String> mProtectedBroadcasts = new ArraySet<String>();
这个SharedLibraryEntry其实就是PMS中的内部类,专门用来存储一些重要字段!好了到这一步,整个已安装的apk信息树就建立了,每个apk的应用名,包名,图标,Activity,Service等信息都存在系统中,当用户使用intent 跳转到某个Activity或者启动某个Service时,系统则会到这个信息表中进行查找,若找不到,就会报错的,就比如有些时候,没有注册四大组件,或者没有写应有的权限!程序就会崩溃!
最后总结一下:
用张时序图来表现具体流程!